diff --git a/DEPS b/DEPS
index 82da2461..a5309936 100644
--- a/DEPS
+++ b/DEPS
@@ -201,7 +201,7 @@
   # By default, do not check out versions of toolschains and sdks that are
   # specifically only needed by Lacros.
   'checkout_lacros_sdk': False,
-  'lacros_sdk_version': '14523.0.0',
+  'lacros_sdk_version': '14556.0.0',
 
   # Generate location tag metadata to include in tests result data uploaded
   # to ResultDB. This isn't needed on some configs and the tool that generates
@@ -253,7 +253,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c444394841139218d26be2b12e5953246a26fda7',
+  'skia_revision': '9de6144f7d90371537a7abda2062c6c960072fb6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -261,7 +261,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'b465a7366cb82a1be0bca5c4508eef7157363843',
+  'angle_revision': '3a529ce245d0ca45fa8e85491a26ec7825aa7c92',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -269,7 +269,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'c2710889e7eda2f88f6e8b1323bd3b971bbb885a',
+  'pdfium_revision': '9718f72db3f35da3f073111829485236c6ff7267',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -300,7 +300,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '1e2eb65048f75c64b68708efed6ce904c31f3b2f',
+  'freetype_revision': '53dfdcd8198d2b3201a23c4bad9190519ba918db',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '4cc26bd5a059cf275feff2c934f744617dd26e8c',
+  'catapult_revision': '389f33bb40a3345b73a68613178c789476ceaecf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -328,7 +328,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'b2a3923617328a970bf07eca721bb8f6ad9c4574',
+  'devtools_frontend_revision': 'dee4cc38be8b1ab523ac594c7abf0a08016f9f3f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -368,7 +368,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '80b94d13a707ccf941b4e36bb18b120285db33c8',
+  'dawn_revision': 'b657e0df98b215aac28b3457210bdc5149663bba',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -396,7 +396,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': '31b2aae6c5442c55d819e7a966b8dd996a0edcfa',
+  'nearby_revision': '517d77f5aa4fea8f4437125830cfc55f84705e3d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -715,7 +715,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '7d387221a74e94a7f26224c3e52dc5a1e1401e4c',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '16b7d15f81c0a724b2b2054451cbeae93fbccedb',
       'condition': 'checkout_ios',
   },
 
@@ -816,7 +816,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'RLW71Y2eRTjMSjaHAyDffpdD7OwJhJLbjOf0-BLLmwsC',
+          'version': 'l1xDoCBm1rDEFIlePkzB2hTG4r1YvYoxdNBU3SGjTDoC',
         },
       ],
       'dep_type': 'cipd',
@@ -827,7 +827,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'kXqkgNAZI3mjN_7BL0owH1iDd9WbjGNLQQmF5UiaINMC',
+          'version': 'ScMUxoCQFi3vFXDAlBj3VezWCnqk9hxpFW8GznMw454C',
         },
       ],
       'dep_type': 'cipd',
@@ -838,7 +838,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': '2cIzm3j9sDgwkg5HQzYtZ923qplJv2tQ1yQf7-edmG4C',
+          'version': 'iZunll1kgfbUFl7u6t5VnY4-MHcjb72ZS9UVDhTAr8cC',
         },
       ],
       'dep_type': 'cipd',
@@ -932,7 +932,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_build_tools/aapt2',
-              'version': 'yQIf5Ev_-q9u6Pr_a0APd1dHcImJSmotVADw4Pj151QC',
+              'version': 'wicn5Ce1ay6ivbZ1GNFF0gRSS3NYv_7hJTPtVga3O-QC',
           },
       ],
       'condition': 'checkout_android',
@@ -998,7 +998,7 @@
     Var('chromium_git') + '/angle/angle.git' + '@' +  Var('angle_revision'),
 
   'src/third_party/dav1d/libdav1d':
-    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + 'b1a5189c9d37c837099ce50852b6ce9597b89b0c',
+    Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + '56e7ffc0dbe44970a4d55ec2241824c67add9dd5',
 
   'src/third_party/dawn':
     Var('dawn_git') + '/dawn.git' + '@' +  Var('dawn_revision'),
@@ -1107,7 +1107,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '725b443750be9664a1c5a12ce19d5fc42dda4cd2',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3d2387a672b437b2d32d83120080a5e593b0e5af',
       'condition': 'checkout_chromeos',
   },
 
@@ -1127,7 +1127,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '950a6b4225ed3280aa0fbca2de51bfbaecd7695a',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '76979dae56c08d24b092347a1bdd2abe474555a3',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1267,7 +1267,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '41cdffd71c9948f63c7ad36e1fb0ff519aa7a37e',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '8a5b728e4f43b0eabdb9ea450f956d67cfb22719',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'ea8c08d8783fceda86c19618694881149e23f305',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1510,7 +1510,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'a2dabf80a38b16846d9a3209d61f452a1bfc8f15',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9b8eaa21f16d10cffefdf758a8b900e014e4788d',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1588,7 +1588,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/android/aemu/release/linux-amd64',
-              'version': 'jbMpjSzTRngktfwC6FszY0ASXYXkR77i2_dDrvQsa9oC'
+              'version': 'npFnPpRDsfR65dWNWK7Bd-sNnXyASiLIUQB_fWalsiYC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1692,7 +1692,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '62d7d0c928c9a040dce96aa2f16c00e7e67d59cb',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@923b4de70fb5b6e9268b57837b2d517b2ba556c3',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@5fe1b21d6ba47a8dee59068d6927a4c157ec13a3',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1731,7 +1731,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'a630866d89f74aa95cf3aecd78987637ee195b68',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '026cfa3c8bde814ebbe5e94d0803dfd173c9bb02',
+    Var('webrtc_git') + '/src.git' + '@' + '66ddd5ab6d853c0e8ece811bb2d8b642b8602fc7',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1758,7 +1758,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': 'wjKDZ5vJELJ_j3O037nIWhBEMF0cY4Y1g4tLc47hPJoC',
+          'version': 'iqtz2prn9CUv6A8KCcxJzadmPEDLY1FPP-b2YqIFQ1cC',
         },
       ],
       'dep_type': 'cipd',
@@ -1768,7 +1768,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'Vg04A_bOadtB2ljbA9DGKe69_Uc6pmX5mk_ABoO2R3EC',
+          'version': 'EbVQXa1u0hbZ8pxb0Il6Rbc1ErHpIN_-kMVOzBXMQyoC',
         },
       ],
       'dep_type': 'cipd',
@@ -1779,7 +1779,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'jw5QK1qcTGSBr-yjH0d-9F_MNeq6e5_5aWLq_oGWy0QC',
+          'version': 'Y4l14LBqCsT9EhffPIOtso9VSpwqQE9WccjZdDBZmLoC',
         },
       ],
       'dep_type': 'cipd',
@@ -1790,7 +1790,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-arm64',
-          'version': 'o4BSMT1hKtY4T4VBfANeSm-NuhxoxPYUp3lF0EpoUvMC',
+          'version': 'INEqc8JI_mtww_X0ShOlDkF3S8OG4tjF4Nkei0K7ci8C',
         },
       ],
       'dep_type': 'cipd',
@@ -1801,7 +1801,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@84b3be6df03d468145962a5239d27da83ccd3e14',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@72adf65a661b1c04449d6ca665c9ced97267404e',
     'condition': 'checkout_src_internal',
   },
 
@@ -1853,7 +1853,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'cvaInkU-R_Gp-n2T4FBD02VwkF3VjZEuBh5sAFLoCf8C',
+        'version': 'C8gZ9FIW7W4ZX_T-0MFkA9OSY7DcZzSWVk4IS2fppZwC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3880,15 +3880,29 @@
     ],
   },
   {
-    'name': 'clang_format_mac',
+    'name': 'clang_format_mac_x64',
     'pattern': '.',
-    'condition': 'host_os == "mac"',
+    'condition': 'host_os == "mac" and host_cpu == "x64"',
     'action': [ 'python3',
                 'src/third_party/depot_tools/download_from_google_storage.py',
                 '--no_resume',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
-                '-s', 'src/buildtools/mac/clang-format.sha1',
+                '-s', 'src/buildtools/mac/clang-format.x64.sha1',
+                '-o', 'src/buildtools/mac/clang-format',
+    ],
+  },
+  {
+    'name': 'clang_format_mac_arm64',
+    'pattern': '.',
+    'condition': 'host_os == "mac" and host_cpu == "arm64"',
+    'action': [ 'python3',
+                'src/third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-clang-format',
+                '-s', 'src/buildtools/mac/clang-format.arm64.sha1',
+                '-o', 'src/buildtools/mac/clang-format',
     ],
   },
   {
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 99723fa..9140e2b 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1015,19 +1015,6 @@
     ),
 )
 
-# Format: Sequence of tuples containing:
-# * String pattern or, if starting with a slash, a regular expression.
-# * Sequence of strings to show when the pattern matches.
-_DEPRECATED_MOJO_TYPES : Sequence[BanRule] = (
-    BanRule(
-      r'/\bmojo::AssociatedInterfacePtrInfo\b',
-      (
-        'mojo::AssociatedInterfacePtrInfo<Interface> is deprecated.',
-        'Use mojo::PendingAssociatedRemote<Interface> instead.',
-      ),
-    ),
-)
-
 _IPC_ENUM_TRAITS_DEPRECATED = (
     'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
     'See http://www.chromium.org/Home/chromium-security/education/'
@@ -1733,48 +1720,6 @@
     return result
 
 
-def CheckNoDeprecatedMojoTypes(input_api, output_api):
-    """Make sure that old Mojo types are not used."""
-    warnings = []
-    errors = []
-
-    # For any path that is not an "ok" or an "error" path, a warning will be
-    # raised if deprecated mojo types are found.
-    ok_paths = ['components/arc']
-    error_paths = ['third_party/blink', 'content']
-
-    file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
-    for f in input_api.AffectedFiles(file_filter=file_filter):
-        # Don't check //components/arc, not yet migrated (see crrev.com/c/1868870).
-        if any(map(lambda path: f.LocalPath().startswith(path), ok_paths)):
-            continue
-
-        for line_num, line in f.ChangedContents():
-            for ban_rule in _DEPRECATED_MOJO_TYPES:
-                problems = _GetMessageForMatchingType(input_api, f, line_num,
-                                                      line, ban_rule)
-
-                if problems:
-                    # Raise errors inside |error_paths| and warnings everywhere else.
-                    if any(
-                            map(lambda path: f.LocalPath().startswith(path),
-                                error_paths)):
-                        errors.extend(problems)
-                    else:
-                        warnings.extend(problems)
-
-    result = []
-    if (warnings):
-        result.append(
-            output_api.PresubmitPromptWarning(
-                'Banned Mojo types were used.\n' + '\n'.join(warnings)))
-    if (errors):
-        result.append(
-            output_api.PresubmitError('Banned Mojo types were used.\n' +
-                                      '\n'.join(errors)))
-    return result
-
-
 def CheckNoPragmaOnce(input_api, output_api):
     """Make sure that banned functions are not used."""
     files = []
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 76d425a..beb5f0a4 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -2613,74 +2613,6 @@
     self.assertTrue('bad.mojom' in results[0].message)
     self.assertTrue('good.mojom' not in results[0].message)
 
-  def testDeprecatedMojoTypes(self):
-    ok_paths = ['components/arc']
-    warning_paths = ['some/cpp']
-    error_paths = ['third_party/blink', 'content']
-    test_cases = [
-      {
-        'type': 'mojo::AssociatedInterfacePtrInfo<>',
-        'file': 'file4.cc'
-      },
-      {
-        'type': 'mojo::AssociatedInterfaceRequest<>',
-        'file': 'file5.cc'
-      },
-      {
-        'type': 'mojo::InterfacePtr<>',
-        'file': 'file8.cc'
-      },
-      {
-        'type': 'mojo::InterfacePtrInfo<>',
-        'file': 'file9.cc'
-      },
-      {
-        'type': 'mojo::InterfaceRequest<>',
-        'file': 'file10.cc'
-      },
-      {
-        'type': 'mojo::MakeRequest()',
-        'file': 'file11.cc'
-      },
-    ]
-
-    # Build the list of MockFiles considering paths that should trigger warnings
-    # as well as paths that should trigger errors.
-    input_api = MockInputApi()
-    input_api.files = []
-    for test_case in test_cases:
-      for path in ok_paths:
-        input_api.files.append(MockFile(os.path.join(path, test_case['file']),
-                                        [test_case['type']]))
-      for path in warning_paths:
-        input_api.files.append(MockFile(os.path.join(path, test_case['file']),
-                                        [test_case['type']]))
-      for path in error_paths:
-        input_api.files.append(MockFile(os.path.join(path, test_case['file']),
-                                        [test_case['type']]))
-
-    results = PRESUBMIT.CheckNoDeprecatedMojoTypes(input_api, MockOutputApi())
-
-    # warnings are results[0], errors are results[1]
-    self.assertEqual(2, len(results))
-
-    for test_case in test_cases:
-      # Check that no warnings nor errors have been triggered for these paths.
-      for path in ok_paths:
-        self.assertFalse(path in results[0].message)
-        self.assertFalse(path in results[1].message)
-
-      # Check warnings have been triggered for these paths.
-      for path in warning_paths:
-        self.assertTrue(path in results[0].message)
-        self.assertFalse(path in results[1].message)
-
-      # Check errors have been triggered for these paths.
-      for path in error_paths:
-        self.assertFalse(path in results[0].message)
-        self.assertTrue(path in results[1].message)
-
-
 class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
   def testTruePositives(self):
     mock_input_api = MockInputApi()
diff --git a/WATCHLISTS b/WATCHLISTS
index b2b5c0e..55f2499 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1360,6 +1360,7 @@
     },
     'multidevice': {
       'filepath': 'ash/components/proximity_auth/'\
+                  '|ash/components/multidevice/'\
                   '|ash/multi_device_setup/'\
                   '|ash/resources/multidevice_resources.grdp'\
                   '|ash/services/device_sync/'\
@@ -1374,7 +1375,6 @@
                   '|chrome/browser/ui/webui/chromeos/multidevice_setup/'\
                   '|chrome/browser/ui/webui/settings/chromeos/multidevice'\
                   '|chrome/test/data/webui/multidevice_setup/'\
-                  '|chromeos/components/multidevice/'\
                   '|ui/webui/resources/cr_components/chromeos/multidevice_setup/'
     },
     'multipaste': {
@@ -1506,6 +1506,9 @@
                   '|OriginTrial'\
                   '|ConditionalFeature',
     },
+    'os_crypt': {
+      'filepath': 'components/os_crypt/',
+    },
     'ozone': {
       'filepath': 'ui/ozone/|'\
         'ui/events/ozone/|'\
@@ -2689,6 +2692,7 @@
     'omnibox': ['jdonnelly+watch@chromium.org'],
     'origin_trials': ['chasej+watch@chromium.org',
                       'iclelland+watch@chromium.org'],
+    'os_crypt': ['wfh+watch@chromium.org'],
     'ozone': ['ozone-reviews@chromium.org'],
     'ozone_fuchsia': ['dworsham@google.com',
                       'emircan@google.com',
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 4b124688..95f8910 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -251,9 +251,6 @@
                     "Executes tasks with  the kBootstrap task type on the default task queues "
                             + "(based on priority of the task) rather than a dedicated "
                             + "high-priority task queue."),
-            Flag.baseFeature(BlinkFeatures.RTC_DISALLOW_PLAN_B_OUTSIDE_DEPRECATION_TRIAL,
-                    "Makes constructing an RTCPeerConnection with {sdpSemantics:'plan-b'} throw "
-                            + "an exception."),
             Flag.baseFeature(BlinkFeatures.PREFETCH_ANDROID_FONTS,
                     "Enables prefetching Android fonts on renderer startup."),
             Flag.baseFeature(AwFeatures.WEBVIEW_LEGACY_TLS_SUPPORT,
@@ -300,5 +297,8 @@
                     "Free Canvas2D resources when the webview is in the background."),
             Flag.baseFeature(VizFeatures.SURFACE_SYNC_THROTTLING,
                     "Enables throttling of Surface Sync to improve rotations"),
+            Flag.baseFeature(BlinkFeatures.AUTOFILL_SHADOW_DOM,
+                    "Enables Autofill associate form elements with form "
+                            + "control elements across shadow boundaries."),
     };
 }
diff --git a/android_webview/tools/generate_flag_labels.py b/android_webview/tools/generate_flag_labels.py
index 84072d39..b301eea 100755
--- a/android_webview/tools/generate_flag_labels.py
+++ b/android_webview/tools/generate_flag_labels.py
@@ -51,9 +51,9 @@
     'GmsCoreEmoji': 'GMSCoreEmoji',
     'KeyboardAccessoryPaymentVirtualCardFeature': 'IPH_KeyboardAccessoryPaymentVirtualCard',  # pylint: disable=line-too-long
     'CompositeBgColorAnimation': 'CompositeBGColorAnimation',
-    'RtcDisallowPlanBOutsideDeprecationTrial': 'RTCDisallowPlanBOutsideDeprecationTrial',  # pylint: disable=line-too-long
     'enable-http2-grease-settings': 'http2-grease-settings',
-    'AvoidUnnecessaryBeforeUnloadCheckPostTask': 'AvoidUnnecessaryBeforeUnloadCheck',  # pylint: disable=line-too-long
+    'AvoidUnnecessaryBeforeUnloadCheckPostTask': 'AvoidUnnecessaryBeforeUnloadCheck',  # pylint: disable=line-too-long,
+    'AutofillShadowDom': 'AutofillShadowDOM'
 }
 
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 0cbf25f4..76c2f85 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1388,6 +1388,8 @@
     "system/phonehub/tether_connection_pending_view.cc",
     "system/phonehub/tether_connection_pending_view.h",
     "system/phonehub/ui_constants.h",
+    "system/power/adaptive_charging_controller.cc",
+    "system/power/adaptive_charging_controller.h",
     "system/power/backlights_forced_off_setter.cc",
     "system/power/backlights_forced_off_setter.h",
     "system/power/battery_image_source.cc",
@@ -1769,6 +1771,8 @@
     "wm/desks/templates/desks_templates_presenter.h",
     "wm/desks/templates/desks_templates_util.cc",
     "wm/desks/templates/desks_templates_util.h",
+    "wm/desks/templates/restore_data_collector.cc",
+    "wm/desks/templates/restore_data_collector.h",
     "wm/desks/templates/save_desk_template_button.cc",
     "wm/desks/templates/save_desk_template_button.h",
     "wm/desks/zero_state_button.cc",
@@ -2070,6 +2074,7 @@
     "//ash/components/audio",
     "//ash/components/fwupd",
     "//ash/components/geolocation",
+    "//ash/components/multidevice/logging",
     "//ash/components/peripheral_notification",
     "//ash/components/phonehub",
     "//ash/components/settings",
@@ -2104,7 +2109,6 @@
     "//cc/paint:paint",
     "//chromeos/assistant:buildflags",
     "//chromeos/components/feature_usage",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/components/quick_answers/public/cpp:prefs",
     "//chromeos/components/sensors:buildflags",
     "//chromeos/components/sensors:sensors",
@@ -3094,6 +3098,8 @@
     "projector/test/mock_projector_metadata_controller.h",
     "projector/test/mock_projector_ui_controller.cc",
     "projector/test/mock_projector_ui_controller.h",
+    "system/geolocation/test_geolocation_url_loader_factory.cc",
+    "system/geolocation/test_geolocation_url_loader_factory.h",
     "test/layer_animation_stopped_waiter.cc",
     "test/layer_animation_stopped_waiter.h",
 
@@ -3225,6 +3231,7 @@
     "//ash/components/attestation:test_support",
     "//ash/components/audio",
     "//ash/components/disks:test_support",
+    "//ash/components/geolocation",
     "//ash/constants",
     "//ash/keyboard/ui",
     "//ash/keyboard/ui:test_support",
diff --git a/ash/DEPS b/ash/DEPS
index 88f5332c..66f42b1 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -58,7 +58,6 @@
   "-chromeos",
   # //ash can use chromeos components that sit below it in the dependency tree.
   "+chromeos/components/feature_usage",
-  "+chromeos/components/multidevice",
   "+chromeos/components/quick_answers",
   "+chromeos/components/sensors",
   "+chromeos/constants",
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc
index c4fdff020..5fbeaa39 100644
--- a/ash/app_list/views/search_result_tile_item_view.cc
+++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -29,12 +29,12 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/views/accessibility/accessibility_paint_checks.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
@@ -42,7 +42,6 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/focus/focus_manager.h"
-#include "ui/views/image_model_utils.h"
 
 namespace ash {
 
@@ -395,8 +394,7 @@
     return;
   }
 
-  gfx::ImageSkia badge_icon_skia =
-      views::GetImageSkiaFromImageModel(badge_icon, GetColorProvider());
+  gfx::ImageSkia badge_icon_skia = badge_icon.Rasterize(GetColorProvider());
 
   if (use_badge_icon_background) {
     badge_icon_skia =
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index 6fb4c4e..d508bfb 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -46,7 +46,6 @@
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/style/typography.h"
@@ -446,8 +445,8 @@
     return;
   }
 
-  gfx::ImageSkia badge_icon_skia = views::GetImageSkiaFromImageModel(
-      result()->badge_icon(), GetColorProvider());
+  gfx::ImageSkia badge_icon_skia =
+      result()->badge_icon().Rasterize(GetColorProvider());
 
   if (result()->use_badge_icon_background()) {
     badge_icon_skia =
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 9ed542c..df8184d6 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1871,6 +1871,9 @@
       <message name="IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_TITLE" desc="The text of the title of the desks templates delete dialog, which shows up when trying to delete a template.">
         Delete template?
       </message>
+      <message name="IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION" desc="The text of the description of the desks templates unsupported apps dialog, which shows up when trying to save a desk that contains an unsupported app (i.e. Crostini app).">
+        Lacros windows aren't currently supported. Other apps will be saved.
+      </message>
       <message name="IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LINUX_APPS_DIALOG_DESCRIPTION" desc="The text of the description of the desks templates unsupported apps dialog, which shows up when trying to save a desk that contains an unsupported app (i.e. Crostini app).">
         Linux apps aren’t currently supported. Other apps will be saved.
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..0bf8860
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+ac5ac60f1df14eab58e8a94244722f33be076cb9
\ No newline at end of file
diff --git a/ash/components/BUILD.gn b/ash/components/BUILD.gn
index 248bb6e..f008060 100644
--- a/ash/components/BUILD.gn
+++ b/ash/components/BUILD.gn
@@ -33,6 +33,7 @@
     "//ash/components/geolocation:unit_tests",
     "//ash/components/login/auth:unit_tests",
     "//ash/components/login/session:unit_tests",
+    "//ash/components/multidevice:unit_tests",
     "//ash/components/peripheral_notification:unit_tests",
     "//ash/components/phonehub:unit_tests",
     "//ash/components/policy:unit_tests",
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc
index a17e6b9..4f9f766d 100644
--- a/ash/components/arc/arc_features.cc
+++ b/ash/components/arc/arc_features.cc
@@ -111,7 +111,7 @@
 // When enabled, utility processes are spawned to perform hardware decode
 // acceleration on behalf of ARC++/ARCVM instead of using the GPU process.
 const base::Feature kOutOfProcessVideoDecoding{
-    "OutOfProcessVideoDecoding", base::FEATURE_ENABLED_BY_DEFAULT};
+    "OutOfProcessVideoDecoding", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Controls ARC picture-in-picture feature. If this is enabled, then Android
 // will control which apps can enter PIP. If this is disabled, then ARC PIP
diff --git a/ash/components/device_activity/device_activity_controller.cc b/ash/components/device_activity/device_activity_controller.cc
index bae2ca31..5badb91b 100644
--- a/ash/components/device_activity/device_activity_controller.cc
+++ b/ash/components/device_activity/device_activity_controller.cc
@@ -11,6 +11,7 @@
 #include "ash/components/device_activity/monthly_use_case_impl.h"
 #include "base/check_op.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/rand_util.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
@@ -76,11 +77,37 @@
                              unix_epoch);
 }
 
+// static
+base::TimeDelta DeviceActivityController::DetermineStartUpDelay(
+    base::Time chrome_first_run_ts) {
+  // |random_delay| picks a random minute between [0, 29] inclusive (30 buckets)
+  // to delay start. This will distribute the high qps during certain times,
+  // across 30 equally probable buckets.
+  base::TimeDelta random_delay = base::Minutes(base::RandInt(0, 29));
+
+  // Wait at least 10 minutes from the first chrome run sentinel file creation
+  // time. This creation time is used as an indicator of when the device last
+  // reset (powerwashed/recovery/RMA). PSM servers take 10 minutes from CheckIn
+  // to return the correct response for CheckMembership requests, since the PSM
+  // servers need to update their cache.
+  //
+  // This delay avoids the scenario where a device checks in, powerwashes, and
+  // on device start up, gets the wrong check membership response.
+  base::TimeDelta delay_on_first_chrome_run;
+  base::Time current_ts = base::Time::Now();
+  if (current_ts < (chrome_first_run_ts + base::Minutes(10))) {
+    delay_on_first_chrome_run =
+        chrome_first_run_ts + base::Minutes(10) - current_ts;
+  }
+
+  return delay_on_first_chrome_run + random_delay;
+}
+
 DeviceActivityController::DeviceActivityController(
     version_info::Channel chromeos_channel,
     PrefService* local_state,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    base::TimeDelta start_delay)
+    base::TimeDelta start_up_delay)
     : statistics_provider_(
           chromeos::system::StatisticsProvider::GetInstance()) {
   DeviceActivityClient::RecordDeviceActivityMethodCalled(
@@ -90,15 +117,12 @@
   DCHECK(!g_ash_device_activity_controller);
   g_ash_device_activity_controller = this;
 
-  // Uses a random minute between [0, 29] inclusive (30 buckets) to delay start.
-  // This will distribute the high qps during certain times, across 30 equally
-  // probable buckets.
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&device_activity::DeviceActivityController::Start,
                      weak_factory_.GetWeakPtr(), chromeos_channel, local_state,
                      url_loader_factory),
-      start_delay);
+      start_up_delay);
 }
 
 DeviceActivityController::~DeviceActivityController() {
diff --git a/ash/components/device_activity/device_activity_controller.h b/ash/components/device_activity/device_activity_controller.h
index 097ef300..08f5c54f 100644
--- a/ash/components/device_activity/device_activity_controller.h
+++ b/ash/components/device_activity/device_activity_controller.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/component_export.h"
+#include "base/time/time.h"
 #include "chromeos/system/statistics_provider.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -32,11 +33,15 @@
   // Registers local state preferences.
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
+  // Determines the total start up delay before starting device activity
+  // reporting.
+  static base::TimeDelta DetermineStartUpDelay(base::Time chrome_first_run_ts);
+
   DeviceActivityController(
       version_info::Channel chromeos_channel,
       PrefService* local_state,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      base::TimeDelta start_delay);
+      base::TimeDelta start_up_delay);
   DeviceActivityController(const DeviceActivityController&) = delete;
   DeviceActivityController& operator=(const DeviceActivityController&) = delete;
   ~DeviceActivityController();
diff --git a/ash/components/device_activity/device_activity_controller_unittest.cc b/ash/components/device_activity/device_activity_controller_unittest.cc
index 0c2fe703..6cfcc5f1 100644
--- a/ash/components/device_activity/device_activity_controller_unittest.cc
+++ b/ash/components/device_activity/device_activity_controller_unittest.cc
@@ -53,7 +53,7 @@
 
     device_activity_controller_ = std::make_unique<DeviceActivityController>(
         kFakeChromeOSChannel, local_state(), test_shared_loader_factory_,
-        /* start_delay */ base::Minutes(0));
+        /* start_up_delay */ base::Minutes(0));
   }
 
   void TearDown() override { device_activity_controller_.reset(); }
diff --git a/chromeos/components/multidevice/BUILD.gn b/ash/components/multidevice/BUILD.gn
similarity index 89%
rename from chromeos/components/multidevice/BUILD.gn
rename to ash/components/multidevice/BUILD.gn
index 98781b8..7cee0074 100644
--- a/chromeos/components/multidevice/BUILD.gn
+++ b/ash/components/multidevice/BUILD.gn
@@ -2,7 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-assert(is_chromeos, "MultiDevice is Chrome OS only")
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash)
 
 static_library("multidevice") {
   sources = [
@@ -27,10 +29,10 @@
   ]
 
   deps = [
+    "//ash/components/multidevice/logging",
     "//ash/services/device_sync/proto:util",
     "//base",
     "//base:i18n",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/dbus",
     "//chromeos/dbus/easy_unlock",
     "//components/prefs",
@@ -88,10 +90,10 @@
   deps = [
     ":multidevice",
     ":test_support",
+    "//ash/components/multidevice/logging:unit_tests",
+    "//ash/components/multidevice/mojom:unit_tests",
     "//ash/services/device_sync/proto",
     "//base/test:test_support",
-    "//chromeos/components/multidevice/logging:unit_tests",
-    "//chromeos/components/multidevice/mojom:unit_tests",
     "//testing/gtest",
   ]
 }
diff --git a/ash/components/multidevice/DEPS b/ash/components/multidevice/DEPS
new file mode 100644
index 0000000..c69a828b
--- /dev/null
+++ b/ash/components/multidevice/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/securemessage/proto",
+]
diff --git a/chromeos/components/multidevice/DIR_METADATA b/ash/components/multidevice/DIR_METADATA
similarity index 100%
rename from chromeos/components/multidevice/DIR_METADATA
rename to ash/components/multidevice/DIR_METADATA
diff --git a/chromeos/components/multidevice/OWNERS b/ash/components/multidevice/OWNERS
similarity index 100%
rename from chromeos/components/multidevice/OWNERS
rename to ash/components/multidevice/OWNERS
diff --git a/chromeos/components/multidevice/beacon_seed.cc b/ash/components/multidevice/beacon_seed.cc
similarity index 98%
rename from chromeos/components/multidevice/beacon_seed.cc
rename to ash/components/multidevice/beacon_seed.cc
index f91fb05..587f511 100644
--- a/chromeos/components/multidevice/beacon_seed.cc
+++ b/ash/components/multidevice/beacon_seed.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/beacon_seed.h"
 
 #include <algorithm>
 
diff --git a/chromeos/components/multidevice/beacon_seed.h b/ash/components/multidevice/beacon_seed.h
similarity index 93%
rename from chromeos/components/multidevice/beacon_seed.h
rename to ash/components/multidevice/beacon_seed.h
index 8cd26a49..46ae45f 100644
--- a/chromeos/components/multidevice/beacon_seed.h
+++ b/ash/components/multidevice/beacon_seed.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_
+#define ASH_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_
 
 #include <google/protobuf/repeated_field.h>
 #include <ostream>
@@ -76,4 +76,4 @@
 }  // namespace multidevice
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_BEACON_SEED_H_
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache.cc b/ash/components/multidevice/expiring_remote_device_cache.cc
similarity index 93%
rename from chromeos/components/multidevice/expiring_remote_device_cache.cc
rename to ash/components/multidevice/expiring_remote_device_cache.cc
index d8fe0fb..30ccc26 100644
--- a/chromeos/components/multidevice/expiring_remote_device_cache.cc
+++ b/ash/components/multidevice/expiring_remote_device_cache.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/expiring_remote_device_cache.h"
+#include "ash/components/multidevice/expiring_remote_device_cache.h"
 
+#include "ash/components/multidevice/remote_device_cache.h"
 #include "base/containers/contains.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
 
 namespace chromeos {
 
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache.h b/ash/components/multidevice/expiring_remote_device_cache.h
similarity index 86%
rename from chromeos/components/multidevice/expiring_remote_device_cache.h
rename to ash/components/multidevice/expiring_remote_device_cache.h
index 5fc71aa..0b4d1a38 100644
--- a/chromeos/components/multidevice/expiring_remote_device_cache.h
+++ b/ash/components/multidevice/expiring_remote_device_cache.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
+#define ASH_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
 
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/containers/flat_set.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
@@ -74,4 +74,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_EXPIRING_REMOTE_DEVICE_CACHE_H_
diff --git a/chromeos/components/multidevice/expiring_remote_device_cache_unittest.cc b/ash/components/multidevice/expiring_remote_device_cache_unittest.cc
similarity index 95%
rename from chromeos/components/multidevice/expiring_remote_device_cache_unittest.cc
rename to ash/components/multidevice/expiring_remote_device_cache_unittest.cc
index 175e0157..4f047c12 100644
--- a/chromeos/components/multidevice/expiring_remote_device_cache_unittest.cc
+++ b/ash/components/multidevice/expiring_remote_device_cache_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/expiring_remote_device_cache.h"
+#include "ash/components/multidevice/expiring_remote_device_cache.h"
 
 #include <algorithm>
 
-#include "chromeos/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chromeos/components/multidevice/fake_secure_message_delegate.cc b/ash/components/multidevice/fake_secure_message_delegate.cc
similarity index 98%
rename from chromeos/components/multidevice/fake_secure_message_delegate.cc
rename to ash/components/multidevice/fake_secure_message_delegate.cc
index d8fabc5b..8ed3197 100644
--- a/chromeos/components/multidevice/fake_secure_message_delegate.cc
+++ b/ash/components/multidevice/fake_secure_message_delegate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
 
 #include <stddef.h>
 
diff --git a/chromeos/components/multidevice/fake_secure_message_delegate.h b/ash/components/multidevice/fake_secure_message_delegate.h
similarity index 88%
rename from chromeos/components/multidevice/fake_secure_message_delegate.h
rename to ash/components/multidevice/fake_secure_message_delegate.h
index c8ec59b..8e63fddb 100644
--- a/chromeos/components/multidevice/fake_secure_message_delegate.h
+++ b/ash/components/multidevice/fake_secure_message_delegate.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_
+#define ASH_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_
 
 #include <memory>
 
-#include "chromeos/components/multidevice/secure_message_delegate.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 
 namespace chromeos {
 
@@ -85,4 +85,4 @@
 using ::chromeos::multidevice::FakeSecureMessageDelegateFactory;
 }  // namespace ash::multidevice
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_FAKE_SECURE_MESSAGE_DELEGATE_H_
diff --git a/chromeos/components/multidevice/fake_secure_message_delegate_unittest.cc b/ash/components/multidevice/fake_secure_message_delegate_unittest.cc
similarity index 98%
rename from chromeos/components/multidevice/fake_secure_message_delegate_unittest.cc
rename to ash/components/multidevice/fake_secure_message_delegate_unittest.cc
index 1d0ab90b..7ead603 100644
--- a/chromeos/components/multidevice/fake_secure_message_delegate_unittest.cc
+++ b/ash/components/multidevice/fake_secure_message_delegate_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
 
 #include "base/bind.h"
 #include "base/callback.h"
diff --git a/chromeos/components/multidevice/logging/BUILD.gn b/ash/components/multidevice/logging/BUILD.gn
similarity index 86%
rename from chromeos/components/multidevice/logging/BUILD.gn
rename to ash/components/multidevice/logging/BUILD.gn
index 2db4ca0..218e7572 100644
--- a/chromeos/components/multidevice/logging/BUILD.gn
+++ b/ash/components/multidevice/logging/BUILD.gn
@@ -2,6 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash)
+
 static_library("logging") {
   sources = [
     "log_buffer.cc",
diff --git a/chromeos/components/multidevice/logging/log_buffer.cc b/ash/components/multidevice/logging/log_buffer.cc
similarity index 96%
rename from chromeos/components/multidevice/logging/log_buffer.cc
rename to ash/components/multidevice/logging/log_buffer.cc
index 5c6709e9..fb3e023e 100644
--- a/chromeos/components/multidevice/logging/log_buffer.cc
+++ b/ash/components/multidevice/logging/log_buffer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/logging/log_buffer.h"
+#include "ash/components/multidevice/logging/log_buffer.h"
 
 #include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
diff --git a/chromeos/components/multidevice/logging/log_buffer.h b/ash/components/multidevice/logging/log_buffer.h
similarity index 92%
rename from chromeos/components/multidevice/logging/log_buffer.h
rename to ash/components/multidevice/logging/log_buffer.h
index b3b366df..4217ce2 100644
--- a/chromeos/components/multidevice/logging/log_buffer.h
+++ b/ash/components/multidevice/logging/log_buffer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_
+#define ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_
 
 #include <stddef.h>
 
@@ -92,4 +92,4 @@
 }  // namespace multidevice
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOG_BUFFER_H_
diff --git a/chromeos/components/multidevice/logging/logging.cc b/ash/components/multidevice/logging/logging.cc
similarity index 92%
rename from chromeos/components/multidevice/logging/logging.cc
rename to ash/components/multidevice/logging/logging.cc
index 4f3fa36..bd4c9e5 100644
--- a/chromeos/components/multidevice/logging/logging.cc
+++ b/ash/components/multidevice/logging/logging.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
-#include "chromeos/components/multidevice/logging/log_buffer.h"
+#include "ash/components/multidevice/logging/log_buffer.h"
 
 namespace chromeos {
 
diff --git a/chromeos/components/multidevice/logging/logging.h b/ash/components/multidevice/logging/logging.h
similarity index 92%
rename from chromeos/components/multidevice/logging/logging.h
rename to ash/components/multidevice/logging/logging.h
index 9b504b593..112b519a 100644
--- a/chromeos/components/multidevice/logging/logging.h
+++ b/ash/components/multidevice/logging/logging.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_
+#define ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_
 
 #include <sstream>
 
@@ -70,4 +70,4 @@
 }  // namespace multidevice
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_LOGGING_LOGGING_H_
diff --git a/chromeos/components/multidevice/logging/logging_unittest.cc b/ash/components/multidevice/logging/logging_unittest.cc
similarity index 96%
rename from chromeos/components/multidevice/logging/logging_unittest.cc
rename to ash/components/multidevice/logging/logging_unittest.cc
index 8f96b35e..1e4f4289 100644
--- a/chromeos/components/multidevice/logging/logging_unittest.cc
+++ b/ash/components/multidevice/logging/logging_unittest.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 #include <stddef.h>
 
+#include "ash/components/multidevice/logging/log_buffer.h"
 #include "base/lazy_instance.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
-#include "chromeos/components/multidevice/logging/log_buffer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chromeos/components/multidevice/mojom/BUILD.gn b/ash/components/multidevice/mojom/BUILD.gn
similarity index 87%
rename from chromeos/components/multidevice/mojom/BUILD.gn
rename to ash/components/multidevice/mojom/BUILD.gn
index 02ace131..c0399992 100644
--- a/chromeos/components/multidevice/mojom/BUILD.gn
+++ b/ash/components/multidevice/mojom/BUILD.gn
@@ -2,8 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
+assert(is_chromeos_ash)
+
 mojom("mojom") {
   sources = [ "multidevice_types.mojom" ]
   public_deps = [ "//mojo/public/mojom/base" ]
@@ -26,12 +29,12 @@
       traits_headers = [ "multidevice_mojom_traits.h" ]
       traits_sources = [ "multidevice_mojom_traits.cc" ]
       traits_public_deps = [
+        "//ash/components/multidevice",
         "//base",
-        "//chromeos/components/multidevice",
       ]
       traits_deps = [
+        "//ash/components/multidevice/logging",
         "//ash/services/device_sync/proto",
-        "//chromeos/components/multidevice/logging",
         "//device/bluetooth/public/cpp",
       ]
     },
@@ -45,9 +48,9 @@
 
   deps = [
     ":mojom",
+    "//ash/components/multidevice",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
     "//mojo/public/cpp/test_support:test_utils",
     "//testing/gtest",
   ]
diff --git a/chromeos/components/multidevice/mojom/OWNERS b/ash/components/multidevice/mojom/OWNERS
similarity index 100%
rename from chromeos/components/multidevice/mojom/OWNERS
rename to ash/components/multidevice/mojom/OWNERS
diff --git a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.cc b/ash/components/multidevice/mojom/multidevice_mojom_traits.cc
similarity index 98%
rename from chromeos/components/multidevice/mojom/multidevice_mojom_traits.cc
rename to ash/components/multidevice/mojom/multidevice_mojom_traits.cc
index e4be468..825595a 100644
--- a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.cc
+++ b/ash/components/multidevice/mojom/multidevice_mojom_traits.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/mojom/multidevice_mojom_traits.h"
+#include "ash/components/multidevice/mojom/multidevice_mojom_traits.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/notreached.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "device/bluetooth/public/cpp/bluetooth_address.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 
diff --git a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.h b/ash/components/multidevice/mojom/multidevice_mojom_traits.h
similarity index 85%
rename from chromeos/components/multidevice/mojom/multidevice_mojom_traits.h
rename to ash/components/multidevice/mojom/multidevice_mojom_traits.h
index 31e5a1d..fad5ef0 100644
--- a/chromeos/components/multidevice/mojom/multidevice_mojom_traits.h
+++ b/ash/components/multidevice/mojom/multidevice_mojom_traits.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_
+#define ASH_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_
 
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/mojom/multidevice_types.mojom-shared.h"
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/mojom/multidevice_types.mojom-shared.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 
@@ -87,4 +87,4 @@
 
 }  // namespace mojo
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_MOJOM_MULTIDEVICE_MOJOM_TRAITS_H_
diff --git a/chromeos/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc b/ash/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc
similarity index 94%
rename from chromeos/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc
rename to ash/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc
index b835c1d0..2a516e0 100644
--- a/chromeos/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc
+++ b/ash/components/multidevice/mojom/multidevice_mojom_traits_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/mojom/multidevice_mojom_traits.h"
+#include "ash/components/multidevice/mojom/multidevice_mojom_traits.h"
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/mojom/multidevice_types.mojom.h"
+#include "ash/components/multidevice/remote_device.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/mojom/multidevice_types.mojom.h"
-#include "chromeos/components/multidevice/remote_device.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromeos/components/multidevice/mojom/multidevice_types.mojom b/ash/components/multidevice/mojom/multidevice_types.mojom
similarity index 100%
rename from chromeos/components/multidevice/mojom/multidevice_types.mojom
rename to ash/components/multidevice/mojom/multidevice_types.mojom
diff --git a/chromeos/components/multidevice/remote_device.cc b/ash/components/multidevice/remote_device.cc
similarity index 97%
rename from chromeos/components/multidevice/remote_device.cc
rename to ash/components/multidevice/remote_device.cc
index 51ef814..4a9ee52 100644
--- a/chromeos/components/multidevice/remote_device.cc
+++ b/ash/components/multidevice/remote_device.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device.h"
 
 #include "base/base64.h"
 
diff --git a/chromeos/components/multidevice/remote_device.h b/ash/components/multidevice/remote_device.h
similarity index 89%
rename from chromeos/components/multidevice/remote_device.h
rename to ash/components/multidevice/remote_device.h
index 90f5617..b1d6a88d 100644
--- a/chromeos/components/multidevice/remote_device.h
+++ b/ash/components/multidevice/remote_device.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_
+#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_
 
 #include <map>
 #include <string>
 #include <vector>
 
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 
 namespace chromeos {
 
@@ -94,4 +94,4 @@
 }  // namespace multidevice
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_H_
diff --git a/chromeos/components/multidevice/remote_device_cache.cc b/ash/components/multidevice/remote_device_cache.cc
similarity index 96%
rename from chromeos/components/multidevice/remote_device_cache.cc
rename to ash/components/multidevice/remote_device_cache.cc
index 43f068b..f5b3e8c6 100644
--- a/chromeos/components/multidevice/remote_device_cache.cc
+++ b/ash/components/multidevice/remote_device_cache.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_cache.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/chromeos/components/multidevice/remote_device_cache.h b/ash/components/multidevice/remote_device_cache.h
similarity index 89%
rename from chromeos/components/multidevice/remote_device_cache.h
rename to ash/components/multidevice/remote_device_cache.h
index e0ff593..95d5cad 100644
--- a/chromeos/components/multidevice/remote_device_cache.h
+++ b/ash/components/multidevice/remote_device_cache.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
+#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
 
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
@@ -87,4 +87,4 @@
 }
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_CACHE_H_
diff --git a/chromeos/components/multidevice/remote_device_cache_unittest.cc b/ash/components/multidevice/remote_device_cache_unittest.cc
similarity index 97%
rename from chromeos/components/multidevice/remote_device_cache_unittest.cc
rename to ash/components/multidevice/remote_device_cache_unittest.cc
index 7638b9d2..389beb2 100644
--- a/chromeos/components/multidevice/remote_device_cache_unittest.cc
+++ b/ash/components/multidevice/remote_device_cache_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_cache.h"
 
 #include <algorithm>
 
-#include "chromeos/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chromeos/components/multidevice/remote_device_ref.cc b/ash/components/multidevice/remote_device_ref.cc
similarity index 96%
rename from chromeos/components/multidevice/remote_device_ref.cc
rename to ash/components/multidevice/remote_device_ref.cc
index 82598714..67d32e2 100644
--- a/chromeos/components/multidevice/remote_device_ref.cc
+++ b/ash/components/multidevice/remote_device_ref.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 #include <sstream>
 
diff --git a/chromeos/components/multidevice/remote_device_ref.h b/ash/components/multidevice/remote_device_ref.h
similarity index 93%
rename from chromeos/components/multidevice/remote_device_ref.h
rename to ash/components/multidevice/remote_device_ref.h
index 63e441cd..f26d8e0 100644
--- a/chromeos/components/multidevice/remote_device_ref.h
+++ b/ash/components/multidevice/remote_device_ref.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_
+#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_
 
 #include <memory>
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "base/gtest_prod_util.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace ash {
 class EasyUnlockServiceRegular;
@@ -134,4 +134,4 @@
 }  // namespace multidevice
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_REF_H_
diff --git a/chromeos/components/multidevice/remote_device_ref_unittest.cc b/ash/components/multidevice/remote_device_ref_unittest.cc
similarity index 96%
rename from chromeos/components/multidevice/remote_device_ref_unittest.cc
rename to ash/components/multidevice/remote_device_ref_unittest.cc
index 457acac..b22dfa07 100644
--- a/chromeos/components/multidevice/remote_device_ref_unittest.cc
+++ b/ash/components/multidevice/remote_device_ref_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 #include <memory>
 
-#include "chromeos/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chromeos/components/multidevice/remote_device_test_util.cc b/ash/components/multidevice/remote_device_test_util.cc
similarity index 98%
rename from chromeos/components/multidevice/remote_device_test_util.cc
rename to ash/components/multidevice/remote_device_test_util.cc
index 2337992..cc05136 100644
--- a/chromeos/components/multidevice/remote_device_test_util.cc
+++ b/ash/components/multidevice/remote_device_test_util.cc
@@ -5,7 +5,7 @@
 #include <map>
 #include <string>
 
-#include "chromeos/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 
 #include "base/base64.h"
 #include "base/base64url.h"
diff --git a/chromeos/components/multidevice/remote_device_test_util.h b/ash/components/multidevice/remote_device_test_util.h
similarity index 90%
rename from chromeos/components/multidevice/remote_device_test_util.h
rename to ash/components/multidevice/remote_device_test_util.h
index a0065d93..20547e0 100644
--- a/chromeos/components/multidevice/remote_device_test_util.h
+++ b/ash/components/multidevice/remote_device_test_util.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_
+#define ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_
 
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace chromeos {
 
@@ -78,4 +78,4 @@
 }  // namespace multidevice
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_REMOTE_DEVICE_TEST_UTIL_H_
diff --git a/chromeos/components/multidevice/secure_message_delegate.cc b/ash/components/multidevice/secure_message_delegate.cc
similarity index 91%
rename from chromeos/components/multidevice/secure_message_delegate.cc
rename to ash/components/multidevice/secure_message_delegate.cc
index 8e02f29..5a2ea43 100644
--- a/chromeos/components/multidevice/secure_message_delegate.cc
+++ b/ash/components/multidevice/secure_message_delegate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/secure_message_delegate.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 
 namespace chromeos {
 
diff --git a/chromeos/components/multidevice/secure_message_delegate.h b/ash/components/multidevice/secure_message_delegate.h
similarity index 95%
rename from chromeos/components/multidevice/secure_message_delegate.h
rename to ash/components/multidevice/secure_message_delegate.h
index a354419..79b7f84 100644
--- a/chromeos/components/multidevice/secure_message_delegate.h
+++ b/ash/components/multidevice/secure_message_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_
+#define ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_
 
 #include <memory>
 #include <string>
@@ -107,4 +107,4 @@
 using ::chromeos::multidevice::SecureMessageDelegate;
 }
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_H_
diff --git a/chromeos/components/multidevice/secure_message_delegate_impl.cc b/ash/components/multidevice/secure_message_delegate_impl.cc
similarity index 97%
rename from chromeos/components/multidevice/secure_message_delegate_impl.cc
rename to ash/components/multidevice/secure_message_delegate_impl.cc
index dcabd44..1f9b24a 100644
--- a/chromeos/components/multidevice/secure_message_delegate_impl.cc
+++ b/ash/components/multidevice/secure_message_delegate_impl.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/easy_unlock/easy_unlock_client.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
diff --git a/chromeos/components/multidevice/secure_message_delegate_impl.h b/ash/components/multidevice/secure_message_delegate_impl.h
similarity index 91%
rename from chromeos/components/multidevice/secure_message_delegate_impl.h
rename to ash/components/multidevice/secure_message_delegate_impl.h
index 08baa08b..fa336b77 100644
--- a/chromeos/components/multidevice/secure_message_delegate_impl.h
+++ b/ash/components/multidevice/secure_message_delegate_impl.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_
+#define ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_
 
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 
 namespace chromeos {
 
@@ -85,4 +85,4 @@
 using ::chromeos::multidevice::SecureMessageDelegateImpl;
 }
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_SECURE_MESSAGE_DELEGATE_IMPL_H_
diff --git a/chromeos/components/multidevice/software_feature.cc b/ash/components/multidevice/software_feature.cc
similarity index 98%
rename from chromeos/components/multidevice/software_feature.cc
rename to ash/components/multidevice/software_feature.cc
index 6c76e81..4c87b967 100644
--- a/chromeos/components/multidevice/software_feature.cc
+++ b/ash/components/multidevice/software_feature.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature.h"
 
 #include "base/notreached.h"
 
diff --git a/chromeos/components/multidevice/software_feature.h b/ash/components/multidevice/software_feature.h
similarity index 93%
rename from chromeos/components/multidevice/software_feature.h
rename to ash/components/multidevice/software_feature.h
index 5c33e1a..458ca62 100644
--- a/chromeos/components/multidevice/software_feature.h
+++ b/ash/components/multidevice/software_feature.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_
+#define ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_
 
 #include <ostream>
 
@@ -83,4 +83,4 @@
 }
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_H_
diff --git a/chromeos/components/multidevice/software_feature_state.cc b/ash/components/multidevice/software_feature_state.cc
similarity index 90%
rename from chromeos/components/multidevice/software_feature_state.cc
rename to ash/components/multidevice/software_feature_state.cc
index 25d0f95c..b1f692e 100644
--- a/chromeos/components/multidevice/software_feature_state.cc
+++ b/ash/components/multidevice/software_feature_state.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/software_feature_state.h"
+#include "ash/components/multidevice/software_feature_state.h"
 
 namespace chromeos {
 
diff --git a/chromeos/components/multidevice/software_feature_state.h b/ash/components/multidevice/software_feature_state.h
similarity index 84%
rename from chromeos/components/multidevice/software_feature_state.h
rename to ash/components/multidevice/software_feature_state.h
index b9fa4d0..edaa72fa 100644
--- a/chromeos/components/multidevice/software_feature_state.h
+++ b/ash/components/multidevice/software_feature_state.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_
+#define ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_
 
 #include <ostream>
 
@@ -41,4 +41,4 @@
 }
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_SOFTWARE_FEATURE_STATE_H_
diff --git a/chromeos/components/multidevice/stub_multidevice_util.cc b/ash/components/multidevice/stub_multidevice_util.cc
similarity index 97%
rename from chromeos/components/multidevice/stub_multidevice_util.cc
rename to ash/components/multidevice/stub_multidevice_util.cc
index 0f876fd0..c8b2b20 100644
--- a/chromeos/components/multidevice/stub_multidevice_util.cc
+++ b/ash/components/multidevice/stub_multidevice_util.cc
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/multidevice/stub_multidevice_util.h"
+#include "ash/components/multidevice/stub_multidevice_util.h"
 
 #include <map>
 #include <vector>
 
+#include "ash/components/multidevice/beacon_seed.h"
 #include "ash/constants/ash_features.h"
 #include "base/base64.h"
 #include "base/base64url.h"
 #include "base/no_destructor.h"
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
 
 namespace chromeos {
 
diff --git a/chromeos/components/multidevice/stub_multidevice_util.h b/ash/components/multidevice/stub_multidevice_util.h
similarity index 80%
rename from chromeos/components/multidevice/stub_multidevice_util.h
rename to ash/components/multidevice/stub_multidevice_util.h
index 2366ce9..6762822 100644
--- a/chromeos/components/multidevice/stub_multidevice_util.h
+++ b/ash/components/multidevice/stub_multidevice_util.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_
-#define CHROMEOS_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_
+#ifndef ASH_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_
+#define ASH_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_
 
-#include "chromeos/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device.h"
 
 namespace chromeos {
 
@@ -35,4 +35,4 @@
 }
 }  // namespace ash
 
-#endif  // CHROMEOS_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_
+#endif  // ASH_COMPONENTS_MULTIDEVICE_STUB_MULTIDEVICE_UTIL_H_
diff --git a/ash/components/phonehub/BUILD.gn b/ash/components/phonehub/BUILD.gn
index badbe06..601dc8cf 100644
--- a/ash/components/phonehub/BUILD.gn
+++ b/ash/components/phonehub/BUILD.gn
@@ -115,6 +115,8 @@
   ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/components/phonehub/proto",
     "//ash/constants",
     "//ash/services/device_sync/public/cpp",
@@ -124,8 +126,6 @@
     "//ash/services/secure_channel/public/mojom",
     "//ash/webui/eche_app_ui:eche_app_ui_pref",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/dbus/power",
     "//chromeos/services/network_config",
     "//chromeos/services/network_config:in_process_instance",
@@ -179,11 +179,11 @@
   public_deps = [ ":phonehub" ]
 
   deps = [
+    "//ash/components/multidevice/logging",
     "//ash/components/phonehub/proto",
     "//ash/constants",
     "//ash/services/multidevice_setup/public/mojom",
     "//base",
-    "//chromeos/components/multidevice/logging",
   ]
 }
 
@@ -253,6 +253,8 @@
     ":debug",
     ":phonehub",
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/components/phonehub/proto",
     "//ash/constants",
     "//ash/services/device_sync/public/cpp",
@@ -265,8 +267,6 @@
     "//ash/webui/eche_app_ui:eche_app_ui_pref",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//chromeos/dbus/power",
     "//chromeos/network:network",
     "//chromeos/network:test_support",
diff --git a/ash/components/phonehub/browser_tabs_model.cc b/ash/components/phonehub/browser_tabs_model.cc
index bdfd88e3..2dc923b 100644
--- a/ash/components/phonehub/browser_tabs_model.cc
+++ b/ash/components/phonehub/browser_tabs_model.cc
@@ -4,7 +4,7 @@
 
 #include "ash/components/phonehub/browser_tabs_model.h"
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc b/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc
index 515cf12..610ce1e 100644
--- a/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc
+++ b/ash/components/phonehub/camera_roll_thumbnail_decoder_impl.cc
@@ -4,13 +4,13 @@
 
 #include "ash/components/phonehub/camera_roll_thumbnail_decoder_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/camera_roll_item.h"
 #include "ash/components/phonehub/proto/phonehub_api.pb.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
 #include "ui/gfx/image/image.h"
diff --git a/ash/components/phonehub/connection_scheduler_impl.cc b/ash/components/phonehub/connection_scheduler_impl.cc
index 3eb85659..0eaa6acf 100644
--- a/ash/components/phonehub/connection_scheduler_impl.cc
+++ b/ash/components/phonehub/connection_scheduler_impl.cc
@@ -4,11 +4,11 @@
 
 #include "ash/components/phonehub/connection_scheduler_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/feature_status.h"
 #include "ash/services/secure_channel/public/cpp/client/connection_manager.h"
 #include "base/bind.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/cros_state_sender.cc b/ash/components/phonehub/cros_state_sender.cc
index f71cf278..4e01c5e 100644
--- a/ash/components/phonehub/cros_state_sender.cc
+++ b/ash/components/phonehub/cros_state_sender.cc
@@ -4,10 +4,10 @@
 
 #include "ash/components/phonehub/cros_state_sender.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/message_sender.h"
 #include "ash/components/phonehub/phone_model.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/do_not_disturb_controller_impl.cc b/ash/components/phonehub/do_not_disturb_controller_impl.cc
index db99419..8a4dcab 100644
--- a/ash/components/phonehub/do_not_disturb_controller_impl.cc
+++ b/ash/components/phonehub/do_not_disturb_controller_impl.cc
@@ -4,9 +4,9 @@
 
 #include "ash/components/phonehub/do_not_disturb_controller_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/message_sender.h"
 #include "ash/components/phonehub/user_action_recorder.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/fake_camera_roll_manager.cc b/ash/components/phonehub/fake_camera_roll_manager.cc
index e10972f..9cf6262f 100644
--- a/ash/components/phonehub/fake_camera_roll_manager.cc
+++ b/ash/components/phonehub/fake_camera_roll_manager.cc
@@ -4,8 +4,8 @@
 
 #include "ash/components/phonehub/fake_camera_roll_manager.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/proto/phonehub_api.pb.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/fake_multidevice_feature_access_manager.cc b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
index a97c99d..cc98fdd9 100644
--- a/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
+++ b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
@@ -52,6 +52,7 @@
     return;
 
   apps_access_status_ = apps_access_status;
+  NotifyAppsAccessChanged();
 }
 
 MultideviceFeatureAccessManager::AccessStatus
diff --git a/ash/components/phonehub/feature_status_provider_impl.cc b/ash/components/phonehub/feature_status_provider_impl.cc
index a102ad70..bc256a9e 100644
--- a/ash/components/phonehub/feature_status_provider_impl.cc
+++ b/ash/components/phonehub/feature_status_provider_impl.cc
@@ -6,14 +6,14 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 
 namespace ash {
diff --git a/ash/components/phonehub/feature_status_provider_impl_unittest.cc b/ash/components/phonehub/feature_status_provider_impl_unittest.cc
index 238df4ba..80f04450 100644
--- a/ash/components/phonehub/feature_status_provider_impl_unittest.cc
+++ b/ash/components/phonehub/feature_status_provider_impl_unittest.cc
@@ -7,11 +7,11 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_connection_manager.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "components/session_manager/core/session_manager.h"
diff --git a/ash/components/phonehub/find_my_device_controller_impl.cc b/ash/components/phonehub/find_my_device_controller_impl.cc
index 5dd2777..5a900dd 100644
--- a/ash/components/phonehub/find_my_device_controller_impl.cc
+++ b/ash/components/phonehub/find_my_device_controller_impl.cc
@@ -4,9 +4,9 @@
 
 #include "ash/components/phonehub/find_my_device_controller_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/message_sender.h"
 #include "ash/components/phonehub/user_action_recorder.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/invalid_connection_disconnector.cc b/ash/components/phonehub/invalid_connection_disconnector.cc
index b4f50571..1577953 100644
--- a/ash/components/phonehub/invalid_connection_disconnector.cc
+++ b/ash/components/phonehub/invalid_connection_disconnector.cc
@@ -4,10 +4,10 @@
 
 #include "ash/components/phonehub/invalid_connection_disconnector.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/phone_model.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/message_receiver.h b/ash/components/phonehub/message_receiver.h
index 16ffa5c8..593a65c2 100644
--- a/ash/components/phonehub/message_receiver.h
+++ b/ash/components/phonehub/message_receiver.h
@@ -5,10 +5,10 @@
 #ifndef ASH_COMPONENTS_PHONEHUB_MESSAGE_RECEIVER_H_
 #define ASH_COMPONENTS_PHONEHUB_MESSAGE_RECEIVER_H_
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/proto/phonehub_api.pb.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 // Responsible for receiving message updates from the remote phone device.
 namespace ash {
diff --git a/ash/components/phonehub/multidevice_feature_access_manager.cc b/ash/components/phonehub/multidevice_feature_access_manager.cc
index 706d7af..5ef4348 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager.cc
+++ b/ash/components/phonehub/multidevice_feature_access_manager.cc
@@ -4,9 +4,9 @@
 
 #include "ash/components/phonehub/multidevice_feature_access_manager.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
@@ -55,6 +55,11 @@
     observer.OnCameraRollAccessChanged();
 }
 
+void MultideviceFeatureAccessManager::NotifyAppsAccessChanged() {
+  for (auto& observer : observer_list_)
+    observer.OnAppsAccessChanged();
+}
+
 void MultideviceFeatureAccessManager::SetNotificationSetupOperationStatus(
     NotificationAccessSetupOperation::Status new_status) {
   DCHECK(IsSetupOperationInProgress());
@@ -92,6 +97,10 @@
   // Optional method, inherit class doesn't have to implement this
 }
 
+void MultideviceFeatureAccessManager::Observer::OnAppsAccessChanged() {
+  // Optional method, inherit class doesn't have to implement this
+}
+
 std::ostream& operator<<(std::ostream& stream,
                          MultideviceFeatureAccessManager::AccessStatus status) {
   switch (status) {
diff --git a/ash/components/phonehub/multidevice_feature_access_manager.h b/ash/components/phonehub/multidevice_feature_access_manager.h
index dfaf4b37..2b8b8710 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager.h
+++ b/ash/components/phonehub/multidevice_feature_access_manager.h
@@ -72,6 +72,10 @@
     // Called when camera roll access has changed; use
     // GetCameraRollAccessStatus() for the new status.
     virtual void OnCameraRollAccessChanged();
+
+    // Called when apps access has changed; use
+    // GetAppsAccessStatus() for the new status.
+    virtual void OnAppsAccessChanged();
   };
 
   MultideviceFeatureAccessManager(MultideviceFeatureAccessManager&) = delete;
@@ -118,6 +122,7 @@
 
   void NotifyNotificationAccessChanged();
   void NotifyCameraRollAccessChanged();
+  void NotifyAppsAccessChanged();
   void SetNotificationSetupOperationStatus(
       NotificationAccessSetupOperation::Status new_status);
 
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl.cc b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
index 22ea4db4..c7d86df 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
@@ -4,12 +4,14 @@
 
 #include "ash/components/phonehub/multidevice_feature_access_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/connection_scheduler.h"
 #include "ash/components/phonehub/message_sender.h"
 #include "ash/components/phonehub/pref_names.h"
 #include "ash/components/phonehub/util/histogram_util.h"
+#include "ash/constants/ash_features.h"
 #include "ash/webui/eche_app_ui/pref_names.h"
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "pref_names.h"
@@ -48,10 +50,18 @@
 
   current_feature_status_ = feature_status_provider_->GetStatus();
   feature_status_provider_->AddObserver(this);
+
+  pref_change_registrar_.Init(pref_service_);
+  pref_change_registrar_.Add(
+      eche_app::prefs::kAppsAccessStatus,
+      base::BindRepeating(
+          &MultideviceFeatureAccessManagerImpl::NotifyAppsAccessChanged,
+          base::Unretained(this)));
 }
 
 MultideviceFeatureAccessManagerImpl::~MultideviceFeatureAccessManagerImpl() {
   feature_status_provider_->RemoveObserver(this);
+  pref_change_registrar_.RemoveAll();
 }
 
 bool MultideviceFeatureAccessManagerImpl::
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl.h b/ash/components/phonehub/multidevice_feature_access_manager_impl.h
index 0670373..d52e8f1 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager_impl.h
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.h
@@ -9,6 +9,7 @@
 
 #include "ash/components/phonehub/feature_status_provider.h"
 #include "ash/components/phonehub/message_receiver.h"
+#include "components/prefs/pref_change_registrar.h"
 
 class PrefRegistrySimple;
 class PrefService;
@@ -65,6 +66,9 @@
   FeatureStatusProvider* feature_status_provider_;
   MessageSender* message_sender_;
   ConnectionScheduler* connection_scheduler_;
+
+  // Registers preference value change listeners.
+  PrefChangeRegistrar pref_change_registrar_;
 };
 
 }  // namespace phonehub
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc b/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc
index 5dc97a50..3951777 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl_unittest.cc
@@ -11,6 +11,8 @@
 #include "ash/components/phonehub/fake_message_sender.h"
 #include "ash/components/phonehub/notification_access_setup_operation.h"
 #include "ash/components/phonehub/pref_names.h"
+#include "ash/webui/eche_app_ui/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -35,6 +37,9 @@
   // MultideviceFeatureAccessManager::Observer:
   void OnCameraRollAccessChanged() override { ++num_calls_; }
 
+  // MultideviceFeatureAccessManager::Observer:
+  void OnAppsAccessChanged() override { ++num_calls_; }
+
  private:
   size_t num_calls_ = 0;
 };
@@ -77,6 +82,9 @@
         std::make_unique<FakeFeatureStatusProvider>();
     fake_message_sender_ = std::make_unique<FakeMessageSender>();
     fake_connection_scheduler_ = std::make_unique<FakeConnectionScheduler>();
+    pref_service_.registry()->RegisterIntegerPref(
+        eche_app::prefs::kAppsAccessStatus,
+        /*default_value=*/0);
   }
 
   void TearDown() override { manager_->RemoveObserver(&fake_observer_); }
@@ -98,6 +106,12 @@
     manager_->AddObserver(&fake_observer_);
   }
 
+  void InitializeAppsAccessStatus(MultideviceFeatureAccessManager::AccessStatus
+                                      apps_access_expected_status) {
+    pref_service_.SetInteger(eche_app::prefs::kAppsAccessStatus,
+                             static_cast<int>(apps_access_expected_status));
+  }
+
   NotificationAccessSetupOperation::Status
   GetNotificationAccessSetupOperationStatus() {
     return fake_delegate_.status();
@@ -127,6 +141,13 @@
     EXPECT_EQ(expected_status, manager_->GetCameraRollAccessStatus());
   }
 
+  void VerifyAppsAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus expected_status) {
+    EXPECT_EQ(static_cast<int>(expected_status),
+              pref_service_.GetInteger(eche_app::prefs::kAppsAccessStatus));
+    EXPECT_EQ(expected_status, manager_->GetAppsAccessStatus());
+  }
+
   bool HasMultideviceFeatureSetupUiBeenDismissed() {
     return manager_->HasMultideviceFeatureSetupUiBeenDismissed();
   }
@@ -597,5 +618,28 @@
   EXPECT_EQ(1u, GetNumObserverCalls());
 }
 
+TEST_F(MultideviceFeatureAccessManagerImplTest, AppsAccessChanged) {
+  InitializeAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted,
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  InitializeAppsAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  EXPECT_EQ(1u, GetNumObserverCalls());
+
+  InitializeAppsAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kAvailableButNotGranted);
+  EXPECT_EQ(2u, GetNumObserverCalls());
+
+  InitializeAppsAccessStatus(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  VerifyAppsAccessGrantedState(
+      MultideviceFeatureAccessManager::AccessStatus::kProhibited);
+  EXPECT_EQ(3u, GetNumObserverCalls());
+}
+
 }  // namespace phonehub
 }  // namespace ash
diff --git a/ash/components/phonehub/multidevice_setup_state_updater.cc b/ash/components/phonehub/multidevice_setup_state_updater.cc
index 1e063589..3609fdd 100644
--- a/ash/components/phonehub/multidevice_setup_state_updater.cc
+++ b/ash/components/phonehub/multidevice_setup_state_updater.cc
@@ -4,11 +4,11 @@
 
 #include "ash/components/phonehub/multidevice_setup_state_updater.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/pref_names.h"
 #include "ash/components/phonehub/util/histogram_util.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "base/callback_helpers.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/components/phonehub/notification_manager.cc b/ash/components/phonehub/notification_manager.cc
index 67901a0..ef3f62d 100644
--- a/ash/components/phonehub/notification_manager.cc
+++ b/ash/components/phonehub/notification_manager.cc
@@ -6,7 +6,7 @@
 
 #include <sstream>
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/notification_manager_impl.cc b/ash/components/phonehub/notification_manager_impl.cc
index d992be44..7b746b59 100644
--- a/ash/components/phonehub/notification_manager_impl.cc
+++ b/ash/components/phonehub/notification_manager_impl.cc
@@ -4,11 +4,11 @@
 
 #include "ash/components/phonehub/notification_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/message_sender.h"
 #include "ash/components/phonehub/notification.h"
 #include "ash/components/phonehub/user_action_recorder.h"
 #include "base/containers/flat_set.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/notification_processor.cc b/ash/components/phonehub/notification_processor.cc
index a049952..5549268 100644
--- a/ash/components/phonehub/notification_processor.cc
+++ b/ash/components/phonehub/notification_processor.cc
@@ -4,6 +4,7 @@
 
 #include "ash/components/phonehub/notification_processor.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/notification.h"
 #include "ash/components/phonehub/notification_manager.h"
 #include "ash/constants/ash_features.h"
@@ -11,7 +12,6 @@
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
diff --git a/ash/components/phonehub/phone_status_model.cc b/ash/components/phonehub/phone_status_model.cc
index 3c035a1f..d6235f69 100644
--- a/ash/components/phonehub/phone_status_model.cc
+++ b/ash/components/phonehub/phone_status_model.cc
@@ -4,7 +4,7 @@
 
 #include "ash/components/phonehub/phone_status_model.h"
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace phonehub {
diff --git a/ash/components/phonehub/phone_status_processor_unittest.cc b/ash/components/phonehub/phone_status_processor_unittest.cc
index 655eb0f3..86fc413 100644
--- a/ash/components/phonehub/phone_status_processor_unittest.cc
+++ b/ash/components/phonehub/phone_status_processor_unittest.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/phonehub/fake_do_not_disturb_controller.h"
 #include "ash/components/phonehub/fake_feature_status_provider.h"
 #include "ash/components/phonehub/fake_find_my_device_controller.h"
@@ -28,7 +29,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/image/image.h"
 
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
index b1cd21db..cd2ec1e 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
@@ -4,9 +4,10 @@
 
 #include "ash/components/phonehub/recent_apps_interaction_handler_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/notification.h"
 #include "ash/components/phonehub/pref_names.h"
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/constants/ash_features.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
@@ -182,6 +183,10 @@
   ComputeAndUpdateUiState();
 }
 
+void RecentAppsInteractionHandlerImpl::OnAppsAccessChanged() {
+  ComputeAndUpdateUiState();
+}
+
 void RecentAppsInteractionHandlerImpl::ComputeAndUpdateUiState() {
   ui_state_ = RecentAppsUiState::HIDDEN;
 
@@ -195,7 +200,14 @@
   // 3. Otherwise, no recent apps view will be shown.
   bool allow_streaming = multidevice_setup_client_->GetFeatureState(
                              Feature::kEche) == FeatureState::kEnabledByUser;
-  if (!allow_streaming) {
+
+  bool is_apps_access_required =
+      features::IsEchePhoneHubPermissionsOnboarding() &&
+      multidevice_feature_access_manager_->GetAppsAccessStatus() ==
+          phonehub::MultideviceFeatureAccessManager::AccessStatus::
+              kAvailableButNotGranted;
+
+  if (!allow_streaming || is_apps_access_required) {
     NotifyRecentAppsViewUiStateUpdated();
     return;
   }
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.h b/ash/components/phonehub/recent_apps_interaction_handler_impl.h
index eb625eb..bf6e37c 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl.h
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.h
@@ -56,6 +56,7 @@
 
   // MultideviceFeatureAccessManager::Observer:
   void OnNotificationAccessChanged() override;
+  void OnAppsAccessChanged() override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(RecentAppsInteractionHandlerTest, RecentAppsUpdated);
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
index 99faf543..0c3038e 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
@@ -9,8 +9,10 @@
 #include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/notification.h"
 #include "ash/components/phonehub/pref_names.h"
+#include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "base/test/scoped_feature_list.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -58,6 +60,9 @@
 
   // testing::Test:
   void SetUp() override {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kEchePhoneHubPermissionsOnboarding},
+        /*disabled_features=*/{});
     RecentAppsInteractionHandlerImpl::RegisterPrefs(pref_service_.registry());
     fake_multidevice_setup_client_ =
         std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
@@ -152,6 +157,13 @@
             MultideviceFeatureAccessManager::AccessProhibitedReason::kUnknown);
   }
 
+  void SetAppsAccessStatus(bool enabled) {
+    fake_multidevice_feature_access_manager_.SetAppsAccessStatusInternal(
+        enabled ? MultideviceFeatureAccessManager::AccessStatus::kAccessGranted
+                : MultideviceFeatureAccessManager::AccessStatus::
+                      kAvailableButNotGranted);
+  }
+
   std::vector<RecentAppsInteractionHandler::UserState> GetDefaultUserStates() {
     RecentAppsInteractionHandler::UserState user_state1;
     user_state1.user_id = 1;
@@ -226,6 +238,7 @@
   std::unique_ptr<RecentAppsInteractionHandlerImpl> interaction_handler_;
   TestingPrefServiceSimple pref_service_;
   FakeMultideviceFeatureAccessManager fake_multidevice_feature_access_manager_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(RecentAppsInteractionHandlerTest, RecentAppsClicked) {
@@ -438,6 +451,7 @@
        OnFeatureStatesChangedToEnabledWithEmptyRecentAppsList) {
   SetEcheFeatureState(FeatureState::kEnabledByUser);
   SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser);
+  SetAppsAccessStatus(true);
   SetNotificationAccess(true);
 
   EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::PLACEHOLDER_VIEW,
@@ -448,6 +462,7 @@
        DisableNotificationAccessWithEmptyRecentAppsList) {
   SetEcheFeatureState(FeatureState::kEnabledByUser);
   SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser);
+  SetAppsAccessStatus(true);
   SetNotificationAccess(true);
 
   EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::PLACEHOLDER_VIEW,
@@ -491,7 +506,7 @@
       Notification::AppMetadata(app_visible_name1, package_name1, gfx::Image(),
                                 /*icon_color=*/absl::nullopt,
                                 /*icon_is_monochrome=*/true, expected_user_id1);
-
+  SetAppsAccessStatus(true);
   handler().NotifyRecentAppAddedOrUpdated(app_metadata1, now);
   SetEcheFeatureState(FeatureState::kEnabledByUser);
 
@@ -510,6 +525,7 @@
                                 /*icon_color=*/absl::nullopt,
                                 /*icon_is_monochrome=*/true, expected_user_id1);
 
+  SetAppsAccessStatus(true);
   handler().NotifyRecentAppAddedOrUpdated(app_metadata1, now);
   SetEcheFeatureState(FeatureState::kEnabledByUser);
 
@@ -541,6 +557,7 @@
        UiStateChangedToVisibleWhenRecentAppBeAdded) {
   SetEcheFeatureState(FeatureState::kEnabledByUser);
   SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser);
+  SetAppsAccessStatus(true);
   SetNotificationAccess(true);
 
   EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::PLACEHOLDER_VIEW,
@@ -560,6 +577,45 @@
             handler().ui_state());
 }
 
+TEST_F(RecentAppsInteractionHandlerTest, DisableAppsAccess) {
+  GenerateDefaultAppMetadata();
+
+  // The apps access has not been granted yet so the UI state always HIDDEN.
+  SetAppsAccessStatus(true);
+
+  SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser);
+  SetNotificationAccess(true);
+
+  EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN,
+            handler().ui_state());
+
+  SetNotificationAccess(false);
+
+  EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN,
+            handler().ui_state());
+
+  // Disable notification access permission on the local device.
+  SetNotificationAccess(true);
+  SetPhoneHubNotificationsFeatureState(FeatureState::kDisabledByUser);
+
+  EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN,
+            handler().ui_state());
+
+  // Disable notification access permission on both devices.
+  SetNotificationAccess(false);
+  SetPhoneHubNotificationsFeatureState(FeatureState::kDisabledByUser);
+
+  EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN,
+            handler().ui_state());
+
+  // Enable notification access permission back on both devices.
+  SetNotificationAccess(true);
+  SetPhoneHubNotificationsFeatureState(FeatureState::kEnabledByUser);
+
+  EXPECT_EQ(RecentAppsInteractionHandler::RecentAppsUiState::HIDDEN,
+            handler().ui_state());
+}
+
 TEST_F(RecentAppsInteractionHandlerTest,
        PrefBeClearedWhenFeatureStatesChangedToUnavailableNoVerifiedHost) {
   SaveRecentAppsToPref();
diff --git a/ash/components/phonehub/tether_controller_impl.cc b/ash/components/phonehub/tether_controller_impl.cc
index 07231a63..c10f234 100644
--- a/ash/components/phonehub/tether_controller_impl.cc
+++ b/ash/components/phonehub/tether_controller_impl.cc
@@ -4,10 +4,10 @@
 
 #include "ash/components/phonehub/tether_controller_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/phone_status_model.h"
 #include "ash/components/phonehub/user_action_recorder.h"
 #include "ash/components/phonehub/util/histogram_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/services/network_config/in_process_instance.h"
 
 namespace ash {
diff --git a/ash/components/proximity_auth/BUILD.gn b/ash/components/proximity_auth/BUILD.gn
index 3e4185c..ece0cc00 100644
--- a/ash/components/proximity_auth/BUILD.gn
+++ b/ash/components/proximity_auth/BUILD.gn
@@ -46,6 +46,8 @@
   ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/components/proximity_auth/public/mojom",
     "//ash/constants",
     "//ash/public/cpp",
@@ -54,8 +56,6 @@
     "//ash/services/secure_channel/public/cpp/client",
     "//ash/services/secure_channel/public/mojom",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/dbus/power",
     "//chromeos/dbus/session_manager",
     "//components/account_id",
@@ -81,13 +81,13 @@
   public_deps = [ ":proximity_auth" ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
+    "//ash/components/multidevice/logging",
     "//ash/services/device_sync/proto",
     "//ash/services/secure_channel:test_support",
     "//ash/services/secure_channel/public/cpp/client",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
-    "//chromeos/components/multidevice/logging",
     "//testing/gmock",
   ]
 }
@@ -108,6 +108,9 @@
   deps = [
     ":proximity_auth",
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/public/cpp",
     "//ash/services/multidevice_setup/public/cpp:prefs",
@@ -116,9 +119,6 @@
     "//ash/services/secure_channel/public/cpp/client:test_support",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/dbus/power",
     "//chromeos/dbus/power:power_manager_proto",
     "//components/prefs:test_support",
diff --git a/ash/components/proximity_auth/fake_remote_device_life_cycle.h b/ash/components/proximity_auth/fake_remote_device_life_cycle.h
index 0bc2deb..7808a11 100644
--- a/ash/components/proximity_auth/fake_remote_device_life_cycle.h
+++ b/ash/components/proximity_auth/fake_remote_device_life_cycle.h
@@ -5,11 +5,11 @@
 #ifndef ASH_COMPONENTS_PROXIMITY_AUTH_FAKE_REMOTE_DEVICE_LIFE_CYCLE_H_
 #define ASH_COMPONENTS_PROXIMITY_AUTH_FAKE_REMOTE_DEVICE_LIFE_CYCLE_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/proximity_auth/remote_device_life_cycle.h"
 // TODO(https://crbug.com/1164001): move to forward declaration.
 #include "ash/services/secure_channel/public/cpp/client/client_channel.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace proximity_auth {
 
diff --git a/ash/components/proximity_auth/messenger_impl.cc b/ash/components/proximity_auth/messenger_impl.cc
index 4b44eb9..d62ad4b 100644
--- a/ash/components/proximity_auth/messenger_impl.cc
+++ b/ash/components/proximity_auth/messenger_impl.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/messenger_observer.h"
 #include "ash/components/proximity_auth/remote_status_update.h"
 #include "base/base64url.h"
@@ -16,7 +17,6 @@
 #include "base/location.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace proximity_auth {
 
diff --git a/ash/components/proximity_auth/messenger_impl_unittest.cc b/ash/components/proximity_auth/messenger_impl_unittest.cc
index 4f9439a..afb6c95 100644
--- a/ash/components/proximity_auth/messenger_impl_unittest.cc
+++ b/ash/components/proximity_auth/messenger_impl_unittest.cc
@@ -6,13 +6,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/proximity_auth/messenger_observer.h"
 #include "ash/components/proximity_auth/remote_status_update.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_client_channel.h"
 #include "base/callback.h"
 #include "base/test/scoped_feature_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
index da82de59..0454e7b 100644
--- a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
+++ b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
@@ -7,11 +7,11 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/proximity_auth_pref_names.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "base/logging.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
index 14e0b12..9472cd8a 100644
--- a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
+++ b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
@@ -7,11 +7,11 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/proximity_auth_pref_names.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "base/bind.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/ash/components/proximity_auth/proximity_auth_system.cc b/ash/components/proximity_auth/proximity_auth_system.cc
index d6051a64..53e7b81 100644
--- a/ash/components/proximity_auth/proximity_auth_system.cc
+++ b/ash/components/proximity_auth/proximity_auth_system.cc
@@ -4,11 +4,11 @@
 
 #include "ash/components/proximity_auth/proximity_auth_system.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/proximity_auth_client.h"
 #include "ash/components/proximity_auth/remote_device_life_cycle_impl.h"
 #include "ash/components/proximity_auth/unlock_manager_impl.h"
 #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace proximity_auth {
 
diff --git a/ash/components/proximity_auth/proximity_auth_system.h b/ash/components/proximity_auth/proximity_auth_system.h
index 4c1d8b5b..ef2f559 100644
--- a/ash/components/proximity_auth/proximity_auth_system.h
+++ b/ash/components/proximity_auth/proximity_auth_system.h
@@ -8,8 +8,8 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "components/account_id/account_id.h"
 
 namespace ash {
diff --git a/ash/components/proximity_auth/proximity_auth_system_unittest.cc b/ash/components/proximity_auth/proximity_auth_system_unittest.cc
index 0a96a80..4344099 100644
--- a/ash/components/proximity_auth/proximity_auth_system_unittest.cc
+++ b/ash/components/proximity_auth/proximity_auth_system_unittest.cc
@@ -6,6 +6,10 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/components/proximity_auth/fake_lock_handler.h"
 #include "ash/components/proximity_auth/fake_remote_device_life_cycle.h"
 #include "ash/components/proximity_auth/mock_proximity_auth_client.h"
@@ -17,10 +21,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/proximity_auth/proximity_monitor_impl.cc b/ash/components/proximity_auth/proximity_monitor_impl.cc
index 824e717d..e2ba09c 100644
--- a/ash/components/proximity_auth/proximity_monitor_impl.cc
+++ b/ash/components/proximity_auth/proximity_monitor_impl.cc
@@ -9,12 +9,12 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/metrics.h"
 #include "ash/services/secure_channel/public/cpp/client/client_channel.h"
 #include "base/bind.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 
diff --git a/ash/components/proximity_auth/proximity_monitor_impl.h b/ash/components/proximity_auth/proximity_monitor_impl.h
index 4ca05fc7..026f5d19 100644
--- a/ash/components/proximity_auth/proximity_monitor_impl.h
+++ b/ash/components/proximity_auth/proximity_monitor_impl.h
@@ -7,10 +7,10 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/proximity_auth/proximity_monitor.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc b/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc
index cb230b8..16b1fe0 100644
--- a/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc
+++ b/ash/components/proximity_auth/proximity_monitor_impl_unittest.cc
@@ -7,6 +7,10 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/components/proximity_auth/proximity_monitor_observer.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/secure_channel/fake_connection.h"
@@ -20,10 +24,6 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/ash/components/proximity_auth/remote_device_life_cycle.h b/ash/components/proximity_auth/remote_device_life_cycle.h
index 5401c37..b0f97af 100644
--- a/ash/components/proximity_auth/remote_device_life_cycle.h
+++ b/ash/components/proximity_auth/remote_device_life_cycle.h
@@ -7,7 +7,7 @@
 
 #include <ostream>
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 namespace secure_channel {
diff --git a/ash/components/proximity_auth/remote_device_life_cycle_impl.cc b/ash/components/proximity_auth/remote_device_life_cycle_impl.cc
index 4ac5f147..d439789a 100644
--- a/ash/components/proximity_auth/remote_device_life_cycle_impl.cc
+++ b/ash/components/proximity_auth/remote_device_life_cycle_impl.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/messenger_impl.h"
 #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
 #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h"
 #include "base/bind.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace proximity_auth {
 
diff --git a/ash/components/proximity_auth/remote_device_life_cycle_impl.h b/ash/components/proximity_auth/remote_device_life_cycle_impl.h
index 767a491..8be6370 100644
--- a/ash/components/proximity_auth/remote_device_life_cycle_impl.h
+++ b/ash/components/proximity_auth/remote_device_life_cycle_impl.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/proximity_auth/messenger_observer.h"
 #include "ash/components/proximity_auth/remote_device_life_cycle.h"
 #include "ash/services/secure_channel/public/cpp/client/connection_attempt.h"
@@ -15,7 +16,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 namespace secure_channel {
diff --git a/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc b/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc
index 2ccd652c..40e6691 100644
--- a/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc
+++ b/ash/components/proximity_auth/remote_device_life_cycle_impl_unittest.cc
@@ -9,6 +9,8 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/proximity_auth/messenger.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_client_channel.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_connection_attempt.h"
@@ -17,8 +19,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/proximity_auth/remote_status_update.cc b/ash/components/proximity_auth/remote_status_update.cc
index 628397e..3bee9b5 100644
--- a/ash/components/proximity_auth/remote_status_update.cc
+++ b/ash/components/proximity_auth/remote_status_update.cc
@@ -4,8 +4,8 @@
 
 #include "ash/components/proximity_auth/remote_status_update.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace {
 
diff --git a/ash/components/proximity_auth/screenlock_bridge.cc b/ash/components/proximity_auth/screenlock_bridge.cc
index 051241a..ecfbf87 100644
--- a/ash/components/proximity_auth/screenlock_bridge.cc
+++ b/ash/components/proximity_auth/screenlock_bridge.cc
@@ -9,8 +9,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "build/build_config.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 
 namespace proximity_auth {
diff --git a/ash/components/proximity_auth/unlock_manager_impl.cc b/ash/components/proximity_auth/unlock_manager_impl.cc
index 924c38a..388988bb 100644
--- a/ash/components/proximity_auth/unlock_manager_impl.cc
+++ b/ash/components/proximity_auth/unlock_manager_impl.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/proximity_auth/messenger.h"
 #include "ash/components/proximity_auth/metrics.h"
 #include "ash/components/proximity_auth/proximity_auth_client.h"
@@ -18,8 +20,6 @@
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 
 namespace proximity_auth {
diff --git a/ash/components/proximity_auth/unlock_manager_impl_unittest.cc b/ash/components/proximity_auth/unlock_manager_impl_unittest.cc
index c80a5ab..277811d 100644
--- a/ash/components/proximity_auth/unlock_manager_impl_unittest.cc
+++ b/ash/components/proximity_auth/unlock_manager_impl_unittest.cc
@@ -7,6 +7,8 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/proximity_auth/fake_lock_handler.h"
 #include "ash/components/proximity_auth/fake_remote_device_life_cycle.h"
 #include "ash/components/proximity_auth/messenger.h"
@@ -23,8 +25,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/timer/mock_timer.h"
 #include "build/build_config.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/suspend.pb.h"
diff --git a/ash/components/tether/BUILD.gn b/ash/components/tether/BUILD.gn
index 6d1c7bb..25616c9b 100644
--- a/ash/components/tether/BUILD.gn
+++ b/ash/components/tether/BUILD.gn
@@ -117,6 +117,7 @@
   ]
 
   deps = [
+    "//ash/components/multidevice/logging",
     "//ash/components/tether/proto",
     "//ash/constants",
     "//ash/services/device_sync/public/cpp",
@@ -125,7 +126,6 @@
     "//ash/services/secure_channel/public/cpp/shared",
     "//ash/services/secure_channel/public/mojom",
     "//base",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/dbus/power",
     "//chromeos/login/login_state",
     "//chromeos/network",
@@ -200,14 +200,14 @@
   public_deps = [ ":tether" ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/components/tether/proto",
     "//ash/services/device_sync/public/cpp:test_support",
     "//ash/services/secure_channel/public/cpp/client:test_support",
     "//ash/services/secure_channel/public/cpp/shared",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//chromeos/network:test_support",
     "//device/bluetooth",
     "//testing/gmock",
@@ -262,6 +262,8 @@
   deps = [
     ":test_support",
     ":tether",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/components/tether/proto",
     "//ash/services/device_sync:test_support",
     "//ash/services/device_sync/public/cpp",
@@ -273,8 +275,6 @@
     "//ash/services/secure_channel/public/cpp/client:test_support",
     "//ash/services/secure_channel/public/cpp/shared",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//chromeos/dbus/power",
     "//chromeos/login/login_state",
     "//chromeos/network:test_support",
diff --git a/ash/components/tether/active_host.cc b/ash/components/tether/active_host.cc
index 9137bb49..50fd3efbb 100644
--- a/ash/components/tether/active_host.cc
+++ b/ash/components/tether/active_host.cc
@@ -4,13 +4,13 @@
 
 #include "ash/components/tether/active_host.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/pref_names.h"
 #include "ash/components/tether/tether_host_fetcher.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/components/tether/active_host.h b/ash/components/tether/active_host.h
index 1fc436973..29849457 100644
--- a/ash/components/tether/active_host.h
+++ b/ash/components/tether/active_host.h
@@ -7,10 +7,10 @@
 
 #include <string>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefRegistrySimple;
diff --git a/ash/components/tether/active_host_network_state_updater.cc b/ash/components/tether/active_host_network_state_updater.cc
index 7f676d1..574708de 100644
--- a/ash/components/tether/active_host_network_state_updater.cc
+++ b/ash/components/tether/active_host_network_state_updater.cc
@@ -4,10 +4,10 @@
 
 #include "ash/components/tether/active_host_network_state_updater.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/active_host.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/network/network_state_handler.h"
 
 namespace ash {
diff --git a/ash/components/tether/active_host_unittest.cc b/ash/components/tether/active_host_unittest.cc
index eef9239..0602392 100644
--- a/ash/components/tether/active_host_unittest.cc
+++ b/ash/components/tether/active_host_unittest.cc
@@ -6,11 +6,11 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/fake_tether_host_fetcher.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc b/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc
index 46468049..bf6cd9f7 100644
--- a/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc
+++ b/ash/components/tether/asynchronous_shutdown_object_container_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/fake_disconnect_tethering_request_sender.h"
 #include "ash/components/tether/fake_tether_host_fetcher.h"
 #include "ash/components/tether/tether_component_impl.h"
@@ -16,7 +17,6 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/connect_tethering_operation.cc b/ash/components/tether/connect_tethering_operation.cc
index 6aa17c7..d447ff4 100644
--- a/ash/components/tether/connect_tethering_operation.cc
+++ b/ash/components/tether/connect_tethering_operation.cc
@@ -4,13 +4,13 @@
 
 #include "ash/components/tether/connect_tethering_operation.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/proto/tether.pb.h"
 #include "ash/components/tether/tether_host_response_recorder.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/connect_tethering_operation.h b/ash/components/tether/connect_tethering_operation.h
index ab3069cd..15c4835 100644
--- a/ash/components/tether/connect_tethering_operation.h
+++ b/ash/components/tether/connect_tethering_operation.h
@@ -10,6 +10,7 @@
 #include <map>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/message_transfer_operation.h"
 // TODO(https://crbug.com/1164001): move to forward declaration
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
@@ -19,7 +20,6 @@
 #include "base/observer_list.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/connect_tethering_operation_unittest.cc b/ash/components/tether/connect_tethering_operation_unittest.cc
index e54c3612..ff5523ec 100644
--- a/ash/components/tether/connect_tethering_operation_unittest.cc
+++ b/ash/components/tether/connect_tethering_operation_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/mock_tether_host_response_recorder.h"
 #include "ash/components/tether/proto/tether.pb.h"
@@ -22,7 +23,6 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/connection_preserver_impl.cc b/ash/components/tether/connection_preserver_impl.cc
index f3e4841..804acf4e 100644
--- a/ash/components/tether/connection_preserver_impl.cc
+++ b/ash/components/tether/connection_preserver_impl.cc
@@ -4,11 +4,11 @@
 
 #include "ash/components/tether/connection_preserver_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/tether_host_response_recorder.h"
 #include "base/bind.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
diff --git a/ash/components/tether/connection_preserver_impl_unittest.cc b/ash/components/tether/connection_preserver_impl_unittest.cc
index b600ef0..e0f8f20 100644
--- a/ash/components/tether/connection_preserver_impl_unittest.cc
+++ b/ash/components/tether/connection_preserver_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/fake_active_host.h"
 #include "ash/components/tether/mock_tether_host_response_recorder.h"
 #include "ash/components/tether/timer_factory.h"
@@ -18,7 +19,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/test/task_environment.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
diff --git a/ash/components/tether/crash_recovery_manager_impl.cc b/ash/components/tether/crash_recovery_manager_impl.cc
index 2a5b725..5e75c5a7 100644
--- a/ash/components/tether/crash_recovery_manager_impl.cc
+++ b/ash/components/tether/crash_recovery_manager_impl.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/host_scan_cache.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 
diff --git a/ash/components/tether/crash_recovery_manager_impl_unittest.cc b/ash/components/tether/crash_recovery_manager_impl_unittest.cc
index 974d59c9..795d8c8 100644
--- a/ash/components/tether/crash_recovery_manager_impl_unittest.cc
+++ b/ash/components/tether/crash_recovery_manager_impl_unittest.cc
@@ -7,13 +7,13 @@
 #include <memory>
 #include <sstream>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/fake_active_host.h"
 #include "ash/components/tether/fake_host_scan_cache.h"
 #include "ash/components/tether/host_scan_cache_entry.h"
 #include "base/bind.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/device_status_util_unittest.cc b/ash/components/tether/device_status_util_unittest.cc
index b05932c..b7b86c7 100644
--- a/ash/components/tether/device_status_util_unittest.cc
+++ b/ash/components/tether/device_status_util_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/fake_tether_host_fetcher.h"
 #include "ash/components/tether/proto_test_util.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/tether/disconnect_tethering_operation.cc b/ash/components/tether/disconnect_tethering_operation.cc
index 93930b9..31b2389 100644
--- a/ash/components/tether/disconnect_tethering_operation.cc
+++ b/ash/components/tether/disconnect_tethering_operation.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/proto/tether.pb.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/disconnect_tethering_operation_unittest.cc b/ash/components/tether/disconnect_tethering_operation_unittest.cc
index 506b49a..86aebc3 100644
--- a/ash/components/tether/disconnect_tethering_operation_unittest.cc
+++ b/ash/components/tether/disconnect_tethering_operation_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/proto/tether.pb.h"
 #include "ash/components/tether/test_timer_factory.h"
@@ -20,7 +21,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/tether/disconnect_tethering_request_sender_impl.cc b/ash/components/tether/disconnect_tethering_request_sender_impl.cc
index 38c2100..864d305 100644
--- a/ash/components/tether/disconnect_tethering_request_sender_impl.cc
+++ b/ash/components/tether/disconnect_tethering_request_sender_impl.cc
@@ -6,11 +6,11 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/tether_host_fetcher.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc b/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc
index 584844f..e43f2f7 100644
--- a/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc
+++ b/ash/components/tether/disconnect_tethering_request_sender_impl_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/disconnect_tethering_operation.h"
 #include "ash/components/tether/disconnect_tethering_request_sender.h"
 #include "ash/components/tether/fake_tether_host_fetcher.h"
@@ -13,8 +15,6 @@
 #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/ash/components/tether/fake_active_host.cc b/ash/components/tether/fake_active_host.cc
index 65c06811..f75740d 100644
--- a/ash/components/tether/fake_active_host.cc
+++ b/ash/components/tether/fake_active_host.cc
@@ -6,10 +6,10 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "base/base64.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/components/tether/fake_tether_host_fetcher.h b/ash/components/tether/fake_tether_host_fetcher.h
index 1328e22..198dd92 100644
--- a/ash/components/tether/fake_tether_host_fetcher.h
+++ b/ash/components/tether/fake_tether_host_fetcher.h
@@ -7,8 +7,8 @@
 
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/tether_host_fetcher.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/gms_core_notifications_state_tracker_impl.cc b/ash/components/tether/gms_core_notifications_state_tracker_impl.cc
index 4aa8b7d..459c3b8 100644
--- a/ash/components/tether/gms_core_notifications_state_tracker_impl.cc
+++ b/ash/components/tether/gms_core_notifications_state_tracker_impl.cc
@@ -6,7 +6,7 @@
 
 #include <sstream>
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/gms_core_notifications_state_tracker_impl.h b/ash/components/tether/gms_core_notifications_state_tracker_impl.h
index 024c8d3..4a13fa96 100644
--- a/ash/components/tether/gms_core_notifications_state_tracker_impl.h
+++ b/ash/components/tether/gms_core_notifications_state_tracker_impl.h
@@ -9,9 +9,9 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/gms_core_notifications_state_tracker.h"
 #include "ash/components/tether/host_scanner_operation.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc b/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc
index 9eb80201..ed32dfd 100644
--- a/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc
+++ b/ash/components/tether/gms_core_notifications_state_tracker_impl_unittest.cc
@@ -9,8 +9,8 @@
 #include <string>
 #include <vector>
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/tether/host_connection_metrics_logger_unittest.cc b/ash/components/tether/host_connection_metrics_logger_unittest.cc
index 34bb783..2187974 100644
--- a/ash/components/tether/host_connection_metrics_logger_unittest.cc
+++ b/ash/components/tether/host_connection_metrics_logger_unittest.cc
@@ -6,11 +6,11 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/fake_active_host.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/ash/components/tether/host_scan_device_prioritizer.h b/ash/components/tether/host_scan_device_prioritizer.h
index 7525eee..72897332 100644
--- a/ash/components/tether/host_scan_device_prioritizer.h
+++ b/ash/components/tether/host_scan_device_prioritizer.h
@@ -5,7 +5,7 @@
 #ifndef ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_H_
 #define ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_H_
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/host_scan_device_prioritizer_impl.h b/ash/components/tether/host_scan_device_prioritizer_impl.h
index b3c39fa..7101547 100644
--- a/ash/components/tether/host_scan_device_prioritizer_impl.h
+++ b/ash/components/tether/host_scan_device_prioritizer_impl.h
@@ -5,8 +5,8 @@
 #ifndef ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_IMPL_H_
 #define ASH_COMPONENTS_TETHER_HOST_SCAN_DEVICE_PRIORITIZER_IMPL_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/host_scan_device_prioritizer.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc b/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc
index e0b72305..a74e93d6 100644
--- a/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc
+++ b/ash/components/tether/host_scan_device_prioritizer_impl_unittest.cc
@@ -6,9 +6,9 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/tether_host_response_recorder.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/tether/host_scan_scheduler_impl.cc b/ash/components/tether/host_scan_scheduler_impl.cc
index c4d3acae..54c4cd1 100644
--- a/ash/components/tether/host_scan_scheduler_impl.cc
+++ b/ash/components/tether/host_scan_scheduler_impl.cc
@@ -6,13 +6,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/host_scanner_impl.cc b/ash/components/tether/host_scanner_impl.cc
index 81fb2a0..e738839 100644
--- a/ash/components/tether/host_scanner_impl.cc
+++ b/ash/components/tether/host_scanner_impl.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/connection_preserver.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/device_status_util.h"
@@ -16,7 +17,6 @@
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_state.h"
 #include "components/session_manager/core/session_manager.h"
 
diff --git a/ash/components/tether/host_scanner_impl.h b/ash/components/tether/host_scanner_impl.h
index 4810e718..0313768 100644
--- a/ash/components/tether/host_scanner_impl.h
+++ b/ash/components/tether/host_scanner_impl.h
@@ -9,6 +9,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/host_scanner.h"
 #include "ash/components/tether/host_scanner_operation.h"
 #include "ash/components/tether/notification_presenter.h"
@@ -20,7 +21,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/time/clock.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/network/network_state_handler.h"
 #include "components/session_manager/core/session_manager_observer.h"
 
diff --git a/ash/components/tether/host_scanner_impl_unittest.cc b/ash/components/tether/host_scanner_impl_unittest.cc
index 3044129..78ae6b6 100644
--- a/ash/components/tether/host_scanner_impl_unittest.cc
+++ b/ash/components/tether/host_scanner_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/fake_connection_preserver.h"
 #include "ash/components/tether/fake_host_scan_cache.h"
@@ -27,7 +28,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/network/network_state_test_helper.h"
 #include "components/session_manager/core/session_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/host_scanner_operation.cc b/ash/components/tether/host_scanner_operation.cc
index 9ff716e5..4f2d143b 100644
--- a/ash/components/tether/host_scanner_operation.cc
+++ b/ash/components/tether/host_scanner_operation.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/connection_preserver.h"
 #include "ash/components/tether/host_scan_device_prioritizer.h"
 #include "ash/components/tether/message_wrapper.h"
@@ -16,7 +17,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/host_scanner_operation.h b/ash/components/tether/host_scanner_operation.h
index 2c41206..890bc0a 100644
--- a/ash/components/tether/host_scanner_operation.h
+++ b/ash/components/tether/host_scanner_operation.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/message_transfer_operation.h"
 // TODO(https://crbug.com/1164001): move to forward declaration
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
@@ -16,7 +17,6 @@
 #include "base/gtest_prod_util.h"
 #include "base/observer_list.h"
 #include "base/time/clock.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/host_scanner_operation_unittest.cc b/ash/components/tether/host_scanner_operation_unittest.cc
index b7ab56b..409478d 100644
--- a/ash/components/tether/host_scanner_operation_unittest.cc
+++ b/ash/components/tether/host_scanner_operation_unittest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/fake_connection_preserver.h"
 #include "ash/components/tether/host_scan_device_prioritizer.h"
 #include "ash/components/tether/message_wrapper.h"
@@ -23,7 +24,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_simple_task_runner.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
diff --git a/ash/components/tether/hotspot_usage_duration_tracker.cc b/ash/components/tether/hotspot_usage_duration_tracker.cc
index c77bc17..d9e93160 100644
--- a/ash/components/tether/hotspot_usage_duration_tracker.cc
+++ b/ash/components/tether/hotspot_usage_duration_tracker.cc
@@ -4,9 +4,9 @@
 
 #include "ash/components/tether/hotspot_usage_duration_tracker.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/keep_alive_operation.cc b/ash/components/tether/keep_alive_operation.cc
index d6f56db2..bf2b89c 100644
--- a/ash/components/tether/keep_alive_operation.cc
+++ b/ash/components/tether/keep_alive_operation.cc
@@ -4,12 +4,12 @@
 
 #include "ash/components/tether/keep_alive_operation.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/proto/tether.pb.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/keep_alive_operation_unittest.cc b/ash/components/tether/keep_alive_operation_unittest.cc
index 798868a..3939c28 100644
--- a/ash/components/tether/keep_alive_operation_unittest.cc
+++ b/ash/components/tether/keep_alive_operation_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/proto_test_util.h"
 #include "ash/components/tether/test_timer_factory.h"
@@ -17,7 +18,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/components/tether/keep_alive_scheduler_unittest.cc b/ash/components/tether/keep_alive_scheduler_unittest.cc
index fdf3f47b..e3a60650 100644
--- a/ash/components/tether/keep_alive_scheduler_unittest.cc
+++ b/ash/components/tether/keep_alive_scheduler_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/fake_active_host.h"
 #include "ash/components/tether/fake_host_scan_cache.h"
@@ -15,7 +16,6 @@
 #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
 #include "base/memory/ptr_util.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/ash/components/tether/message_transfer_operation.cc b/ash/components/tether/message_transfer_operation.cc
index cb1f966..a0d89a1 100644
--- a/ash/components/tether/message_transfer_operation.cc
+++ b/ash/components/tether/message_transfer_operation.cc
@@ -7,11 +7,11 @@
 #include <memory>
 #include <set>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/timer_factory.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/message_transfer_operation_unittest.cc b/ash/components/tether/message_transfer_operation_unittest.cc
index a974aa4..0e9df3e 100644
--- a/ash/components/tether/message_transfer_operation_unittest.cc
+++ b/ash/components/tether/message_transfer_operation_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/message_wrapper.h"
 #include "ash/components/tether/proto_test_util.h"
 #include "ash/components/tether/test_timer_factory.h"
@@ -15,7 +16,6 @@
 #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
 #include "base/memory/ptr_util.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/components/tether/mock_tether_host_response_recorder.h b/ash/components/tether/mock_tether_host_response_recorder.h
index b64114d..452db1d 100644
--- a/ash/components/tether/mock_tether_host_response_recorder.h
+++ b/ash/components/tether/mock_tether_host_response_recorder.h
@@ -7,8 +7,8 @@
 
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/tether_host_response_recorder.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace ash {
diff --git a/ash/components/tether/network_configuration_remover.cc b/ash/components/tether/network_configuration_remover.cc
index 58af15fb..34f8c10 100644
--- a/ash/components/tether/network_configuration_remover.cc
+++ b/ash/components/tether/network_configuration_remover.cc
@@ -4,8 +4,8 @@
 
 #include "ash/components/tether/network_configuration_remover.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
 
 namespace {
diff --git a/ash/components/tether/network_connection_handler_tether_delegate.cc b/ash/components/tether/network_connection_handler_tether_delegate.cc
index 7ca8680..2cd83f9 100644
--- a/ash/components/tether/network_connection_handler_tether_delegate.cc
+++ b/ash/components/tether/network_connection_handler_tether_delegate.cc
@@ -4,13 +4,13 @@
 
 #include "ash/components/tether/network_connection_handler_tether_delegate.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/active_host.h"
 #include "ash/components/tether/tether_connector.h"
 #include "ash/components/tether/tether_disconnector.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/containers/contains.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/network_host_scan_cache.cc b/ash/components/tether/network_host_scan_cache.cc
index 6061ec8..ae2b918 100644
--- a/ash/components/tether/network_host_scan_cache.cc
+++ b/ash/components/tether/network_host_scan_cache.cc
@@ -4,10 +4,10 @@
 
 #include "ash/components/tether/network_host_scan_cache.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/tether_host_response_recorder.h"
 #include "base/containers/contains.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 
diff --git a/ash/components/tether/network_list_sorter.h b/ash/components/tether/network_list_sorter.h
index d105e39..ccd53beb 100644
--- a/ash/components/tether/network_list_sorter.h
+++ b/ash/components/tether/network_list_sorter.h
@@ -5,7 +5,7 @@
 #ifndef ASH_COMPONENTS_TETHER_NETWORK_LIST_SORTER_H_
 #define ASH_COMPONENTS_TETHER_NETWORK_LIST_SORTER_H_
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "chromeos/network/network_state_handler.h"
 
 namespace ash {
diff --git a/ash/components/tether/notification_presenter.h b/ash/components/tether/notification_presenter.h
index 3421862..cf8c86ca 100644
--- a/ash/components/tether/notification_presenter.h
+++ b/ash/components/tether/notification_presenter.h
@@ -5,7 +5,7 @@
 #ifndef ASH_COMPONENTS_TETHER_NOTIFICATION_PRESENTER_H_
 #define ASH_COMPONENTS_TETHER_NOTIFICATION_PRESENTER_H_
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "chromeos/network/network_state.h"
 
 namespace ash {
diff --git a/ash/components/tether/notification_remover_unittest.cc b/ash/components/tether/notification_remover_unittest.cc
index e47742d..54016bf 100644
--- a/ash/components/tether/notification_remover_unittest.cc
+++ b/ash/components/tether/notification_remover_unittest.cc
@@ -6,13 +6,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/fake_active_host.h"
 #include "ash/components/tether/fake_host_scan_cache.h"
 #include "ash/components/tether/fake_notification_presenter.h"
 #include "ash/components/tether/host_scan_test_util.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/network/network_state_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
diff --git a/ash/components/tether/tether_component_impl.cc b/ash/components/tether/tether_component_impl.cc
index c8418bd..b2cd76c 100644
--- a/ash/components/tether/tether_component_impl.cc
+++ b/ash/components/tether/tether_component_impl.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/active_host.h"
 #include "ash/components/tether/asynchronous_shutdown_object_container_impl.h"
 #include "ash/components/tether/crash_recovery_manager_impl.h"
@@ -21,7 +22,6 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 
 namespace ash {
diff --git a/ash/components/tether/tether_connector_impl.cc b/ash/components/tether/tether_connector_impl.cc
index 084971f..96f23ba 100644
--- a/ash/components/tether/tether_connector_impl.cc
+++ b/ash/components/tether/tether_connector_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/components/tether/tether_connector_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/active_host.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/disconnect_tethering_request_sender.h"
@@ -16,7 +17,6 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/metrics/histogram_macros.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/tether_connector_impl_unittest.cc b/ash/components/tether/tether_connector_impl_unittest.cc
index 869d3518..d162f4b 100644
--- a/ash/components/tether/tether_connector_impl_unittest.cc
+++ b/ash/components/tether/tether_connector_impl_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/connect_tethering_operation.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/fake_active_host.h"
@@ -24,8 +26,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/tether_disconnector_impl.cc b/ash/components/tether/tether_disconnector_impl.cc
index 2e002701..c48a311 100644
--- a/ash/components/tether/tether_disconnector_impl.cc
+++ b/ash/components/tether/tether_disconnector_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/components/tether/tether_disconnector_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/active_host.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/disconnect_tethering_request_sender.h"
@@ -12,7 +13,6 @@
 #include "ash/components/tether/wifi_hotspot_disconnector.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/tether_disconnector_impl_unittest.cc b/ash/components/tether/tether_disconnector_impl_unittest.cc
index f942d8bf..8e9e5230 100644
--- a/ash/components/tether/tether_disconnector_impl_unittest.cc
+++ b/ash/components/tether/tether_disconnector_impl_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/tether/device_id_tether_network_guid_map.h"
 #include "ash/components/tether/fake_active_host.h"
 #include "ash/components/tether/fake_disconnect_tethering_request_sender.h"
@@ -14,8 +16,6 @@
 #include "ash/components/tether/fake_wifi_hotspot_disconnector.h"
 #include "ash/components/tether/tether_session_completion_logger.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/ash/components/tether/tether_host_fetcher.cc b/ash/components/tether/tether_host_fetcher.cc
index e98af797..7ead01c 100644
--- a/ash/components/tether/tether_host_fetcher.cc
+++ b/ash/components/tether/tether_host_fetcher.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/tether_host_fetcher.h b/ash/components/tether/tether_host_fetcher.h
index d0de5e6..77a67dc 100644
--- a/ash/components/tether/tether_host_fetcher.h
+++ b/ash/components/tether/tether_host_fetcher.h
@@ -7,9 +7,9 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/callback.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/ash/components/tether/tether_host_fetcher_impl.cc b/ash/components/tether/tether_host_fetcher_impl.cc
index 7b5c659b..229c349 100644
--- a/ash/components/tether/tether_host_fetcher_impl.cc
+++ b/ash/components/tether/tether_host_fetcher_impl.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/remote_device.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/tether_host_fetcher_impl.h b/ash/components/tether/tether_host_fetcher_impl.h
index eba6bc8..b5c07fb 100644
--- a/ash/components/tether/tether_host_fetcher_impl.h
+++ b/ash/components/tether/tether_host_fetcher_impl.h
@@ -7,11 +7,11 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/tether_host_fetcher.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/tether_host_fetcher_impl_unittest.cc b/ash/components/tether/tether_host_fetcher_impl_unittest.cc
index 6490d243..348135a9 100644
--- a/ash/components/tether/tether_host_fetcher_impl_unittest.cc
+++ b/ash/components/tether/tether_host_fetcher_impl_unittest.cc
@@ -7,16 +7,16 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/bind.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/components/tether/tether_host_response_recorder.h b/ash/components/tether/tether_host_response_recorder.h
index 4857bcc..8282e8f1 100644
--- a/ash/components/tether/tether_host_response_recorder.h
+++ b/ash/components/tether/tether_host_response_recorder.h
@@ -8,8 +8,8 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 class PrefService;
 
diff --git a/ash/components/tether/tether_host_response_recorder_unittest.cc b/ash/components/tether/tether_host_response_recorder_unittest.cc
index 44086edc..3a0c71c5 100644
--- a/ash/components/tether/tether_host_response_recorder_unittest.cc
+++ b/ash/components/tether/tether_host_response_recorder_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/components/tether/tether_network_disconnection_handler.cc b/ash/components/tether/tether_network_disconnection_handler.cc
index 2dd23c1..19db361e 100644
--- a/ash/components/tether/tether_network_disconnection_handler.cc
+++ b/ash/components/tether/tether_network_disconnection_handler.cc
@@ -4,6 +4,7 @@
 
 #include "ash/components/tether/tether_network_disconnection_handler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/disconnect_tethering_request_sender.h"
 #include "ash/components/tether/network_configuration_remover.h"
 #include "ash/components/tether/tether_disconnector.h"
@@ -14,7 +15,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
diff --git a/ash/components/tether/top_level_host_scan_cache.cc b/ash/components/tether/top_level_host_scan_cache.cc
index bc405c4..bcf1565 100644
--- a/ash/components/tether/top_level_host_scan_cache.cc
+++ b/ash/components/tether/top_level_host_scan_cache.cc
@@ -6,12 +6,12 @@
 
 #include <algorithm>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/active_host.h"
 #include "ash/components/tether/persistent_host_scan_cache.h"
 #include "ash/components/tether/timer_factory.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/components/tether/wifi_hotspot_connector.cc b/ash/components/tether/wifi_hotspot_connector.cc
index b8e586e..e7e23f781 100644
--- a/ash/components/tether/wifi_hotspot_connector.cc
+++ b/ash/components/tether/wifi_hotspot_connector.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/guid.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_connect.h"
 #include "chromeos/network/network_handler.h"
diff --git a/ash/components/tether/wifi_hotspot_disconnector_impl.cc b/ash/components/tether/wifi_hotspot_disconnector_impl.cc
index a12c0fa..8b27c880 100644
--- a/ash/components/tether/wifi_hotspot_disconnector_impl.cc
+++ b/ash/components/tether/wifi_hotspot_disconnector_impl.cc
@@ -4,11 +4,11 @@
 
 #include "ash/components/tether/wifi_hotspot_disconnector_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/network_configuration_remover.h"
 #include "ash/components/tether/pref_names.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 3da72786..66f2636 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -24,6 +24,11 @@
 
 }  // namespace
 
+// Enables the UI and logic that minimizes the amount of time the device spends
+// at full battery. This preserves battery lifetime.
+const base::Feature kAdaptiveCharging{"AdaptiveCharging",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Adjusts portrait mode split view to avoid the input field in the bottom
 // window being occluded by the virtual keyboard.
 const base::Feature kAdjustSplitViewForVK{"AdjustSplitViewForVK",
@@ -642,7 +647,7 @@
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables the System Web App (SWA) version of file manager.
-const base::Feature kFilesSWA{"FilesSWA", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kFilesSWA{"FilesSWA", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables partitioning of removable disks in file manager.
 const base::Feature kFilesSinglePartitionFormat{
@@ -1009,7 +1014,7 @@
 // If enabled, EULA and ARC Terms of Service screens are skipped and merged
 // into Consolidated Consent Screen.
 const base::Feature kOobeConsolidatedConsent{"OobeConsolidatedConsent",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables or disables the Chrome OS OOBE HID Detection Revamp, which updates
 // the OOBE HID detection screen UI and related infrastructure. See
@@ -1457,7 +1462,7 @@
 // Enables or disables whether to store UMA logs per-user and whether metrics
 // consent is per-user.
 const base::Feature kPerUserMetrics{"PerUserMetricsConsent",
-                                    base::FEATURE_DISABLED_BY_DEFAULT};
+                                    base::FEATURE_ENABLED_BY_DEFAULT};
 ////////////////////////////////////////////////////////////////////////////////
 
 bool AreContextualNudgesEnabled() {
@@ -1478,6 +1483,10 @@
   return base::FeatureList::IsEnabled(kWindowsFollowCursor);
 }
 
+bool IsAdaptiveChargingEnabled() {
+  return base::FeatureList::IsEnabled(kAdaptiveCharging);
+}
+
 bool IsAdjustSplitViewForVKEnabled() {
   return base::FeatureList::IsEnabled(kAdjustSplitViewForVK);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 29461803..f75f9cab 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -18,6 +18,8 @@
 // being rolled out via Finch, add a comment in the .cc file.
 
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kAdaptiveCharging;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kAdjustSplitViewForVK;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kAllowAmbientEQ;
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -564,6 +566,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreDesksTrackpadSwipeImprovementsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool AreImprovedScreenCaptureSettingsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool DoWindowsFollowCursor();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAdaptiveChargingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAdjustSplitViewForVKEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAllowAmbientEQEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsAmbientModeAnimationEnabled();
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.cc b/ash/multi_device_setup/multi_device_notification_presenter.cc
index c846a07b..d3dc1b0 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -19,7 +20,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/chromeos/devicetype_utils.h"
 #include "ui/message_center/message_center.h"
diff --git a/ash/public/cpp/desk_template.cc b/ash/public/cpp/desk_template.cc
index c5b6e93..0c935816 100644
--- a/ash/public/cpp/desk_template.cc
+++ b/ash/public/cpp/desk_template.cc
@@ -25,8 +25,8 @@
 
 // static
 bool DeskTemplate::IsAppTypeSupported(aura::Window* window) {
-  // For now we'll crostini and lacros windows in desk template. We'll also
-  // ignore ARC apps unless the flag is turned on.
+  // For now we'll ignore crostini and lacros windows in desk template. We'll
+  // also ignore ARC apps unless the flag is turned on.
   const AppType app_type =
       static_cast<AppType>(window->GetProperty(aura::client::kAppType));
   switch (app_type) {
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.cc b/ash/quick_pair/repository/fake_fast_pair_repository.cc
index f77dc54..e3b69044 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.cc
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.cc
@@ -76,6 +76,15 @@
 }
 
 // Unimplemented.
+void FakeFastPairRepository::CheckOptInStatus(
+    CheckOptInStatusCallback callback) {}
+
+// Unimplemented.
+void FakeFastPairRepository::UpdateOptInStatus(
+    nearby::fastpair::OptInStatus opt_in_status,
+    UpdateOptInStatusCallback callback) {}
+
+// Unimplemented.
 void FakeFastPairRepository::FetchDeviceImages(scoped_refptr<Device> device) {
   return;
 }
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.h b/ash/quick_pair/repository/fake_fast_pair_repository.h
index 8036598..509116b 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.h
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.h
@@ -62,6 +62,9 @@
   bool EvictDeviceImages(const device::BluetoothDevice* device) override;
   absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
   GetImagesForDevice(const std::string& device_id) override;
+  void CheckOptInStatus(CheckOptInStatusCallback callback) override;
+  void UpdateOptInStatus(nearby::fastpair::OptInStatus opt_in_status,
+                         UpdateOptInStatusCallback callback) override;
 
  private:
   static void SetInstance(FastPairRepository* instance);
diff --git a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc
index 1ce3afdd..e36bfa2a 100644
--- a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc
+++ b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.cc
@@ -16,18 +16,47 @@
 FakeFootprintsFetcher::~FakeFootprintsFetcher() = default;
 
 void FakeFootprintsFetcher::GetUserDevices(UserReadDevicesCallback callback) {
+  if (response_.has_value()) {
+    std::move(callback).Run(std::move(response_));
+    return;
+  }
+
   nearby::fastpair::UserReadDevicesResponse response;
   for (const auto& entry : account_key_to_info_map_) {
     *response.add_fast_pair_info() = entry.second;
   }
+
+  if (add_user_result_)
+    *response.add_fast_pair_info() = opt_in_status_info_;
   std::move(callback).Run(std::move(response));
 }
 
-void FakeFootprintsFetcher::AddUserDevice(nearby::fastpair::FastPairInfo info,
-                                          AddDeviceCallback callback) {
+void FakeFootprintsFetcher::SetGetUserDevicesResponse(
+    absl::optional<nearby::fastpair::UserReadDevicesResponse> response) {
+  response_ = response;
+}
+
+void FakeFootprintsFetcher::SetAddUserFastPairInfoResult(bool add_user_result) {
+  add_user_result_ = add_user_result;
+}
+
+void FakeFootprintsFetcher::AddUserFastPairInfo(
+    nearby::fastpair::FastPairInfo info,
+    AddDeviceCallback callback) {
+  if (info.has_opt_in_status() && add_user_result_) {
+    opt_in_status_info_ = info;
+    std::move(callback).Run(add_user_result_);
+    return;
+  }
+
+  if (info.has_opt_in_status() && !add_user_result_) {
+    std::move(callback).Run(add_user_result_);
+    return;
+  }
+
   account_key_to_info_map_[base::HexEncode(
       base::as_bytes(base::make_span(info.device().account_key())))] = info;
-  std::move(callback).Run(true);
+  std::move(callback).Run(add_user_result_);
 }
 
 void FakeFootprintsFetcher::DeleteUserDevice(const std::string& hex_account_key,
diff --git a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h
index 3bf7614..19baf5b 100644
--- a/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h
+++ b/ash/quick_pair/repository/fast_pair/fake_footprints_fetcher.h
@@ -23,14 +23,22 @@
 
   // FootprintsFetcher::
   void GetUserDevices(UserReadDevicesCallback callback) override;
-  void AddUserDevice(nearby::fastpair::FastPairInfo info,
-                     AddDeviceCallback callback) override;
+  void AddUserFastPairInfo(nearby::fastpair::FastPairInfo info,
+                           AddDeviceCallback callback) override;
   void DeleteUserDevice(const std::string& hex_account_key,
                         DeleteDeviceCallback callback) override;
 
   bool ContainsKey(const std::vector<uint8_t>& account_key);
 
+  void SetGetUserDevicesResponse(
+      absl::optional<nearby::fastpair::UserReadDevicesResponse> response);
+
+  void SetAddUserFastPairInfoResult(bool add_user_result);
+
  private:
+  bool add_user_result_ = true;
+  absl::optional<nearby::fastpair::UserReadDevicesResponse> response_;
+  nearby::fastpair::FastPairInfo opt_in_status_info_;
   base::flat_map<std::string, nearby::fastpair::FastPairInfo>
       account_key_to_info_map_;
 };
diff --git a/ash/quick_pair/repository/fast_pair/footprints_fetcher.h b/ash/quick_pair/repository/fast_pair/footprints_fetcher.h
index 650ef359..baeb22f7 100644
--- a/ash/quick_pair/repository/fast_pair/footprints_fetcher.h
+++ b/ash/quick_pair/repository/fast_pair/footprints_fetcher.h
@@ -34,8 +34,8 @@
   virtual ~FootprintsFetcher() = default;
 
   virtual void GetUserDevices(UserReadDevicesCallback callback) = 0;
-  virtual void AddUserDevice(nearby::fastpair::FastPairInfo info,
-                             AddDeviceCallback callback) = 0;
+  virtual void AddUserFastPairInfo(nearby::fastpair::FastPairInfo info,
+                                   AddDeviceCallback callback) = 0;
   virtual void DeleteUserDevice(const std::string& hex_account_key,
                                 DeleteDeviceCallback callback) = 0;
 };
diff --git a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
index faa0e062..957b60f0 100644
--- a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
+++ b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
@@ -128,8 +128,9 @@
   std::move(callback).Run(devices);
 }
 
-void FootprintsFetcherImpl::AddUserDevice(nearby::fastpair::FastPairInfo info,
-                                          AddDeviceCallback callback) {
+void FootprintsFetcherImpl::AddUserFastPairInfo(
+    nearby::fastpair::FastPairInfo info,
+    AddDeviceCallback callback) {
   auto http_fetcher = CreateHttpFetcher();
   auto* raw_http_fetcher = http_fetcher.get();
   raw_http_fetcher->ExecutePostRequest(
diff --git a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h
index 59635de..261129a 100644
--- a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h
+++ b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h
@@ -27,8 +27,8 @@
   ~FootprintsFetcherImpl() override;
 
   void GetUserDevices(UserReadDevicesCallback callback) override;
-  void AddUserDevice(nearby::fastpair::FastPairInfo info,
-                     AddDeviceCallback callback) override;
+  void AddUserFastPairInfo(nearby::fastpair::FastPairInfo info,
+                           AddDeviceCallback callback) override;
   void DeleteUserDevice(const std::string& hex_account_key,
                         DeleteDeviceCallback callback) override;
 
diff --git a/ash/quick_pair/repository/fast_pair/proto_conversions.cc b/ash/quick_pair/repository/fast_pair/proto_conversions.cc
index bbdaab8..845ad48 100644
--- a/ash/quick_pair/repository/fast_pair/proto_conversions.cc
+++ b/ash/quick_pair/repository/fast_pair/proto_conversions.cc
@@ -97,5 +97,12 @@
   return proto;
 }
 
+nearby::fastpair::FastPairInfo BuildFastPairInfoForOptIn(
+    nearby::fastpair::OptInStatus opt_in_status) {
+  nearby::fastpair::FastPairInfo proto;
+  proto.set_opt_in_status(opt_in_status);
+  return proto;
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/repository/fast_pair/proto_conversions.h b/ash/quick_pair/repository/fast_pair/proto_conversions.h
index 3d813774..b9b422c0 100644
--- a/ash/quick_pair/repository/fast_pair/proto_conversions.h
+++ b/ash/quick_pair/repository/fast_pair/proto_conversions.h
@@ -17,6 +17,9 @@
     const std::vector<uint8_t>& account_key,
     DeviceMetadata* metadata);
 
+nearby::fastpair::FastPairInfo BuildFastPairInfoForOptIn(
+    nearby::fastpair::OptInStatus opt_in_status);
+
 }  // namespace quick_pair
 }  // namespace ash
 
diff --git a/ash/quick_pair/repository/fast_pair_repository.h b/ash/quick_pair/repository/fast_pair_repository.h
index 0d3f9ac..f88fb6d8 100644
--- a/ash/quick_pair/repository/fast_pair_repository.h
+++ b/ash/quick_pair/repository/fast_pair_repository.h
@@ -33,6 +33,9 @@
     base::OnceCallback<void(absl::optional<PairingMetadata>)>;
 using DeviceMetadataCallback = base::OnceCallback<void(DeviceMetadata*, bool)>;
 using ValidModelIdCallback = base::OnceCallback<void(bool)>;
+using CheckOptInStatusCallback =
+    base::OnceCallback<void(nearby::fastpair::OptInStatus)>;
+using UpdateOptInStatusCallback = base::OnceCallback<void(bool)>;
 
 // The entry point for the Repository component in the Quick Pair system,
 // responsible for connecting to back-end services.
@@ -80,6 +83,19 @@
   virtual absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
   GetImagesForDevice(const std::string& device_id) = 0;
 
+  // Fetches the opt in status from Footprints to determine the status for
+  // saving a user's devices to their account, which is synced all across a
+  // user's devices.
+  virtual void CheckOptInStatus(CheckOptInStatusCallback callback) = 0;
+
+  // Updates the opt in status for saving a user's devices to their account,
+  // synced across all of a user's devices, based on |opt_in_status|.
+  // If |opt_in_status| reflects the user being opted out, then all devices
+  // saved to their account are removed. The success or failure of updating
+  // the opt in status is reflected in running |callback|.
+  virtual void UpdateOptInStatus(nearby::fastpair::OptInStatus opt_in_status,
+                                 UpdateOptInStatusCallback callback) = 0;
+
  protected:
   static void SetInstance(FastPairRepository* instance);
 };
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.cc b/ash/quick_pair/repository/fast_pair_repository_impl.cc
index 7d5b1cd..74d4311 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.cc
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.cc
@@ -204,12 +204,12 @@
   DCHECK(device->classic_address());
   GetDeviceMetadata(
       device->metadata_id,
-      base::BindOnce(&FastPairRepositoryImpl::AddToFootprints,
+      base::BindOnce(&FastPairRepositoryImpl::AddDeviceToFootprints,
                      weak_ptr_factory_.GetWeakPtr(), device->metadata_id,
                      device->classic_address().value(), account_key));
 }
 
-void FastPairRepositoryImpl::AddToFootprints(
+void FastPairRepositoryImpl::AddDeviceToFootprints(
     const std::string& hex_model_id,
     const std::string& mac_address,
     const std::vector<uint8_t>& account_key,
@@ -220,13 +220,13 @@
     return;
   }
 
-  footprints_fetcher_->AddUserDevice(
+  footprints_fetcher_->AddUserFastPairInfo(
       BuildFastPairInfo(hex_model_id, account_key, metadata),
-      base::BindOnce(&FastPairRepositoryImpl::OnAddToFootprintsComplete,
+      base::BindOnce(&FastPairRepositoryImpl::OnAddDeviceToFootprintsComplete,
                      weak_ptr_factory_.GetWeakPtr(), mac_address, account_key));
 }
 
-void FastPairRepositoryImpl::OnAddToFootprintsComplete(
+void FastPairRepositoryImpl::OnAddDeviceToFootprintsComplete(
     const std::string& mac_address,
     const std::vector<uint8_t>& account_key,
     bool success) {
@@ -238,6 +238,51 @@
   saved_device_registry_->SaveAccountKey(mac_address, account_key);
 }
 
+void FastPairRepositoryImpl::CheckOptInStatus(
+    CheckOptInStatusCallback callback) {
+  footprints_fetcher_->GetUserDevices(
+      base::BindOnce(&FastPairRepositoryImpl::OnCheckOptInStatus,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FastPairRepositoryImpl::OnCheckOptInStatus(
+    CheckOptInStatusCallback callback,
+    absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices) {
+  QP_LOG(INFO) << __func__;
+  if (!user_devices) {
+    QP_LOG(WARNING)
+        << __func__
+        << ": Missing UserReadDevicesResponse from call to Footprints";
+    std::move(callback).Run(nearby::fastpair::OptInStatus::STATUS_UNKNOWN);
+    return;
+  }
+
+  for (const auto& info : user_devices->fast_pair_info()) {
+    if (info.has_opt_in_status()) {
+      std::move(callback).Run(info.opt_in_status());
+      return;
+    }
+  }
+
+  std::move(callback).Run(nearby::fastpair::OptInStatus::STATUS_UNKNOWN);
+}
+
+void FastPairRepositoryImpl::UpdateOptInStatus(
+    nearby::fastpair::OptInStatus opt_in_status,
+    UpdateOptInStatusCallback callback) {
+  footprints_fetcher_->AddUserFastPairInfo(
+      BuildFastPairInfoForOptIn(opt_in_status),
+      base::BindOnce(&FastPairRepositoryImpl::OnUpdateOptInStatusComplete,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FastPairRepositoryImpl::OnUpdateOptInStatusComplete(
+    UpdateOptInStatusCallback callback,
+    bool success) {
+  QP_LOG(INFO) << __func__ << ": success=" << success;
+  std::move(callback).Run(success);
+}
+
 bool FastPairRepositoryImpl::DeleteAssociatedDevice(
     const device::BluetoothDevice* device) {
   absl::optional<const std::vector<uint8_t>> account_key =
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.h b/ash/quick_pair/repository/fast_pair_repository_impl.h
index 40f48da..6782edf 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.h
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.h
@@ -68,6 +68,9 @@
   bool EvictDeviceImages(const device::BluetoothDevice* device) override;
   absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
   GetImagesForDevice(const std::string& device_id) override;
+  void CheckOptInStatus(CheckOptInStatusCallback callback) override;
+  void UpdateOptInStatus(nearby::fastpair::OptInStatus opt_in_status,
+                         UpdateOptInStatusCallback callback) override;
 
  private:
   void CheckAccountKeysImpl(const AccountKeyFilter& account_key_filter,
@@ -92,15 +95,20 @@
                                 const std::vector<uint8_t> account_key,
                                 DeviceMetadata* device_metadata,
                                 bool has_retryable_error);
-  void AddToFootprints(const std::string& hex_model_id,
-                       const std::string& mac_address,
-                       const std::vector<uint8_t>& account_key,
-                       DeviceMetadata* metadata,
-                       bool has_retryable_error);
-  void OnAddToFootprintsComplete(const std::string& mac_address,
-                                 const std::vector<uint8_t>& account_key,
-                                 bool success);
-  // Fethces the |device_metadata| images to the DeviceImageStore for
+  void AddDeviceToFootprints(const std::string& hex_model_id,
+                             const std::string& mac_address,
+                             const std::vector<uint8_t>& account_key,
+                             DeviceMetadata* metadata,
+                             bool has_retryable_error);
+  void OnAddDeviceToFootprintsComplete(const std::string& mac_address,
+                                       const std::vector<uint8_t>& account_key,
+                                       bool success);
+  void OnCheckOptInStatus(
+      CheckOptInStatusCallback callback,
+      absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices);
+  void OnUpdateOptInStatusComplete(UpdateOptInStatusCallback callback,
+                                   bool success);
+  // Fetches the |device_metadata| images to the DeviceImageStore for
   // |hex_model_id|.
   void CompleteFetchDeviceImages(const std::string& hex_model_id,
                                  DeviceMetadata* device_metadata,
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
index bbc8ab5..ac569d57 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
+++ b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
 #include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
@@ -231,7 +232,7 @@
   DeviceMetadata metadata(device, gfx::Image());
 
   // FakeFootprintsFetcher APIs are actually synchronous.
-  footprints_fetcher_->AddUserDevice(
+  footprints_fetcher_->AddUserFastPairInfo(
       BuildFastPairInfo(kValidModelId, kAccountKey1, &metadata),
       base::DoNothing());
 
@@ -346,5 +347,76 @@
   ASSERT_FALSE(device_id_map_->GetModelIdForDeviceId(kTestDeviceId));
 }
 
+TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_OptedIn) {
+  base::MockCallback<base::OnceCallback<void(bool)>> callback1;
+  EXPECT_CALL(callback1, Run(true)).Times(1);
+  fast_pair_repository_->UpdateOptInStatus(
+      nearby::fastpair::OptInStatus::STATUS_OPTED_IN, callback1.Get());
+  base::RunLoop().RunUntilIdle();
+
+  base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>>
+      callback2;
+  EXPECT_CALL(callback2,
+              Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_OPTED_IN)))
+      .Times(1);
+  fast_pair_repository_->CheckOptInStatus(callback2.Get());
+}
+
+TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_OptedOut) {
+  base::MockCallback<base::OnceCallback<void(bool)>> callback1;
+  EXPECT_CALL(callback1, Run(true)).Times(1);
+  fast_pair_repository_->UpdateOptInStatus(
+      nearby::fastpair::OptInStatus::STATUS_OPTED_OUT, callback1.Get());
+  base::RunLoop().RunUntilIdle();
+
+  base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>>
+      callback2;
+  EXPECT_CALL(callback2,
+              Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_OPTED_OUT)))
+      .Times(1);
+  fast_pair_repository_->CheckOptInStatus(callback2.Get());
+}
+
+TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_StatusUnknown) {
+  base::MockCallback<base::OnceCallback<void(bool)>> callback1;
+  EXPECT_CALL(callback1, Run(true)).Times(1);
+  fast_pair_repository_->UpdateOptInStatus(
+      nearby::fastpair::OptInStatus::STATUS_UNKNOWN, callback1.Get());
+  base::RunLoop().RunUntilIdle();
+
+  base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>>
+      callback2;
+  EXPECT_CALL(callback2,
+              Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_UNKNOWN)))
+      .Times(1);
+  fast_pair_repository_->CheckOptInStatus(callback2.Get());
+}
+
+TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_NoFootprintsResponse) {
+  footprints_fetcher_->SetGetUserDevicesResponse(absl::nullopt);
+  base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>>
+      callback;
+  EXPECT_CALL(callback,
+              Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_UNKNOWN)))
+      .Times(1);
+  fast_pair_repository_->CheckOptInStatus(callback.Get());
+}
+
+TEST_F(FastPairRepositoryImplTest, UpdateOptInStatus_OptedInUpdateFailed) {
+  footprints_fetcher_->SetAddUserFastPairInfoResult(/*add_user_result=*/false);
+  base::MockCallback<base::OnceCallback<void(bool)>> callback1;
+  EXPECT_CALL(callback1, Run(false)).Times(1);
+  fast_pair_repository_->UpdateOptInStatus(
+      nearby::fastpair::OptInStatus::STATUS_OPTED_IN, callback1.Get());
+  base::RunLoop().RunUntilIdle();
+
+  base::MockCallback<base::OnceCallback<void(nearby::fastpair::OptInStatus)>>
+      callback2;
+  EXPECT_CALL(callback2,
+              Run(testing::Eq(nearby::fastpair::OptInStatus::STATUS_UNKNOWN)))
+      .Times(1);
+  fast_pair_repository_->CheckOptInStatus(callback2.Get());
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/repository/mock_fast_pair_repository.h b/ash/quick_pair/repository/mock_fast_pair_repository.h
index 475fe571..825574c 100644
--- a/ash/quick_pair/repository/mock_fast_pair_repository.h
+++ b/ash/quick_pair/repository/mock_fast_pair_repository.h
@@ -55,6 +55,15 @@
               GetImagesForDevice,
               (const std::string& device_id),
               (override));
+  MOCK_METHOD(void,
+              CheckOptInStatus,
+              (CheckOptInStatusCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              UpdateOptInStatus,
+              (nearby::fastpair::OptInStatus opt_in_status,
+               UpdateOptInStatusCallback callback),
+              (override));
 };
 
 }  // namespace quick_pair
diff --git a/ash/resources/BUILD.gn b/ash/resources/BUILD.gn
index 3ccf85d0..6bb770f 100644
--- a/ash/resources/BUILD.gn
+++ b/ash/resources/BUILD.gn
@@ -27,10 +27,10 @@
   ]
 
   deps = [
+    "//ash/components/multidevice/mojom:mojom_js",
     "//ash/services/cellular_setup/public/mojom:mojom_js",
     "//ash/services/device_sync/public/mojom:mojom_js",
     "//ash/services/multidevice_setup/public/mojom:mojom_js",
-    "//chromeos/components/multidevice/mojom:mojom_js",
   ]
 }
 
diff --git a/ash/resources/multidevice_resources.grdp b/ash/resources/multidevice_resources.grdp
index 5ff8ffb..4d4051e 100644
--- a/ash/resources/multidevice_resources.grdp
+++ b/ash/resources/multidevice_resources.grdp
@@ -2,13 +2,13 @@
 <grit-part>
   <!-- Mojo JS files. -->
   <include name="IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_HTML"
-      file="${mojom_root}/chromeos/components/multidevice/mojom/multidevice_types.mojom.html"
-      resource_path="mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom.html"
+      file="${mojom_root}/ash/components/multidevice/mojom/multidevice_types.mojom.html"
+      resource_path="mojo/ash/components/multidevice/mojom/multidevice_types.mojom.html"
       use_base_dir="false"
       type="BINDATA" />
   <include name="IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_LITE_JS"
-      file="${mojom_root}/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js"
-      resource_path="mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js"
+      file="${mojom_root}/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js"
+      resource_path="mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js"
       use_base_dir="false"
       type="BINDATA" />
   <include name="IDR_MULTIDEVICE_DEVICE_SYNC_MOJOM_HTML"
diff --git a/ash/services/device_sync/BUILD.gn b/ash/services/device_sync/BUILD.gn
index 798f51b..0a59463c 100644
--- a/ash/services/device_sync/BUILD.gn
+++ b/ash/services/device_sync/BUILD.gn
@@ -148,21 +148,21 @@
 
   public_deps = [
     ":feature_status_change",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/services/device_sync/public/mojom",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//components/signin/public/identity_manager",
     "//services/network/public/cpp",
   ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/services/device_sync/proto:util",
     "//ash/services/device_sync/public/cpp",
     "//ash/services/device_sync/public/mojom",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/network",
     "//components/gcm_driver",
     "//components/prefs",
@@ -257,8 +257,8 @@
 
   deps = [
     ":device_sync",
+    "//ash/components/multidevice:stub_multidevice_util",
     "//base",
-    "//chromeos/components/multidevice:stub_multidevice_util",
   ]
 }
 
@@ -305,6 +305,8 @@
     ":device_sync",
     ":feature_status_change",
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/constants",
     "//ash/services/device_sync/proto:test_support",
     "//ash/services/device_sync/proto:util",
@@ -315,8 +317,6 @@
     "//ash/services/device_sync/public/mojom:unit_tests",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//chromeos/dbus:test_support",
     "//chromeos/network",
     "//chromeos/network:test_support",
diff --git a/ash/services/device_sync/DEPS b/ash/services/device_sync/DEPS
index 80f94885..da5f42b 100644
--- a/ash/services/device_sync/DEPS
+++ b/ash/services/device_sync/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/components/multidevice",
   "+ash/constants",
   "+components/gcm_driver",
   "+components/signin/public",
diff --git a/ash/services/device_sync/OWNERS b/ash/services/device_sync/OWNERS
index 4b6c4d12..6d67d25f 100644
--- a/ash/services/device_sync/OWNERS
+++ b/ash/services/device_sync/OWNERS
@@ -1,4 +1,4 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
 
 per-file *_type_converter*.*=set noparent
 per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS
diff --git a/ash/services/device_sync/cryptauth_api_call_flow.cc b/ash/services/device_sync/cryptauth_api_call_flow.cc
index 98f02f4..4eca841 100644
--- a/ash/services/device_sync/cryptauth_api_call_flow.cc
+++ b/ash/services/device_sync/cryptauth_api_call_flow.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/device_sync/cryptauth_api_call_flow.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/strings/string_number_conversions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "net/base/url_util.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/ash/services/device_sync/cryptauth_client_impl.cc b/ash/services/device_sync/cryptauth_client_impl.cc
index e128518..6232b0a 100644
--- a/ash/services/device_sync/cryptauth_client_impl.cc
+++ b/ash/services/device_sync/cryptauth_client_impl.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
 #include "ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h"
@@ -13,7 +14,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/ash/services/device_sync/cryptauth_device.cc b/ash/services/device_sync/cryptauth_device.cc
index 7731259..adf2235 100644
--- a/ash/services/device_sync/cryptauth_device.cc
+++ b/ash/services/device_sync/cryptauth_device.cc
@@ -6,12 +6,12 @@
 
 #include <sstream>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "ash/services/device_sync/value_string_encoding.h"
 #include "base/i18n/time_formatting.h"
 #include "base/json/values_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_device.h b/ash/services/device_sync/cryptauth_device.h
index 302d3513..bbb9f30 100644
--- a/ash/services/device_sync/cryptauth_device.h
+++ b/ash/services/device_sync/cryptauth_device.h
@@ -9,11 +9,11 @@
 #include <ostream>
 #include <string>
 
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc b/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc
index 1fe71246..f67773b 100644
--- a/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc
+++ b/ash/services/device_sync/cryptauth_device_activity_getter_impl.cc
@@ -7,6 +7,9 @@
 #include <array>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_key_bundle.h"
@@ -17,9 +20,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_device_manager_impl.cc b/ash/services/device_sync/cryptauth_device_manager_impl.cc
index 8077dd9..165a012 100644
--- a/ash/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/ash/services/device_sync/cryptauth_device_manager_impl.cc
@@ -10,6 +10,8 @@
 #include <stdexcept>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/pref_names.h"
 #include "ash/services/device_sync/proto/enum_util.h"
@@ -21,8 +23,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc
index 8f3c3ea..6fa5229 100644
--- a/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/fake_cryptauth_gcm_manager.h"
 #include "ash/services/device_sync/mock_cryptauth_client.h"
 #include "ash/services/device_sync/mock_sync_scheduler.h"
@@ -24,7 +25,6 @@
 #include "base/test/gmock_move_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prefs/testing_pref_service.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
diff --git a/ash/services/device_sync/cryptauth_device_notifier_impl.cc b/ash/services/device_sync/cryptauth_device_notifier_impl.cc
index 76f58df9..26382e4 100644
--- a/ash/services/device_sync/cryptauth_device_notifier_impl.cc
+++ b/ash/services/device_sync/cryptauth_device_notifier_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_key_bundle.h"
@@ -13,7 +14,6 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_device_registry_impl.cc b/ash/services/device_sync/cryptauth_device_registry_impl.cc
index 1cdbecb..6b35072b 100644
--- a/ash/services/device_sync/cryptauth_device_registry_impl.cc
+++ b/ash/services/device_sync/cryptauth_device_registry_impl.cc
@@ -7,11 +7,11 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/cryptauth_device.h"
 #include "ash/services/device_sync/pref_names.h"
 #include "ash/services/device_sync/value_string_encoding.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc b/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc
index 3a180be..a808fbe4 100644
--- a/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc
@@ -9,6 +9,8 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/cryptauth_device_registry.h"
 #include "ash/services/device_sync/pref_names.h"
 #include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
@@ -17,8 +19,6 @@
 #include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/device_sync/cryptauth_device_syncer_impl.cc b/ash/services/device_sync/cryptauth_device_syncer_impl.cc
index 6e06649..b3586da98a 100644
--- a/ash/services/device_sync/cryptauth_device_syncer_impl.cc
+++ b/ash/services/device_sync/cryptauth_device_syncer_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_ecies_encryptor_impl.h"
@@ -23,7 +24,6 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_device_unittest.cc b/ash/services/device_sync/cryptauth_device_unittest.cc
index 1763b082..db50f10 100644
--- a/ash/services/device_sync/cryptauth_device_unittest.cc
+++ b/ash/services/device_sync/cryptauth_device_unittest.cc
@@ -6,11 +6,11 @@
 
 #include <map>
 
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
 #include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc b/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc
index 3f901ce..9c1e39b 100644
--- a/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc
+++ b/ash/services/device_sync/cryptauth_ecies_encryptor_impl.cc
@@ -6,12 +6,12 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/device_sync/value_string_encoding.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc b/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc
index 98e463f0..7c6ea35 100644
--- a/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_ecies_encryptor_impl_unittest.cc
@@ -6,13 +6,13 @@
 
 #include <string>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/device_sync/value_string_encoding.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
diff --git a/ash/services/device_sync/cryptauth_enroller_factory_impl.cc b/ash/services/device_sync/cryptauth_enroller_factory_impl.cc
index a94354e..fed3df94 100644
--- a/ash/services/device_sync/cryptauth_enroller_factory_impl.cc
+++ b/ash/services/device_sync/cryptauth_enroller_factory_impl.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/device_sync/cryptauth_enroller_impl.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_enroller_impl.cc b/ash/services/device_sync/cryptauth_enroller_impl.cc
index 066d9476..f1f8c70 100644
--- a/ash/services/device_sync/cryptauth_enroller_impl.cc
+++ b/ash/services/device_sync/cryptauth_enroller_impl.cc
@@ -6,11 +6,11 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/device_sync/cryptauth_client_impl.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "crypto/sha2.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc b/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc
index 2781246..4f79738 100644
--- a/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_enroller_impl_unittest.cc
@@ -6,11 +6,11 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
 #include "ash/services/device_sync/mock_cryptauth_client.h"
 #include "ash/services/device_sync/network_request_error.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
diff --git a/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc b/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc
index 485d558..8721ac3 100644
--- a/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc
+++ b/ash/services/device_sync/cryptauth_enrollment_manager_impl.cc
@@ -8,6 +8,8 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/device_sync/cryptauth_enroller.h"
 #include "ash/services/device_sync/pref_names.h"
 #include "ash/services/device_sync/proto/enum_util.h"
@@ -18,8 +20,6 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc b/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc
index eeed04e..fcbfd2f0 100644
--- a/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_enrollment_manager_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
 #include "ash/services/device_sync/cryptauth_enroller.h"
 #include "ash/services/device_sync/fake_cryptauth_gcm_manager.h"
 #include "ash/services/device_sync/mock_sync_scheduler.h"
@@ -18,7 +19,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/device_sync/cryptauth_feature_status_getter.h b/ash/services/device_sync/cryptauth_feature_status_getter.h
index af4b86e..622a464 100644
--- a/ash/services/device_sync/cryptauth_feature_status_getter.h
+++ b/ash/services/device_sync/cryptauth_feature_status_getter.h
@@ -8,13 +8,13 @@
 #include <map>
 #include <string>
 
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/cryptauth_device_sync_result.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace cryptauthv2 {
 class RequestContext;
diff --git a/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc b/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc
index b07956b..427a9e84 100644
--- a/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc
+++ b/ash/services/device_sync/cryptauth_feature_status_getter_impl.cc
@@ -7,6 +7,9 @@
 #include <array>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_feature_type.h"
@@ -15,9 +18,6 @@
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc b/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
index 0c15b52..44428f0 100644
--- a/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
@@ -8,6 +8,8 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_device.h"
 #include "ash/services/device_sync/cryptauth_device_sync_result.h"
@@ -23,8 +25,6 @@
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/device_sync/cryptauth_feature_status_setter.h b/ash/services/device_sync/cryptauth_feature_status_setter.h
index 1683094..aa617983 100644
--- a/ash/services/device_sync/cryptauth_feature_status_setter.h
+++ b/ash/services/device_sync/cryptauth_feature_status_setter.h
@@ -7,10 +7,10 @@
 
 #include <string>
 
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/network_request_error.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/software_feature.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc b/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc
index b3659f1a..bbe3888d 100644
--- a/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc
+++ b/ash/services/device_sync/cryptauth_feature_status_setter_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_feature_type.h"
@@ -15,7 +16,6 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_feature_status_setter_impl.h b/ash/services/device_sync/cryptauth_feature_status_setter_impl.h
index 663a53f6..738da3d 100644
--- a/ash/services/device_sync/cryptauth_feature_status_setter_impl.h
+++ b/ash/services/device_sync/cryptauth_feature_status_setter_impl.h
@@ -9,6 +9,7 @@
 #include <ostream>
 #include <string>
 
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/cryptauth_feature_status_setter.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/network_request_error.h"
@@ -17,7 +18,6 @@
 #include "base/containers/queue.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_feature_type.h b/ash/services/device_sync/cryptauth_feature_type.h
index 5a82642..c452009 100644
--- a/ash/services/device_sync/cryptauth_feature_type.h
+++ b/ash/services/device_sync/cryptauth_feature_type.h
@@ -8,8 +8,8 @@
 #include <ostream>
 #include <string>
 
+#include "ash/components/multidevice/software_feature.h"
 #include "base/containers/flat_set.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_gcm_manager_impl.cc b/ash/services/device_sync/cryptauth_gcm_manager_impl.cc
index 7c29eef..6fb5910 100644
--- a/ash/services/device_sync/cryptauth_gcm_manager_impl.cc
+++ b/ash/services/device_sync/cryptauth_gcm_manager_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/device_sync/cryptauth_gcm_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/cryptauth_feature_type.h"
 #include "ash/services/device_sync/cryptauth_key_bundle.h"
 #include "ash/services/device_sync/pref_names.h"
@@ -14,7 +15,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/gcm_driver/gcm_driver.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc b/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc
index 02797507..b8d9f91 100644
--- a/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc
+++ b/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_ecies_encryptor_impl.h"
@@ -14,7 +15,6 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "crypto/sha2.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_key_bundle.cc b/ash/services/device_sync/cryptauth_key_bundle.cc
index eaf20ed..d82ac6c 100644
--- a/ash/services/device_sync/cryptauth_key_bundle.cc
+++ b/ash/services/device_sync/cryptauth_key_bundle.cc
@@ -4,12 +4,12 @@
 
 #include "ash/services/device_sync/cryptauth_key_bundle.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/cryptauth_enrollment_constants.h"
 #include "ash/services/device_sync/value_string_encoding.h"
 #include "base/containers/contains.h"
 #include "base/no_destructor.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_key_creator_impl.cc b/ash/services/device_sync/cryptauth_key_creator_impl.cc
index d38f3ae..8fd69f8d 100644
--- a/ash/services/device_sync/cryptauth_key_creator_impl.cc
+++ b/ash/services/device_sync/cryptauth_key_creator_impl.cc
@@ -4,12 +4,12 @@
 
 #include "ash/services/device_sync/cryptauth_key_creator_impl.h"
 
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/device_sync/cryptauth_enrollment_constants.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "crypto/hkdf.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc b/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc
index 9572705..ef77593d 100644
--- a/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc
+++ b/ash/services/device_sync/cryptauth_key_creator_impl_unittest.cc
@@ -8,6 +8,8 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/device_sync/cryptauth_enrollment_constants.h"
 #include "ash/services/device_sync/cryptauth_key.h"
 #include "ash/services/device_sync/cryptauth_key_bundle.h"
@@ -15,8 +17,6 @@
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/containers/flat_map.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "crypto/hkdf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc b/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc
index 8f9bdb75..ffcfca6 100644
--- a/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc
+++ b/ash/services/device_sync/cryptauth_key_proof_computer_impl.cc
@@ -6,12 +6,12 @@
 
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/cryptauth_key.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/containers/span.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "crypto/ec_private_key.h"
 #include "crypto/ec_signature_creator.h"
 #include "crypto/hkdf.h"
diff --git a/ash/services/device_sync/cryptauth_key_registry_impl.cc b/ash/services/device_sync/cryptauth_key_registry_impl.cc
index 6ed9706..019dea06 100644
--- a/ash/services/device_sync/cryptauth_key_registry_impl.cc
+++ b/ash/services/device_sync/cryptauth_key_registry_impl.cc
@@ -4,9 +4,9 @@
 
 #include "ash/services/device_sync/cryptauth_key_registry_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/pref_names.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc b/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc
index 6c26f22..b4c4c715 100644
--- a/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc
+++ b/ash/services/device_sync/cryptauth_metadata_syncer_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_ecies_encryptor_impl.h"
@@ -18,7 +19,6 @@
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/device_sync/cryptauth_scheduler_impl.cc b/ash/services/device_sync/cryptauth_scheduler_impl.cc
index 80b0000c..6dbe3e4 100644
--- a/ash/services/device_sync/cryptauth_scheduler_impl.cc
+++ b/ash/services/device_sync/cryptauth_scheduler_impl.cc
@@ -7,12 +7,12 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/pref_names.h"
 #include "ash/services/device_sync/proto/cryptauth_logging.h"
 #include "ash/services/device_sync/value_string_encoding.h"
 #include "base/base64.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc b/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc
index 138854f..5efedf7 100644
--- a/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc
+++ b/ash/services/device_sync/cryptauth_v2_device_manager_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_device_syncer_impl.h"
 #include "ash/services/device_sync/cryptauth_key_registry.h"
@@ -13,7 +14,6 @@
 #include "ash/services/device_sync/synced_bluetooth_address_tracker_impl.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc b/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
index bac87a0..78734f30 100644
--- a/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
+++ b/ash/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
@@ -4,14 +4,14 @@
 
 #include "ash/services/device_sync/cryptauth_v2_device_sync_test_devices.h"
 
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/cryptauth_device.h"
 #include "ash/services/device_sync/fake_ecies_encryption.h"
 #include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "base/check_op.h"
 #include "base/no_destructor.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_v2_enroller_impl.cc b/ash/services/device_sync/cryptauth_v2_enroller_impl.cc
index 6020e02..f003a38 100644
--- a/ash/services/device_sync/cryptauth_v2_enroller_impl.cc
+++ b/ash/services/device_sync/cryptauth_v2_enroller_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_client.h"
 #include "ash/services/device_sync/cryptauth_enrollment_constants.h"
@@ -19,7 +20,6 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc b/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
index 7204437..a97402c 100644
--- a/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
+++ b/ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/device_sync/cryptauth_v2_enrollment_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/cryptauth_enrollment_constants.h"
 #include "ash/services/device_sync/cryptauth_key_registry.h"
 #include "ash/services/device_sync/cryptauth_task_metrics_logger.h"
@@ -18,7 +19,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/clock.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/device_sync/device_sync_impl.cc b/ash/services/device_sync/device_sync_impl.cc
index 7658896b..ae019da 100644
--- a/ash/services/device_sync/device_sync_impl.cc
+++ b/ash/services/device_sync/device_sync_impl.cc
@@ -4,6 +4,8 @@
 
 #include "ash/services/device_sync/device_sync_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/cryptauth_client_impl.h"
 #include "ash/services/device_sync/cryptauth_device_activity_getter_impl.h"
@@ -37,8 +39,6 @@
 #include "base/time/default_clock.h"
 #include "base/timer/timer.h"
 #include "base/unguessable_token.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/consent_level.h"
diff --git a/ash/services/device_sync/device_sync_service_unittest.cc b/ash/services/device_sync/device_sync_service_unittest.cc
index 8299673..5245a85 100644
--- a/ash/services/device_sync/device_sync_service_unittest.cc
+++ b/ash/services/device_sync/device_sync_service_unittest.cc
@@ -7,6 +7,8 @@
 #include <tuple>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/cryptauth_device_manager_impl.h"
 #include "ash/services/device_sync/cryptauth_device_registry_impl.h"
@@ -50,8 +52,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/network/network_handler_test_helper.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
diff --git a/ash/services/device_sync/fake_cryptauth_feature_status_setter.h b/ash/services/device_sync/fake_cryptauth_feature_status_setter.h
index 6a9fc22..5a013db 100644
--- a/ash/services/device_sync/fake_cryptauth_feature_status_setter.h
+++ b/ash/services/device_sync/fake_cryptauth_feature_status_setter.h
@@ -9,12 +9,12 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/cryptauth_feature_status_setter.h"
 #include "ash/services/device_sync/cryptauth_feature_status_setter_impl.h"
 #include "ash/services/device_sync/network_request_error.h"
 #include "base/callback.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/software_feature.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/fake_cryptauth_v2_enroller.cc b/ash/services/device_sync/fake_cryptauth_v2_enroller.cc
index 5109ff7c..6b366943 100644
--- a/ash/services/device_sync/fake_cryptauth_v2_enroller.cc
+++ b/ash/services/device_sync/fake_cryptauth_v2_enroller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/services/device_sync/fake_cryptauth_v2_enroller.h"
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/fake_device_sync.cc b/ash/services/device_sync/fake_device_sync.cc
index c0e088f6..2b1d4ba 100644
--- a/ash/services/device_sync/fake_device_sync.cc
+++ b/ash/services/device_sync/fake_device_sync.cc
@@ -6,8 +6,8 @@
 
 #include "ash/services/device_sync/fake_device_sync.h"
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/fake_remote_device_v2_loader.h b/ash/services/device_sync/fake_remote_device_v2_loader.h
index 2e405d7..e6af4a2f 100644
--- a/ash/services/device_sync/fake_remote_device_v2_loader.h
+++ b/ash/services/device_sync/fake_remote_device_v2_loader.h
@@ -9,10 +9,10 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device.h"
 #include "ash/services/device_sync/cryptauth_device_registry.h"
 #include "ash/services/device_sync/remote_device_v2_loader.h"
 #include "ash/services/device_sync/remote_device_v2_loader_impl.h"
-#include "chromeos/components/multidevice/remote_device.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/fake_software_feature_manager.h b/ash/services/device_sync/fake_software_feature_manager.h
index ba096ea..97240c94 100644
--- a/ash/services/device_sync/fake_software_feature_manager.h
+++ b/ash/services/device_sync/fake_software_feature_manager.h
@@ -9,11 +9,11 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/network_request_error.h"
 #include "ash/services/device_sync/software_feature_manager.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/software_feature.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/public/cpp/BUILD.gn b/ash/services/device_sync/public/cpp/BUILD.gn
index 2cb3508..857cdfe9 100644
--- a/ash/services/device_sync/public/cpp/BUILD.gn
+++ b/ash/services/device_sync/public/cpp/BUILD.gn
@@ -20,12 +20,12 @@
   ]
 
   public_deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/services/device_sync:feature_status_change",
     "//ash/services/device_sync/public/mojom",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
   ]
 
   deps = [ "//mojo/public/cpp/bindings" ]
@@ -38,12 +38,12 @@
   ]
 
   public_deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/services/device_sync:feature_status_change",
     "//ash/services/device_sync/public/mojom",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
   ]
 
   deps = [
@@ -78,12 +78,12 @@
     ":cpp",
     ":prefs",
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/services/device_sync",
     "//ash/services/device_sync:test_support",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//components/gcm_driver:test_support",
     "//components/prefs:test_support",
     "//components/signin/public/identity_manager:test_support",
diff --git a/ash/services/device_sync/public/cpp/device_sync_client.cc b/ash/services/device_sync/public/cpp/device_sync_client.cc
index 1874444f..57ea220 100644
--- a/ash/services/device_sync/public/cpp/device_sync_client.cc
+++ b/ash/services/device_sync/public/cpp/device_sync_client.cc
@@ -4,7 +4,7 @@
 
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/public/cpp/device_sync_client.h b/ash/services/device_sync/public/cpp/device_sync_client.h
index c6c7760..0274d6fa 100644
--- a/ash/services/device_sync/public/cpp/device_sync_client.h
+++ b/ash/services/device_sync/public/cpp/device_sync_client.h
@@ -9,14 +9,14 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "ash/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/device_sync/public/cpp/device_sync_client_impl.cc b/ash/services/device_sync/public/cpp/device_sync_client_impl.cc
index f1e2d85b..ece8d9a 100644
--- a/ash/services/device_sync/public/cpp/device_sync_client_impl.cc
+++ b/ash/services/device_sync/public/cpp/device_sync_client_impl.cc
@@ -8,14 +8,14 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/expiring_remote_device_cache.h"
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/expiring_remote_device_cache.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/public/cpp/device_sync_client_impl.h b/ash/services/device_sync/public/cpp/device_sync_client_impl.h
index dd1c58a..e17ce0e 100644
--- a/ash/services/device_sync/public/cpp/device_sync_client_impl.h
+++ b/ash/services/device_sync/public/cpp/device_sync_client_impl.h
@@ -9,6 +9,8 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
@@ -16,8 +18,6 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc b/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
index 2af5180..eb3f0b53 100644
--- a/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
+++ b/ash/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
@@ -10,6 +10,8 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/device_sync_impl.h"
 #include "ash/services/device_sync/fake_device_sync.h"
 #include "ash/services/device_sync/feature_status_change.h"
@@ -26,8 +28,6 @@
 #include "base/test/null_task_runner.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_simple_task_runner.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
diff --git a/ash/services/device_sync/public/cpp/fake_device_sync_client.cc b/ash/services/device_sync/public/cpp/fake_device_sync_client.cc
index dcef4a3..ec796c1 100644
--- a/ash/services/device_sync/public/cpp/fake_device_sync_client.cc
+++ b/ash/services/device_sync/public/cpp/fake_device_sync_client.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device_cache.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/public/cpp/fake_device_sync_client.h b/ash/services/device_sync/public/cpp/fake_device_sync_client.h
index d8b2396..abfc085 100644
--- a/ash/services/device_sync/public/cpp/fake_device_sync_client.h
+++ b/ash/services/device_sync/public/cpp/fake_device_sync_client.h
@@ -9,14 +9,14 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "base/callback.h"
 #include "base/containers/circular_deque.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/public/mojom/BUILD.gn b/ash/services/device_sync/public/mojom/BUILD.gn
index 826abb02..adbc7ec 100644
--- a/ash/services/device_sync/public/mojom/BUILD.gn
+++ b/ash/services/device_sync/public/mojom/BUILD.gn
@@ -11,7 +11,7 @@
   sources = [ "device_sync.mojom" ]
 
   public_deps = [
-    "//chromeos/components/multidevice/mojom",
+    "//ash/components/multidevice/mojom",
     "//mojo/public/mojom/base",
   ]
 
diff --git a/ash/services/device_sync/public/mojom/device_sync.mojom b/ash/services/device_sync/public/mojom/device_sync.mojom
index 2719a50..366d8ab 100644
--- a/ash/services/device_sync/public/mojom/device_sync.mojom
+++ b/ash/services/device_sync/public/mojom/device_sync.mojom
@@ -4,7 +4,7 @@
 
 module chromeos.device_sync.mojom;
 
-import "chromeos/components/multidevice/mojom/multidevice_types.mojom";
+import "ash/components/multidevice/mojom/multidevice_types.mojom";
 import "mojo/public/mojom/base/time.mojom";
 
 // Denotes the outcome of an HTTP request, where results like kBadRequest map
diff --git a/ash/services/device_sync/remote_device_loader.cc b/ash/services/device_sync/remote_device_loader.cc
index 4a6d498c..00a2c8ea 100644
--- a/ash/services/device_sync/remote_device_loader.cc
+++ b/ash/services/device_sync/remote_device_loader.cc
@@ -7,14 +7,14 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
-#include "chromeos/components/multidevice/software_feature.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/remote_device_loader.h b/ash/services/device_sync/remote_device_loader.h
index 2b531f5..1e56b2b7 100644
--- a/ash/services/device_sync/remote_device_loader.h
+++ b/ash/services/device_sync/remote_device_loader.h
@@ -8,10 +8,10 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/remote_device.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/remote_device.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/remote_device_loader_unittest.cc b/ash/services/device_sync/remote_device_loader_unittest.cc
index dc45c81..2a16873 100644
--- a/ash/services/device_sync/remote_device_loader_unittest.cc
+++ b/ash/services/device_sync/remote_device_loader_unittest.cc
@@ -9,9 +9,9 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
 #include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/device_sync/remote_device_provider.h b/ash/services/device_sync/remote_device_provider.h
index eba0aa69..b1b1961 100644
--- a/ash/services/device_sync/remote_device_provider.h
+++ b/ash/services/device_sync/remote_device_provider.h
@@ -5,8 +5,8 @@
 #ifndef ASH_SERVICES_DEVICE_SYNC_REMOTE_DEVICE_PROVIDER_H_
 #define ASH_SERVICES_DEVICE_SYNC_REMOTE_DEVICE_PROVIDER_H_
 
+#include "ash/components/multidevice/remote_device.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/remote_device_provider_impl.cc b/ash/services/device_sync/remote_device_provider_impl.cc
index 078a6cf0..3936337 100644
--- a/ash/services/device_sync/remote_device_provider_impl.cc
+++ b/ash/services/device_sync/remote_device_provider_impl.cc
@@ -6,6 +6,8 @@
 
 #include <algorithm>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/remote_device_loader.h"
 #include "ash/services/device_sync/remote_device_v2_loader_impl.h"
@@ -13,8 +15,6 @@
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/remote_device_provider_impl_unittest.cc b/ash/services/device_sync/remote_device_provider_impl_unittest.cc
index 9b03028..c684eae 100644
--- a/ash/services/device_sync/remote_device_provider_impl_unittest.cc
+++ b/ash/services/device_sync/remote_device_provider_impl_unittest.cc
@@ -7,6 +7,10 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/cryptauth_device.h"
 #include "ash/services/device_sync/cryptauth_device_manager.h"
@@ -22,10 +26,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 #include "base/test/scoped_feature_list.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/remote_device_v2_loader.h b/ash/services/device_sync/remote_device_v2_loader.h
index 9c18209..ec3aef2 100644
--- a/ash/services/device_sync/remote_device_v2_loader.h
+++ b/ash/services/device_sync/remote_device_v2_loader.h
@@ -7,9 +7,9 @@
 
 #include <string>
 
+#include "ash/components/multidevice/remote_device.h"
 #include "ash/services/device_sync/cryptauth_device_registry.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/remote_device.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/remote_device_v2_loader_impl.cc b/ash/services/device_sync/remote_device_v2_loader_impl.cc
index 0bd9d77..7ee067c 100644
--- a/ash/services/device_sync/remote_device_v2_loader_impl.cc
+++ b/ash/services/device_sync/remote_device_v2_loader_impl.cc
@@ -6,13 +6,13 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
 #include "ash/services/device_sync/cryptauth_task_metrics_logger.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/remote_device_v2_loader_impl.h b/ash/services/device_sync/remote_device_v2_loader_impl.h
index 5141bab7..e9370f7 100644
--- a/ash/services/device_sync/remote_device_v2_loader_impl.h
+++ b/ash/services/device_sync/remote_device_v2_loader_impl.h
@@ -8,12 +8,12 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/remote_device.h"
 #include "ash/services/device_sync/cryptauth_device.h"
 #include "ash/services/device_sync/cryptauth_device_registry.h"
 #include "ash/services/device_sync/remote_device_v2_loader.h"
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
-#include "chromeos/components/multidevice/remote_device.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc b/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc
index a4a2cae..93f1e3e4 100644
--- a/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc
+++ b/ash/services/device_sync/remote_device_v2_loader_impl_unittest.cc
@@ -7,13 +7,13 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/remote_device.h"
 #include "ash/services/device_sync/cryptauth_device.h"
 #include "ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
 #include "ash/services/device_sync/proto/cryptauth_v2_test_util.h"
 #include "ash/services/device_sync/remote_device_v2_loader_impl.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/components/multidevice/remote_device.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/ash/services/device_sync/software_feature_manager.h b/ash/services/device_sync/software_feature_manager.h
index 2121842..07707c4 100644
--- a/ash/services/device_sync/software_feature_manager.h
+++ b/ash/services/device_sync/software_feature_manager.h
@@ -7,10 +7,10 @@
 
 #include <string>
 
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/network_request_error.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/software_feature.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/software_feature_manager_impl.h b/ash/services/device_sync/software_feature_manager_impl.h
index ebe7653..f724801b 100644
--- a/ash/services/device_sync/software_feature_manager_impl.h
+++ b/ash/services/device_sync/software_feature_manager_impl.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/network_request_error.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
@@ -17,7 +18,6 @@
 #include "base/callback_forward.h"
 #include "base/containers/queue.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/software_feature.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/software_feature_manager_impl_unittest.cc b/ash/services/device_sync/software_feature_manager_impl_unittest.cc
index 33d9a887..9afc3430 100644
--- a/ash/services/device_sync/software_feature_manager_impl_unittest.cc
+++ b/ash/services/device_sync/software_feature_manager_impl_unittest.cc
@@ -4,14 +4,14 @@
 
 #include "ash/services/device_sync/software_feature_manager_impl.h"
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/fake_cryptauth_feature_status_setter.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/mock_cryptauth_client.h"
 #include "ash/services/device_sync/proto/enum_util.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/device_sync/stub_device_sync.cc b/ash/services/device_sync/stub_device_sync.cc
index 0cf2306..497d2af 100644
--- a/ash/services/device_sync/stub_device_sync.cc
+++ b/ash/services/device_sync/stub_device_sync.cc
@@ -7,14 +7,14 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/stub_multidevice_util.h"
 #include "ash/services/device_sync/device_sync_base.h"
 #include "ash/services/device_sync/device_sync_impl.h"
 #include "ash/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/stub_multidevice_util.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/device_sync/sync_scheduler.cc b/ash/services/device_sync/sync_scheduler.cc
index 30ff26c3..9ff672d 100644
--- a/ash/services/device_sync/sync_scheduler.cc
+++ b/ash/services/device_sync/sync_scheduler.cc
@@ -4,7 +4,7 @@
 
 #include "ash/services/device_sync/sync_scheduler.h"
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/sync_scheduler_impl.cc b/ash/services/device_sync/sync_scheduler_impl.cc
index 36fe043..ccc2e1f 100644
--- a/ash/services/device_sync/sync_scheduler_impl.cc
+++ b/ash/services/device_sync/sync_scheduler_impl.cc
@@ -9,11 +9,11 @@
 #include <limits>
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/rand_util.h"
 #include "base/strings/stringprintf.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc b/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc
index 286f913..f30756d 100644
--- a/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc
+++ b/ash/services/device_sync/synced_bluetooth_address_tracker_impl.cc
@@ -6,13 +6,13 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/cryptauth_scheduler.h"
 #include "ash/services/device_sync/pref_names.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
diff --git a/ash/services/device_sync/value_string_encoding.cc b/ash/services/device_sync/value_string_encoding.cc
index a2cf3b25..0662454 100644
--- a/ash/services/device_sync/value_string_encoding.cc
+++ b/ash/services/device_sync/value_string_encoding.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/device_sync/value_string_encoding.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/base64url.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/ash/services/multidevice_setup/BUILD.gn b/ash/services/multidevice_setup/BUILD.gn
index d4232a5..e7192c18 100644
--- a/ash/services/multidevice_setup/BUILD.gn
+++ b/ash/services/multidevice_setup/BUILD.gn
@@ -60,6 +60,8 @@
   ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/services/device_sync/proto:util",
     "//ash/services/device_sync/public/cpp",
@@ -73,8 +75,6 @@
     "//ash/services/secure_channel/public/cpp/client",
     "//ash/services/secure_channel/public/mojom",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//components/pref_registry",
     "//components/prefs:prefs",
     "//components/session_manager/core",
@@ -122,9 +122,9 @@
 
   deps = [
     ":multidevice_setup",
+    "//ash/components/multidevice",
     "//ash/services/multidevice_setup/public/mojom",
     "//base",
-    "//chromeos/components/multidevice",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -153,6 +153,8 @@
   deps = [
     ":multidevice_setup",
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/constants",
     "//ash/services/device_sync/public/cpp:test_support",
     "//ash/services/multidevice_setup/public/cpp:oobe_completion_tracker",
@@ -163,8 +165,6 @@
     "//ash/services/secure_channel/public/cpp/client:test_support",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//components/session_manager/core",
     "//components/sync_preferences:test_support",
     "//testing/gmock",
diff --git a/ash/services/multidevice_setup/DEPS b/ash/services/multidevice_setup/DEPS
index 929d3e4..9e98753 100644
--- a/ash/services/multidevice_setup/DEPS
+++ b/ash/services/multidevice_setup/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/components/multidevice",
   "+ash/constants",
   "+ash/services/device_sync",
   "+components/keyed_service/core",
diff --git a/ash/services/multidevice_setup/OWNERS b/ash/services/multidevice_setup/OWNERS
index 7027ab73..18377141 100644
--- a/ash/services/multidevice_setup/OWNERS
+++ b/ash/services/multidevice_setup/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc
index 67d00745..a755e613 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/multidevice_setup/account_status_change_delegate_notifier.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/logging.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
index bfdbd64..21eefeb 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
@@ -7,11 +7,11 @@
 #include <set>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/host_device_timestamp_manager.h"
 #include "ash/services/multidevice_setup/host_status_provider_impl.h"
 #include "base/memory/ptr_util.h"
 #include "base/time/clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
index f746571..6d45ffe 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
@@ -7,6 +7,8 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/fake_account_status_change_delegate.h"
 #include "ash/services/multidevice_setup/fake_host_device_timestamp_manager.h"
@@ -16,8 +18,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc b/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc
index 8a0b6af..dee3f0e 100644
--- a/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc
+++ b/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc
@@ -4,11 +4,11 @@
 
 #include "ash/services/multidevice_setup/android_sms_app_installing_status_observer.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/host_status_provider.h"
 #include "ash/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc b/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
index daef06b..28c4886 100644
--- a/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
+++ b/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
@@ -6,11 +6,11 @@
 
 #include <string>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/multidevice_setup/fake_feature_state_manager.h"
 #include "ash/services/multidevice_setup/fake_host_status_provider.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/multidevice_setup/device_with_connectivity_status.h b/ash/services/multidevice_setup/device_with_connectivity_status.h
index 50fc6f9..6719e406 100644
--- a/ash/services/multidevice_setup/device_with_connectivity_status.h
+++ b/ash/services/multidevice_setup/device_with_connectivity_status.h
@@ -9,8 +9,8 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
-#include "chromeos/components/multidevice/remote_device.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider.h b/ash/services/multidevice_setup/eligible_host_devices_provider.h
index 414ef785..c0a3b1a3 100644
--- a/ash/services/multidevice_setup/eligible_host_devices_provider.h
+++ b/ash/services/multidevice_setup/eligible_host_devices_provider.h
@@ -5,8 +5,8 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/multidevice_setup/device_with_connectivity_status.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc
index 12e46e7..2e8c103 100644
--- a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc
+++ b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.cc
@@ -4,11 +4,11 @@
 
 #include "ash/services/multidevice_setup/eligible_host_devices_provider_impl.h"
 
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h
index 807c94b..e36032e1 100644
--- a/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h
+++ b/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h
@@ -5,9 +5,9 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_IMPL_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_ELIGIBLE_HOST_DEVICES_PROVIDER_IMPL_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/multidevice_setup/eligible_host_devices_provider.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc b/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc
index 3a67a839..22ab028 100644
--- a/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc
+++ b/ash/services/multidevice_setup/eligible_host_devices_provider_impl_unittest.cc
@@ -6,6 +6,9 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
@@ -13,9 +16,6 @@
 #include "base/containers/flat_set.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time_override.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h b/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h
index ba0666d..3a7a5032 100644
--- a/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h
+++ b/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h
@@ -5,8 +5,8 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_ELIGIBLE_HOST_DEVICES_PROVIDER_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_ELIGIBLE_HOST_DEVICES_PROVIDER_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/multidevice_setup/eligible_host_devices_provider.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/fake_host_backend_delegate.h b/ash/services/multidevice_setup/fake_host_backend_delegate.h
index 23b7ee02a..71527215 100644
--- a/ash/services/multidevice_setup/fake_host_backend_delegate.h
+++ b/ash/services/multidevice_setup/fake_host_backend_delegate.h
@@ -5,8 +5,8 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_BACKEND_DELEGATE_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_FAKE_HOST_BACKEND_DELEGATE_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/multidevice_setup/host_backend_delegate.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/feature_state_manager.cc b/ash/services/multidevice_setup/feature_state_manager.cc
index 6fc2ce15..7806639 100644
--- a/ash/services/multidevice_setup/feature_state_manager.cc
+++ b/ash/services/multidevice_setup/feature_state_manager.cc
@@ -4,7 +4,7 @@
 
 #include "ash/services/multidevice_setup/feature_state_manager.h"
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/feature_state_manager_impl.cc b/ash/services/multidevice_setup/feature_state_manager_impl.cc
index 2566eb1..e823a4f 100644
--- a/ash/services/multidevice_setup/feature_state_manager_impl.cc
+++ b/ash/services/multidevice_setup/feature_state_manager_impl.cc
@@ -6,6 +6,9 @@
 
 #include <array>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/global_state_feature_manager.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
@@ -16,9 +19,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc b/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc
index 8498e4e2..ba0ccce 100644
--- a/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc
+++ b/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/fake_feature_state_manager.h"
@@ -17,7 +18,6 @@
 #include "base/containers/contains.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl.cc b/ash/services/multidevice_setup/global_state_feature_manager_impl.cc
index 564213c..a7067fd 100644
--- a/ash/services/multidevice_setup/global_state_feature_manager_impl.cc
+++ b/ash/services/multidevice_setup/global_state_feature_manager_impl.cc
@@ -9,6 +9,10 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
@@ -24,10 +28,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl.h b/ash/services/multidevice_setup/global_state_feature_manager_impl.h
index 550d18e..2cdf99a 100644
--- a/ash/services/multidevice_setup/global_state_feature_manager_impl.h
+++ b/ash/services/multidevice_setup/global_state_feature_manager_impl.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+// TODO(https://crbug.com/1164001): move to forward declaration
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/device_sync/public/mojom/device_sync.mojom.h"
 #include "ash/services/multidevice_setup/global_state_feature_manager.h"
@@ -15,8 +17,6 @@
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
-// TODO(https://crbug.com/1164001): move to forward declaration
-#include "chromeos/components/multidevice/software_feature.h"
 
 class PrefRegistrySimple;
 class PrefService;
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc b/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc
index a027db67..b47c9964 100644
--- a/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc
+++ b/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc
@@ -8,6 +8,10 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/fake_host_status_provider.h"
@@ -18,10 +22,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc
index 3f872c7..6384fc45 100644
--- a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc
+++ b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.cc
@@ -4,12 +4,12 @@
 
 #include "ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc
index 77091d8..69e41e27 100644
--- a/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc
+++ b/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler_unittest.cc
@@ -6,11 +6,11 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/fake_host_backend_delegate.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/multidevice_setup/host_backend_delegate.h b/ash/services/multidevice_setup/host_backend_delegate.h
index 980439b..989c162 100644
--- a/ash/services/multidevice_setup/host_backend_delegate.h
+++ b/ash/services/multidevice_setup/host_backend_delegate.h
@@ -5,8 +5,8 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/host_backend_delegate_impl.cc b/ash/services/multidevice_setup/host_backend_delegate_impl.cc
index b6fea4c02..187bc9c 100644
--- a/ash/services/multidevice_setup/host_backend_delegate_impl.cc
+++ b/ash/services/multidevice_setup/host_backend_delegate_impl.cc
@@ -7,15 +7,15 @@
 #include <algorithm>
 #include <sstream>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/feature_status_change.h"
 #include "ash/services/multidevice_setup/eligible_host_devices_provider.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/host_backend_delegate_impl.h b/ash/services/multidevice_setup/host_backend_delegate_impl.h
index c3d57f90..f5378b0 100644
--- a/ash/services/multidevice_setup/host_backend_delegate_impl.h
+++ b/ash/services/multidevice_setup/host_backend_delegate_impl.h
@@ -5,11 +5,11 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_IMPL_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_BACKEND_DELEGATE_IMPL_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/multidevice_setup/host_backend_delegate.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class PrefRegistrySimple;
diff --git a/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc b/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc
index f22b999..b84ae660 100644
--- a/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc
+++ b/ash/services/multidevice_setup/host_backend_delegate_impl_unittest.cc
@@ -6,6 +6,9 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/fake_eligible_host_devices_provider.h"
@@ -14,9 +17,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/timer/mock_timer.h"
 #include "base/unguessable_token.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc b/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc
index a4e76a9..c08cc40 100644
--- a/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc
+++ b/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc
@@ -4,9 +4,9 @@
 
 #include "ash/services/multidevice_setup/host_device_timestamp_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/time/clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc b/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc
index 6f3a95d..43c1e93 100644
--- a/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc
+++ b/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc
@@ -6,10 +6,10 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/multidevice_setup/fake_host_status_provider.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/multidevice_setup/host_status_provider.cc b/ash/services/multidevice_setup/host_status_provider.cc
index b55f2b1..f775f5ef 100644
--- a/ash/services/multidevice_setup/host_status_provider.cc
+++ b/ash/services/multidevice_setup/host_status_provider.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/multidevice_setup/host_status_provider.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/logging.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/host_status_provider.h b/ash/services/multidevice_setup/host_status_provider.h
index a8b831b1..1be8c78 100644
--- a/ash/services/multidevice_setup/host_status_provider.h
+++ b/ash/services/multidevice_setup/host_status_provider.h
@@ -5,9 +5,9 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/host_status_provider_impl.cc b/ash/services/multidevice_setup/host_status_provider_impl.cc
index cd5a6b6..930d877 100644
--- a/ash/services/multidevice_setup/host_status_provider_impl.cc
+++ b/ash/services/multidevice_setup/host_status_provider_impl.cc
@@ -6,12 +6,12 @@
 
 #include <algorithm>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/eligible_host_devices_provider.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/host_status_provider_impl.h b/ash/services/multidevice_setup/host_status_provider_impl.h
index cef7eeb1..0103ffc 100644
--- a/ash/services/multidevice_setup/host_status_provider_impl.h
+++ b/ash/services/multidevice_setup/host_status_provider_impl.h
@@ -5,12 +5,12 @@
 #ifndef ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_IMPL_H_
 #define ASH_SERVICES_MULTIDEVICE_SETUP_HOST_STATUS_PROVIDER_IMPL_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/multidevice_setup/host_backend_delegate.h"
 #include "ash/services/multidevice_setup/host_status_provider.h"
 #include "ash/services/multidevice_setup/host_verifier.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc b/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc
index c75f9e9..65913f6 100644
--- a/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc
+++ b/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/fake_eligible_host_devices_provider.h"
 #include "ash/services/multidevice_setup/fake_host_backend_delegate.h"
@@ -13,7 +14,6 @@
 #include "ash/services/multidevice_setup/fake_host_verifier.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/host_verifier.cc b/ash/services/multidevice_setup/host_verifier.cc
index a4bf4d8..77b8e1b 100644
--- a/ash/services/multidevice_setup/host_verifier.cc
+++ b/ash/services/multidevice_setup/host_verifier.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/multidevice_setup/host_verifier.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/logging.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/host_verifier_impl.cc b/ash/services/multidevice_setup/host_verifier_impl.cc
index 9fe20d6..2b4e309f 100644
--- a/ash/services/multidevice_setup/host_verifier_impl.cc
+++ b/ash/services/multidevice_setup/host_verifier_impl.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "base/bind.h"
@@ -13,8 +15,6 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/services/multidevice_setup/host_verifier_impl.h b/ash/services/multidevice_setup/host_verifier_impl.h
index 7550277..258d5b9 100644
--- a/ash/services/multidevice_setup/host_verifier_impl.h
+++ b/ash/services/multidevice_setup/host_verifier_impl.h
@@ -7,13 +7,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/multidevice_setup/host_backend_delegate.h"
 #include "ash/services/multidevice_setup/host_verifier.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/default_clock.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 class PrefRegistrySimple;
 class PrefService;
diff --git a/ash/services/multidevice_setup/host_verifier_impl_unittest.cc b/ash/services/multidevice_setup/host_verifier_impl_unittest.cc
index d778b95..8f50786 100644
--- a/ash/services/multidevice_setup/host_verifier_impl_unittest.cc
+++ b/ash/services/multidevice_setup/host_verifier_impl_unittest.cc
@@ -8,6 +8,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/proto/cryptauth_common.pb.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
@@ -16,9 +19,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/multidevice_setup/multidevice_setup_impl.cc b/ash/services/multidevice_setup/multidevice_setup_impl.cc
index a9e8177..511734c 100644
--- a/ash/services/multidevice_setup/multidevice_setup_impl.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_impl.cc
@@ -7,6 +7,7 @@
 
 #include "ash/services/multidevice_setup/multidevice_setup_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h"
 #include "ash/services/multidevice_setup/android_sms_app_installing_status_observer.h"
@@ -31,7 +32,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc
index ff35582..3f7bdbf8 100644
--- a/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -6,6 +6,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/device_sync/public/cpp/fake_gcm_device_info_provider.h"
@@ -43,7 +44,6 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/multidevice_setup/multidevice_setup_initializer.cc b/ash/services/multidevice_setup/multidevice_setup_initializer.cc
index 451687c..d3d0870 100644
--- a/ash/services/multidevice_setup/multidevice_setup_initializer.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -6,12 +6,12 @@
 
 #include "ash/services/multidevice_setup/multidevice_setup_initializer.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/multidevice_setup_impl.h"
 #include "ash/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h"
 #include "ash/services/multidevice_setup/public/cpp/android_sms_pairing_state_tracker.h"
 #include "base/check.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/multidevice_setup_service.cc b/ash/services/multidevice_setup/multidevice_setup_service.cc
index 74f2542db..35fd78ef 100644
--- a/ash/services/multidevice_setup/multidevice_setup_service.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_service.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/multidevice_setup/multidevice_setup_service.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h"
 #include "ash/services/multidevice_setup/android_sms_app_installing_status_observer.h"
 #include "ash/services/multidevice_setup/global_state_feature_manager_impl.h"
@@ -19,7 +20,6 @@
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "ash/services/multidevice_setup/wifi_sync_notification_controller.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc b/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc
index 914c275a..9b269b35 100644
--- a/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/device_sync/public/cpp/fake_gcm_device_info_provider.h"
 #include "ash/services/multidevice_setup/fake_account_status_change_delegate.h"
@@ -21,7 +22,6 @@
 #include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc b/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc
index ed4e02d3..ef0e2b9 100644
--- a/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc
+++ b/ash/services/multidevice_setup/privileged_host_device_setter_impl.cc
@@ -4,9 +4,9 @@
 
 #include "ash/services/multidevice_setup/privileged_host_device_setter_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/multidevice_setup_base.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/public/cpp/BUILD.gn b/ash/services/multidevice_setup/public/cpp/BUILD.gn
index 44971d2..5af24e7 100644
--- a/ash/services/multidevice_setup/public/cpp/BUILD.gn
+++ b/ash/services/multidevice_setup/public/cpp/BUILD.gn
@@ -15,10 +15,10 @@
   ]
 
   public_deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/services/multidevice_setup/public/mojom",
     "//base",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//mojo/public/cpp/bindings",
   ]
 }
@@ -135,13 +135,13 @@
   deps = [
     ":cpp",
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/services/multidevice_setup",
     "//ash/services/multidevice_setup:test_support",
     "//ash/services/multidevice_setup/public/mojom",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
index 454a1a5..fa2ec8f 100644
--- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
+++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
@@ -6,8 +6,8 @@
 
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.h"
 
+#include "ash/components/multidevice/remote_device.h"
 #include "base/containers/flat_map.h"
-#include "chromeos/components/multidevice/remote_device.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h
index 35bb840..3b7cd99 100644
--- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h
+++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h
@@ -10,10 +10,10 @@
 #include <string>
 #include <tuple>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc
index 706f0ba..1fc4ef7 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/check.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h
index 0ad327c..2495fc2 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h
@@ -8,11 +8,11 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
index c716f678..8d7d8ea 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
@@ -7,12 +7,12 @@
 
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace {
 
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
index 7f36b25..48e483f 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
@@ -8,13 +8,13 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/callback.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
index 99296d5e..501c063 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <tuple>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/multidevice_setup/multidevice_setup_initializer.h"
 #include "ash/services/multidevice_setup/multidevice_setup_service.h"
 #include "ash/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h"
@@ -20,7 +21,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom b/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom
index 873771af..b874d56f 100644
--- a/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom
+++ b/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom
@@ -4,7 +4,7 @@
 
 module ash.multidevice_setup.mojom;
 
-import "chromeos/components/multidevice/mojom/multidevice_types.mojom";
+import "ash/components/multidevice/mojom/multidevice_types.mojom";
 import "ash/services/device_sync/public/mojom/device_sync.mojom";
 
 // Enumeration of possible opt-in entry points for Phone Hub Camera Roll
diff --git a/ash/services/multidevice_setup/wifi_sync_notification_controller.cc b/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
index 6fc7c40..72bb76c4 100644
--- a/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
+++ b/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
@@ -6,6 +6,10 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/multidevice_setup/account_status_change_delegate_notifier.h"
 #include "ash/services/multidevice_setup/global_state_feature_manager.h"
@@ -14,10 +18,6 @@
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/memory/ptr_util.h"
 #include "base/power_monitor/power_monitor.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
diff --git a/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc b/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc
index bf378183..7384607 100644
--- a/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc
+++ b/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc
@@ -6,6 +6,9 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/fake_account_status_change_delegate.h"
@@ -16,9 +19,6 @@
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ash/services/secure_channel/BUILD.gn b/ash/services/secure_channel/BUILD.gn
index 5054be7..0dd2f39 100644
--- a/ash/services/secure_channel/BUILD.gn
+++ b/ash/services/secure_channel/BUILD.gn
@@ -172,14 +172,14 @@
   ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/services/device_sync/proto",
     "//ash/services/secure_channel/public/cpp/shared",
     "//ash/services/secure_channel/public/mojom",
     "//base",
     "//chromeos",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//crypto",
     "//device/bluetooth",
     "//device/bluetooth/public/cpp",
@@ -275,12 +275,12 @@
   public_deps = [ ":secure_channel" ]
 
   deps = [
+    "//ash/components/multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/services/secure_channel/public/cpp/shared",
     "//ash/services/secure_channel/public/mojom",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice/logging",
     "//device/bluetooth",
     "//testing/gmock",
   ]
@@ -339,15 +339,15 @@
 
   deps = [
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
+    "//ash/components/multidevice/logging",
     "//ash/services/secure_channel/public/cpp/client:test_support",
     "//ash/services/secure_channel/public/cpp/client:unit_tests",
     "//ash/services/secure_channel/public/cpp/shared",
     "//ash/services/secure_channel/public/mojom",
     "//ash/services/secure_channel/public/mojom:unit_tests",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
-    "//chromeos/components/multidevice/logging",
     "//device/bluetooth:mocks",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ash/services/secure_channel/DEPS b/ash/services/secure_channel/DEPS
index e918f60..7d47adf 100644
--- a/ash/services/secure_channel/DEPS
+++ b/ash/services/secure_channel/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/components/multidevice",
   "+ash/constants",
   "+ash/services/device_sync",
   "+ash/services/multidevice_setup/public/cpp",
diff --git a/ash/services/secure_channel/OWNERS b/ash/services/secure_channel/OWNERS
index 7027ab73..18377141 100644
--- a/ash/services/secure_channel/OWNERS
+++ b/ash/services/secure_channel/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/ash/services/secure_channel/active_connection_manager.cc b/ash/services/secure_channel/active_connection_manager.cc
index 3b22733..244d99f1 100644
--- a/ash/services/secure_channel/active_connection_manager.cc
+++ b/ash/services/secure_channel/active_connection_manager.cc
@@ -4,11 +4,11 @@
 
 #include "ash/services/secure_channel/active_connection_manager.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/authenticated_channel.h"
 #include "ash/services/secure_channel/client_connection_parameters.h"
 #include "ash/services/secure_channel/connection_details.h"
 #include "base/logging.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/active_connection_manager_impl.cc b/ash/services/secure_channel/active_connection_manager_impl.cc
index d532f82..6d07835 100644
--- a/ash/services/secure_channel/active_connection_manager_impl.cc
+++ b/ash/services/secure_channel/active_connection_manager_impl.cc
@@ -4,9 +4,9 @@
 
 #include "ash/services/secure_channel/active_connection_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/multiplexed_channel_impl.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/authenticated_channel_impl.cc b/ash/services/secure_channel/authenticated_channel_impl.cc
index 0b671b7..f5705b8 100644
--- a/ash/services/secure_channel/authenticated_channel_impl.cc
+++ b/ash/services/secure_channel/authenticated_channel_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/secure_channel/authenticated_channel_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
 #include "base/bind.h"
@@ -11,7 +12,6 @@
 #include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/authenticated_channel_impl_unittest.cc b/ash/services/secure_channel/authenticated_channel_impl_unittest.cc
index f5d0e57fb..c576905 100644
--- a/ash/services/secure_channel/authenticated_channel_impl_unittest.cc
+++ b/ash/services/secure_channel/authenticated_channel_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/fake_authenticated_channel.h"
 #include "ash/services/secure_channel/fake_connection.h"
 #include "ash/services/secure_channel/fake_secure_channel_connection.h"
@@ -21,7 +22,6 @@
 #include "base/containers/contains.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/background_eid_generator.cc b/ash/services/secure_channel/background_eid_generator.cc
index 02f7467..dc8dfdf 100644
--- a/ash/services/secure_channel/background_eid_generator.cc
+++ b/ash/services/secure_channel/background_eid_generator.cc
@@ -7,6 +7,9 @@
 #include <cstring>
 #include <memory>
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/data_with_timestamp.h"
 #include "ash/services/secure_channel/raw_eid_generator.h"
@@ -16,9 +19,6 @@
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/background_eid_generator.h b/ash/services/secure_channel/background_eid_generator.h
index b899c678..d9c003e 100644
--- a/ash/services/secure_channel/background_eid_generator.h
+++ b/ash/services/secure_channel/background_eid_generator.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 // TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace base {
 class Clock;
diff --git a/ash/services/secure_channel/background_eid_generator_unittest.cc b/ash/services/secure_channel/background_eid_generator_unittest.cc
index 3edabca..40649ea8 100644
--- a/ash/services/secure_channel/background_eid_generator_unittest.cc
+++ b/ash/services/secure_channel/background_eid_generator_unittest.cc
@@ -7,15 +7,15 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/data_with_timestamp.h"
 #include "ash/services/secure_channel/raw_eid_generator_impl.h"
 #include "base/strings/string_util.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/secure_channel/ble_advertisement_generator.cc b/ash/services/secure_channel/ble_advertisement_generator.cc
index 2a709148..5b3688a 100644
--- a/ash/services/secure_channel/ble_advertisement_generator.cc
+++ b/ash/services/secure_channel/ble_advertisement_generator.cc
@@ -7,10 +7,10 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/data_with_timestamp.h"
 #include "ash/services/secure_channel/foreground_eid_generator.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/ble_advertisement_generator.h b/ash/services/secure_channel/ble_advertisement_generator.h
index 65d5b89..e81e7e6 100644
--- a/ash/services/secure_channel/ble_advertisement_generator.h
+++ b/ash/services/secure_channel/ble_advertisement_generator.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 // TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/ble_advertisement_generator_unittest.cc b/ash/services/secure_channel/ble_advertisement_generator_unittest.cc
index f4c5703..48fce485 100644
--- a/ash/services/secure_channel/ble_advertisement_generator_unittest.cc
+++ b/ash/services/secure_channel/ble_advertisement_generator_unittest.cc
@@ -6,13 +6,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/data_with_timestamp.h"
 #include "ash/services/secure_channel/mock_foreground_eid_generator.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/secure_channel/ble_advertiser_impl.cc b/ash/services/secure_channel/ble_advertiser_impl.cc
index 577e94d5..a6c1154 100644
--- a/ash/services/secure_channel/ble_advertiser_impl.cc
+++ b/ash/services/secure_channel/ble_advertiser_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/secure_channel/ble_advertiser_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/bluetooth_helper.h"
 #include "ash/services/secure_channel/error_tolerant_ble_advertisement_impl.h"
 #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h"
@@ -15,7 +16,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/ble_characteristics_finder.cc b/ash/services/secure_channel/ble_characteristics_finder.cc
index b0f26b0..4881883 100644
--- a/ash/services/secure_channel/ble_characteristics_finder.cc
+++ b/ash/services/secure_channel/ble_characteristics_finder.cc
@@ -4,10 +4,10 @@
 
 #include "ash/services/secure_channel/ble_characteristics_finder.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/background_eid_generator.h"
 #include "base/bind.h"
 #include "base/strings/string_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
diff --git a/ash/services/secure_channel/ble_characteristics_finder.h b/ash/services/secure_channel/ble_characteristics_finder.h
index bc92d21..af2bcef 100644
--- a/ash/services/secure_channel/ble_characteristics_finder.h
+++ b/ash/services/secure_channel/ble_characteristics_finder.h
@@ -5,13 +5,13 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_BLE_CHARACTERISTICS_FINDER_H_
 #define ASH_SERVICES_SECURE_CHANNEL_BLE_CHARACTERISTICS_FINDER_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/remote_attribute.h"
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
diff --git a/ash/services/secure_channel/ble_characteristics_finder_unittest.cc b/ash/services/secure_channel/ble_characteristics_finder_unittest.cc
index c16a733..3e8c0bc22 100644
--- a/ash/services/secure_channel/ble_characteristics_finder_unittest.cc
+++ b/ash/services/secure_channel/ble_characteristics_finder_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/background_eid_generator.h"
 #include "ash/services/secure_channel/fake_background_eid_generator.h"
 #include "ash/services/secure_channel/remote_attribute.h"
@@ -15,7 +16,6 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
diff --git a/ash/services/secure_channel/ble_connection_manager.cc b/ash/services/secure_channel/ble_connection_manager.cc
index 98b57d99..abc7ad66 100644
--- a/ash/services/secure_channel/ble_connection_manager.cc
+++ b/ash/services/secure_channel/ble_connection_manager.cc
@@ -4,11 +4,11 @@
 
 #include "ash/services/secure_channel/ble_connection_manager.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/authenticated_channel.h"
 #include "base/containers/contains.h"
 #include "base/logging.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/ble_connection_manager_impl.cc b/ash/services/secure_channel/ble_connection_manager_impl.cc
index af65904..04cb90e 100644
--- a/ash/services/secure_channel/ble_connection_manager_impl.cc
+++ b/ash/services/secure_channel/ble_connection_manager_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/authenticated_channel_impl.h"
 #include "ash/services/secure_channel/ble_advertiser_impl.h"
 #include "ash/services/secure_channel/ble_constants.h"
@@ -19,7 +20,6 @@
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc b/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc
index 4ca41461..a921a23 100644
--- a/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc
+++ b/ash/services/secure_channel/ble_connection_manager_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <tuple>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/authenticated_channel_impl.h"
 #include "ash/services/secure_channel/ble_advertiser_impl.h"
 #include "ash/services/secure_channel/ble_constants.h"
@@ -31,7 +32,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/ash/services/secure_channel/ble_scanner.cc b/ash/services/secure_channel/ble_scanner.cc
index 71ea2f9..6cc87aa 100644
--- a/ash/services/secure_channel/ble_scanner.cc
+++ b/ash/services/secure_channel/ble_scanner.cc
@@ -4,10 +4,10 @@
 
 #include "ash/services/secure_channel/ble_scanner.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/notreached.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/ble_scanner.h b/ash/services/secure_channel/ble_scanner.h
index 954c2ff..ebd2dc0 100644
--- a/ash/services/secure_channel/ble_scanner.h
+++ b/ash/services/secure_channel/ble_scanner.h
@@ -7,6 +7,7 @@
 
 #include <ostream>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/connection_attempt_details.h"
 #include "ash/services/secure_channel/connection_role.h"
 #include "ash/services/secure_channel/device_id_pair.h"
@@ -14,7 +15,6 @@
 #include "base/containers/flat_set.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace device {
 class BluetoothDevice;
diff --git a/ash/services/secure_channel/ble_scanner_impl.cc b/ash/services/secure_channel/ble_scanner_impl.cc
index eb1cf80..d07e6e8 100644
--- a/ash/services/secure_channel/ble_scanner_impl.cc
+++ b/ash/services/secure_channel/ble_scanner_impl.cc
@@ -7,6 +7,8 @@
 #include <iostream>
 #include <sstream>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/ble_constants.h"
 #include "ash/services/secure_channel/ble_synchronizer_base.h"
@@ -15,8 +17,6 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_discovery_session.h"
 #include "device/bluetooth/public/cpp/bluetooth_uuid.h"
diff --git a/ash/services/secure_channel/ble_scanner_impl_unittest.cc b/ash/services/secure_channel/ble_scanner_impl_unittest.cc
index 2ca499a5..b2fa6ea6 100644
--- a/ash/services/secure_channel/ble_scanner_impl_unittest.cc
+++ b/ash/services/secure_channel/ble_scanner_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/ble_constants.h"
 #include "ash/services/secure_channel/connection_role.h"
 #include "ash/services/secure_channel/fake_ble_scanner.h"
@@ -17,7 +18,6 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/bind.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/ash/services/secure_channel/ble_synchronizer.cc b/ash/services/secure_channel/ble_synchronizer.cc
index eee4a16a..6bdd1a0 100644
--- a/ash/services/secure_channel/ble_synchronizer.cc
+++ b/ash/services/secure_channel/ble_synchronizer.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/ble_weave_client_connection.cc b/ash/services/secure_channel/ble_weave_client_connection.cc
index aacae577..3c0c2d3 100644
--- a/ash/services/secure_channel/ble_weave_client_connection.cc
+++ b/ash/services/secure_channel/ble_weave_client_connection.cc
@@ -8,6 +8,7 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/background_eid_generator.h"
 #include "ash/services/secure_channel/ble_weave_packet_generator.h"
 #include "ash/services/secure_channel/ble_weave_packet_receiver.h"
@@ -22,7 +23,6 @@
 #include "base/task/task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/bluetooth_gatt_connection.h"
 
 namespace ash::secure_channel::weave {
diff --git a/ash/services/secure_channel/ble_weave_client_connection_unittest.cc b/ash/services/secure_channel/ble_weave_client_connection_unittest.cc
index d1940f77..262d6f5 100644
--- a/ash/services/secure_channel/ble_weave_client_connection_unittest.cc
+++ b/ash/services/secure_channel/ble_weave_client_connection_unittest.cc
@@ -9,6 +9,9 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/ble_weave_packet_generator.h"
 #include "ash/services/secure_channel/ble_weave_packet_receiver.h"
 #include "ash/services/secure_channel/connection_observer.h"
@@ -24,9 +27,6 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/timer/mock_timer.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
diff --git a/ash/services/secure_channel/ble_weave_packet_receiver.cc b/ash/services/secure_channel/ble_weave_packet_receiver.cc
index 5796e289..0a9a509 100644
--- a/ash/services/secure_channel/ble_weave_packet_receiver.cc
+++ b/ash/services/secure_channel/ble_weave_packet_receiver.cc
@@ -12,9 +12,9 @@
 #include <netinet/in.h>
 #endif
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/check_op.h"
 #include "base/notreached.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel::weave {
 
diff --git a/ash/services/secure_channel/bluetooth_helper.cc b/ash/services/secure_channel/bluetooth_helper.cc
index ebe94b5..7b0c406e 100644
--- a/ash/services/secure_channel/bluetooth_helper.cc
+++ b/ash/services/secure_channel/bluetooth_helper.cc
@@ -4,9 +4,9 @@
 
 #include "ash/services/secure_channel/bluetooth_helper.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/logging.h"
 #include "base/notreached.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/bluetooth_helper.h b/ash/services/secure_channel/bluetooth_helper.h
index 4ad8536..de0ce7e 100644
--- a/ash/services/secure_channel/bluetooth_helper.h
+++ b/ash/services/secure_channel/bluetooth_helper.h
@@ -9,9 +9,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/data_with_timestamp.h"
 #include "ash/services/secure_channel/device_id_pair.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/bluetooth_helper_impl.cc b/ash/services/secure_channel/bluetooth_helper_impl.cc
index 18c107b..2ef9ef50 100644
--- a/ash/services/secure_channel/bluetooth_helper_impl.cc
+++ b/ash/services/secure_channel/bluetooth_helper_impl.cc
@@ -4,6 +4,10 @@
 
 #include "ash/services/secure_channel/bluetooth_helper_impl.h"
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/secure_channel/background_eid_generator.h"
 #include "ash/services/secure_channel/ble_advertisement_generator.h"
@@ -11,10 +15,6 @@
 #include "ash/services/secure_channel/foreground_eid_generator.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/bluetooth_helper_impl.h b/ash/services/secure_channel/bluetooth_helper_impl.h
index 1b5525a6..897001d 100644
--- a/ash/services/secure_channel/bluetooth_helper_impl.h
+++ b/ash/services/secure_channel/bluetooth_helper_impl.h
@@ -8,9 +8,9 @@
 #include <memory>
 #include <string>
 
-#include "ash/services/secure_channel/bluetooth_helper.h"
 // TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_cache.h"
+#include "ash/services/secure_channel/bluetooth_helper.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc b/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc
index 76b992d5..8a37d87 100644
--- a/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc
+++ b/ash/services/secure_channel/bluetooth_helper_impl_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/ble_advertisement_generator.h"
 #include "ash/services/secure_channel/device_id_pair.h"
 #include "ash/services/secure_channel/fake_background_eid_generator.h"
@@ -14,8 +16,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/client_connection_parameters.cc b/ash/services/secure_channel/client_connection_parameters.cc
index 133baeaf6..1fe9ec6 100644
--- a/ash/services/secure_channel/client_connection_parameters.cc
+++ b/ash/services/secure_channel/client_connection_parameters.cc
@@ -4,7 +4,7 @@
 
 #include "ash/services/secure_channel/client_connection_parameters.h"
 
-#include "chromeos/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/connect_to_device_operation.h b/ash/services/secure_channel/connect_to_device_operation.h
index dc208d61..80e06155 100644
--- a/ash/services/secure_channel/connect_to_device_operation.h
+++ b/ash/services/secure_channel/connect_to_device_operation.h
@@ -5,10 +5,10 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_CONNECT_TO_DEVICE_OPERATION_H_
 #define ASH_SERVICES_SECURE_CHANNEL_CONNECT_TO_DEVICE_OPERATION_H_
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h"
 #include "base/callback.h"
 #include "base/logging.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/connection.cc b/ash/services/secure_channel/connection.cc
index f0745f2..2eba632a 100644
--- a/ash/services/secure_channel/connection.cc
+++ b/ash/services/secure_channel/connection.cc
@@ -7,13 +7,13 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/connection_observer.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
 #include "ash/services/secure_channel/wire_message.h"
 #include "base/callback.h"
 #include "base/logging.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/connection.h b/ash/services/secure_channel/connection.h
index 1c2555ab..8ec5ece 100644
--- a/ash/services/secure_channel/connection.h
+++ b/ash/services/secure_channel/connection.h
@@ -8,11 +8,11 @@
 #include <memory>
 #include <ostream>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom-forward.h"
 #include "base/callback_forward.h"
 #include "base/observer_list.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/connection_attempt.h b/ash/services/secure_channel/connection_attempt.h
index 136a61c..801063d 100644
--- a/ash/services/secure_channel/connection_attempt.h
+++ b/ash/services/secure_channel/connection_attempt.h
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/client_connection_parameters.h"
 #include "ash/services/secure_channel/connection_attempt_delegate.h"
 #include "ash/services/secure_channel/connection_attempt_details.h"
@@ -17,7 +18,6 @@
 #include "ash/services/secure_channel/pending_connection_request.h"
 #include "ash/services/secure_channel/pending_connection_request_delegate.h"
 #include "base/time/clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/connection_attempt_base.h b/ash/services/secure_channel/connection_attempt_base.h
index 96369fe9..b5e6e28 100644
--- a/ash/services/secure_channel/connection_attempt_base.h
+++ b/ash/services/secure_channel/connection_attempt_base.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_CONNECTION_ATTEMPT_BASE_H_
 #define ASH_SERVICES_SECURE_CHANNEL_CONNECTION_ATTEMPT_BASE_H_
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/authenticated_channel.h"
 #include "ash/services/secure_channel/connect_to_device_operation.h"
 #include "ash/services/secure_channel/connection_attempt.h"
@@ -18,7 +19,6 @@
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/default_clock.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/connection_attempt_details.cc b/ash/services/secure_channel/connection_attempt_details.cc
index 3365df5..9609e506 100644
--- a/ash/services/secure_channel/connection_attempt_details.cc
+++ b/ash/services/secure_channel/connection_attempt_details.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/secure_channel/connection_attempt_details.h"
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/check.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/connection_details.cc b/ash/services/secure_channel/connection_details.cc
index e87cfd5..3226e4c 100644
--- a/ash/services/secure_channel/connection_details.cc
+++ b/ash/services/secure_channel/connection_details.cc
@@ -6,8 +6,8 @@
 
 #include <tuple>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/check.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/connection_unittest.cc b/ash/services/secure_channel/connection_unittest.cc
index 231399b..51a20ab 100644
--- a/ash/services/secure_channel/connection_unittest.cc
+++ b/ash/services/secure_channel/connection_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "ash/services/secure_channel/connection.h"
 
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/connection_observer.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
@@ -12,8 +14,6 @@
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/bind.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/ash/services/secure_channel/device_id_pair.cc b/ash/services/secure_channel/device_id_pair.cc
index 4a381043..33d8a1af 100644
--- a/ash/services/secure_channel/device_id_pair.cc
+++ b/ash/services/secure_channel/device_id_pair.cc
@@ -6,8 +6,8 @@
 
 #include <functional>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "base/hash/hash.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/device_to_device_authenticator.cc b/ash/services/secure_channel/device_to_device_authenticator.cc
index 9b0b8df..de8a4c12 100644
--- a/ash/services/secure_channel/device_to_device_authenticator.cc
+++ b/ash/services/secure_channel/device_to_device_authenticator.cc
@@ -7,6 +7,8 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/secure_channel/authenticator.h"
 #include "ash/services/secure_channel/connection.h"
 #include "ash/services/secure_channel/device_to_device_initiator_helper.h"
@@ -17,8 +19,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/device_to_device_authenticator.h b/ash/services/secure_channel/device_to_device_authenticator.h
index b4c70e32..cfc91b8e 100644
--- a/ash/services/secure_channel/device_to_device_authenticator.h
+++ b/ash/services/secure_channel/device_to_device_authenticator.h
@@ -7,13 +7,13 @@
 
 #include <memory>
 
+// TODO(https://crbug.com/1164001): move to forward declaration.
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/secure_channel/authenticator.h"
 #include "ash/services/secure_channel/connection.h"
 #include "ash/services/secure_channel/connection_observer.h"
 #include "ash/services/secure_channel/session_keys.h"
 #include "base/memory/weak_ptr.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 
 namespace base {
 class OneShotTimer;
diff --git a/ash/services/secure_channel/device_to_device_authenticator_unittest.cc b/ash/services/secure_channel/device_to_device_authenticator_unittest.cc
index 54d3297..865831e 100644
--- a/ash/services/secure_channel/device_to_device_authenticator_unittest.cc
+++ b/ash/services/secure_channel/device_to_device_authenticator_unittest.cc
@@ -8,6 +8,8 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/authenticator.h"
 #include "ash/services/secure_channel/connection.h"
 #include "ash/services/secure_channel/device_to_device_responder_operations.h"
@@ -22,8 +24,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/rand_util.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/secure_channel/device_to_device_initiator_helper.cc b/ash/services/secure_channel/device_to_device_initiator_helper.cc
index 95d9285..9c2e35f 100644
--- a/ash/services/secure_channel/device_to_device_initiator_helper.cc
+++ b/ash/services/secure_channel/device_to_device_initiator_helper.cc
@@ -4,11 +4,11 @@
 
 #include "ash/services/secure_channel/device_to_device_initiator_helper.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "base/bind.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/device_to_device_initiator_helper.h b/ash/services/secure_channel/device_to_device_initiator_helper.h
index 0f863e64..c0404961 100644
--- a/ash/services/secure_channel/device_to_device_initiator_helper.h
+++ b/ash/services/secure_channel/device_to_device_initiator_helper.h
@@ -8,11 +8,11 @@
 #include <memory>
 #include <string>
 
+// TODO(https://crbug.com/1164001): move to forward declaration.
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/secure_channel/session_keys.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "third_party/ukey2/proto/device_to_device_messages.pb.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_operations_unittest.cc b/ash/services/secure_channel/device_to_device_operations_unittest.cc
index 6a6aa0c4..ec2b3c3 100644
--- a/ash/services/secure_channel/device_to_device_operations_unittest.cc
+++ b/ash/services/secure_channel/device_to_device_operations_unittest.cc
@@ -4,12 +4,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
 #include "ash/services/secure_channel/device_to_device_initiator_helper.h"
 #include "ash/services/secure_channel/device_to_device_responder_operations.h"
 #include "ash/services/secure_channel/session_keys.h"
 #include "base/base64url.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_responder_operations.cc b/ash/services/secure_channel/device_to_device_responder_operations.cc
index dc7ffdd0..6649a28 100644
--- a/ash/services/secure_channel/device_to_device_responder_operations.cc
+++ b/ash/services/secure_channel/device_to_device_responder_operations.cc
@@ -4,12 +4,12 @@
 
 #include "ash/services/secure_channel/device_to_device_responder_operations.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/session_keys.h"
 #include "base/bind.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 #include "third_party/ukey2/proto/device_to_device_messages.pb.h"
 
diff --git a/ash/services/secure_channel/device_to_device_responder_operations.h b/ash/services/secure_channel/device_to_device_responder_operations.h
index 5d110ab..40a13da 100644
--- a/ash/services/secure_channel/device_to_device_responder_operations.h
+++ b/ash/services/secure_channel/device_to_device_responder_operations.h
@@ -7,9 +7,9 @@
 
 #include <string>
 
-#include "base/callback_forward.h"
 // TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/secure_message_delegate.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
+#include "base/callback_forward.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/device_to_device_secure_context.cc b/ash/services/secure_channel/device_to_device_secure_context.cc
index 7877d47..e6fe354 100644
--- a/ash/services/secure_channel/device_to_device_secure_context.cc
+++ b/ash/services/secure_channel/device_to_device_secure_context.cc
@@ -6,12 +6,12 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/session_keys.h"
 #include "base/bind.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/device_to_device_secure_context.h b/ash/services/secure_channel/device_to_device_secure_context.h
index 50c69742..2abc6fd3 100644
--- a/ash/services/secure_channel/device_to_device_secure_context.h
+++ b/ash/services/secure_channel/device_to_device_secure_context.h
@@ -9,10 +9,10 @@
 #include <queue>
 #include <vector>
 
+// TODO(https://crbug.com/1164001): move to forward declaration.
+#include "ash/components/multidevice/secure_message_delegate.h"
 #include "ash/services/secure_channel/secure_context.h"
 #include "base/memory/weak_ptr.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/secure_message_delegate.h"
 #include "third_party/ukey2/proto/device_to_device_messages.pb.h"
 
 namespace securemessage {
diff --git a/ash/services/secure_channel/device_to_device_secure_context_unittest.cc b/ash/services/secure_channel/device_to_device_secure_context_unittest.cc
index 6aab6b2..e5b473c6 100644
--- a/ash/services/secure_channel/device_to_device_secure_context_unittest.cc
+++ b/ash/services/secure_channel/device_to_device_secure_context_unittest.cc
@@ -7,12 +7,12 @@
 #include <list>
 #include <memory>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/session_keys.h"
 #include "base/bind.h"
 #include "base/callback.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 
diff --git a/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc b/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc
index 46af006..89000393 100644
--- a/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc
+++ b/ash/services/secure_channel/error_tolerant_ble_advertisement_impl.cc
@@ -6,13 +6,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/ble_constants.h"
 #include "ash/services/secure_channel/ble_synchronizer_base.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/fake_ble_advertisement_generator.cc b/ash/services/secure_channel/fake_ble_advertisement_generator.cc
index 08189e3..43fd608 100644
--- a/ash/services/secure_channel/fake_ble_advertisement_generator.cc
+++ b/ash/services/secure_channel/fake_ble_advertisement_generator.cc
@@ -4,7 +4,7 @@
 
 #include "ash/services/secure_channel/fake_ble_advertisement_generator.h"
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/fake_bluetooth_helper.h b/ash/services/secure_channel/fake_bluetooth_helper.h
index b2162e4..0e0a9aae 100644
--- a/ash/services/secure_channel/fake_bluetooth_helper.h
+++ b/ash/services/secure_channel/fake_bluetooth_helper.h
@@ -10,9 +10,9 @@
 #include <unordered_map>
 #include <utility>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/bluetooth_helper.h"
 #include "ash/services/secure_channel/data_with_timestamp.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/fake_secure_channel.h b/ash/services/secure_channel/fake_secure_channel.h
index ce7d991..c284dae 100644
--- a/ash/services/secure_channel/fake_secure_channel.h
+++ b/ash/services/secure_channel/fake_secure_channel.h
@@ -10,9 +10,9 @@
 #include <tuple>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_cache.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "ash/services/secure_channel/secure_channel_base.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
diff --git a/ash/services/secure_channel/foreground_eid_generator.cc b/ash/services/secure_channel/foreground_eid_generator.cc
index 5f11d2d..26070bd 100644
--- a/ash/services/secure_channel/foreground_eid_generator.cc
+++ b/ash/services/secure_channel/foreground_eid_generator.cc
@@ -7,6 +7,8 @@
 #include <cstring>
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/raw_eid_generator.h"
 #include "ash/services/secure_channel/raw_eid_generator_impl.h"
@@ -16,8 +18,6 @@
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/foreground_eid_generator_unittest.cc b/ash/services/secure_channel/foreground_eid_generator_unittest.cc
index c7f50f4..725e73e6 100644
--- a/ash/services/secure_channel/foreground_eid_generator_unittest.cc
+++ b/ash/services/secure_channel/foreground_eid_generator_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/secure_channel/raw_eid_generator_impl.h"
 #include "base/strings/string_util.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/services/secure_channel/multiplexed_channel_impl.cc b/ash/services/secure_channel/multiplexed_channel_impl.cc
index d8d628af..dc9a656 100644
--- a/ash/services/secure_channel/multiplexed_channel_impl.cc
+++ b/ash/services/secure_channel/multiplexed_channel_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/secure_channel/multiplexed_channel_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
 #include "ash/services/secure_channel/single_client_proxy_impl.h"
@@ -11,7 +12,6 @@
 #include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/nearby_connection.cc b/ash/services/secure_channel/nearby_connection.cc
index 323c21ea..dd60511 100644
--- a/ash/services/secure_channel/nearby_connection.cc
+++ b/ash/services/secure_channel/nearby_connection.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/secure_channel/nearby_connection.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/mojom/nearby_connector.mojom.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
@@ -15,7 +16,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 
diff --git a/ash/services/secure_channel/nearby_connection_manager.cc b/ash/services/secure_channel/nearby_connection_manager.cc
index bb2c529..2b2709b0 100644
--- a/ash/services/secure_channel/nearby_connection_manager.cc
+++ b/ash/services/secure_channel/nearby_connection_manager.cc
@@ -4,9 +4,9 @@
 
 #include "ash/services/secure_channel/nearby_connection_manager.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/authenticated_channel.h"
 #include "base/containers/contains.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/nearby_connection_manager_impl.cc b/ash/services/secure_channel/nearby_connection_manager_impl.cc
index ef75600..0c41889 100644
--- a/ash/services/secure_channel/nearby_connection_manager_impl.cc
+++ b/ash/services/secure_channel/nearby_connection_manager_impl.cc
@@ -4,13 +4,13 @@
 
 #include "ash/services/secure_channel/nearby_connection_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/authenticated_channel_impl.h"
 #include "ash/services/secure_channel/nearby_connection.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "ash/services/secure_channel/secure_channel_disconnector.h"
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc b/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc
index 2441c12..360c8cc 100644
--- a/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc
+++ b/ash/services/secure_channel/nearby_connection_manager_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/authenticated_channel_impl.h"
 #include "ash/services/secure_channel/fake_authenticated_channel.h"
 #include "ash/services/secure_channel/fake_ble_scanner.h"
@@ -16,7 +17,6 @@
 #include "ash/services/secure_channel/public/cpp/client/fake_nearby_connector.h"
 #include "ash/services/secure_channel/secure_channel.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/nearby_connection_unittest.cc b/ash/services/secure_channel/nearby_connection_unittest.cc
index 60e16fdc..1fe0024 100644
--- a/ash/services/secure_channel/nearby_connection_unittest.cc
+++ b/ash/services/secure_channel/nearby_connection_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/secure_channel/nearby_connection.h"
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/connection_observer.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_nearby_connector.h"
@@ -15,7 +16,6 @@
 #include "base/files/file_util.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/pending_ble_initiator_connection_request.cc b/ash/services/secure_channel/pending_ble_initiator_connection_request.cc
index 586e93a..c3a889b 100644
--- a/ash/services/secure_channel/pending_ble_initiator_connection_request.cc
+++ b/ash/services/secure_channel/pending_ble_initiator_connection_request.cc
@@ -6,11 +6,11 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/client_connection_parameters.h"
 #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/pending_ble_listener_connection_request.cc b/ash/services/secure_channel/pending_ble_listener_connection_request.cc
index eed4cf9..92b72a5 100644
--- a/ash/services/secure_channel/pending_ble_listener_connection_request.cc
+++ b/ash/services/secure_channel/pending_ble_listener_connection_request.cc
@@ -6,10 +6,10 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/client_connection_parameters.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/pending_connection_request_base.h b/ash/services/secure_channel/pending_connection_request_base.h
index 3549833..8e5c31bc 100644
--- a/ash/services/secure_channel/pending_connection_request_base.h
+++ b/ash/services/secure_channel/pending_connection_request_base.h
@@ -7,13 +7,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/client_connection_parameters.h"
 #include "ash/services/secure_channel/pending_connection_request.h"
 #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc b/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc
index 6009ba7..e933d71a 100644
--- a/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc
+++ b/ash/services/secure_channel/pending_nearby_initiator_connection_request.cc
@@ -4,9 +4,9 @@
 
 #include "ash/services/secure_channel/pending_nearby_initiator_connection_request.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/presence_monitor_delegate.cc b/ash/services/secure_channel/presence_monitor_delegate.cc
index e2955c9..93426294 100644
--- a/ash/services/secure_channel/presence_monitor_delegate.cc
+++ b/ash/services/secure_channel/presence_monitor_delegate.cc
@@ -4,13 +4,13 @@
 
 #include "ash/services/secure_channel/presence_monitor_delegate.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_cache.h"
 #include "ash/services/secure_channel/ble_scanner_impl.h"
 #include "ash/services/secure_channel/ble_synchronizer.h"
 #include "ash/services/secure_channel/bluetooth_helper_impl.h"
 #include "ash/services/secure_channel/connection_role.h"
 #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/presence_monitor_delegate.h b/ash/services/secure_channel/presence_monitor_delegate.h
index f7d6b67..03a6cba 100644
--- a/ash/services/secure_channel/presence_monitor_delegate.h
+++ b/ash/services/secure_channel/presence_monitor_delegate.h
@@ -5,12 +5,12 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_PRESENCE_MONITOR_DELEGATE_H_
 #define ASH_SERVICES_SECURE_CHANNEL_PRESENCE_MONITOR_DELEGATE_H_
 
+// TODO(https://crbug.com/1164001): move to forward declaration.
+#include "ash/components/multidevice/remote_device_cache.h"
+// TODO(https://crbug.com/1164001): move to forward declaration.
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/ble_scanner.h"
 #include "ash/services/secure_channel/public/cpp/shared/presence_monitor.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_cache.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace device {
 class BluetoothAdapter;
diff --git a/ash/services/secure_channel/presence_monitor_impl.cc b/ash/services/secure_channel/presence_monitor_impl.cc
index 2de86dc..679795c 100644
--- a/ash/services/secure_channel/presence_monitor_impl.cc
+++ b/ash/services/secure_channel/presence_monitor_impl.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/secure_channel/presence_monitor_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/presence_monitor_delegate.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 
diff --git a/ash/services/secure_channel/public/cpp/client/BUILD.gn b/ash/services/secure_channel/public/cpp/client/BUILD.gn
index 5dac9dc..759c0a8 100644
--- a/ash/services/secure_channel/public/cpp/client/BUILD.gn
+++ b/ash/services/secure_channel/public/cpp/client/BUILD.gn
@@ -31,13 +31,13 @@
   ]
 
   deps = [
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/services/device_sync/public/cpp",
     "//ash/services/multidevice_setup/public/cpp",
     "//ash/services/secure_channel/public/cpp/shared",
     "//ash/services/secure_channel/public/mojom",
     "//base",
-    "//chromeos/components/multidevice/logging",
     "//mojo/public/cpp/bindings",
   ]
 }
@@ -80,6 +80,8 @@
   deps = [
     ":client",
     ":test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/constants",
     "//ash/services/device_sync/public/cpp:test_support",
     "//ash/services/multidevice_setup/public/cpp:test_support",
@@ -89,8 +91,6 @@
     "//ash/services/secure_channel/public/mojom",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc b/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc
index 6a745d2..caa5e35d 100644
--- a/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc
+++ b/ash/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/fake_channel.h"
 #include "ash/services/secure_channel/fake_secure_channel.h"
 #include "ash/services/secure_channel/public/cpp/client/client_channel_impl.h"
@@ -30,7 +31,6 @@
 #include "base/test/null_task_runner.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_simple_task_runner.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/secure_channel/public/cpp/client/connection_attempt.h b/ash/services/secure_channel/public/cpp/client/connection_attempt.h
index 1985ad69..1a758fd 100644
--- a/ash/services/secure_channel/public/cpp/client/connection_attempt.h
+++ b/ash/services/secure_channel/public/cpp/client/connection_attempt.h
@@ -5,8 +5,8 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_CONNECTION_ATTEMPT_H_
 #define ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_CONNECTION_ATTEMPT_H_
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc b/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc
index acd16bb..49144c8 100644
--- a/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc
+++ b/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
@@ -20,7 +21,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
 #include "base/timer/mock_timer.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc b/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc
index 6f5739d81..b7ef619c 100644
--- a/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc
+++ b/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.cc
@@ -4,7 +4,7 @@
 
 #include "ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
 
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h b/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h
index 48a462d2..e2af3f90b0 100644
--- a/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h
+++ b/ash/services/secure_channel/public/cpp/client/presence_monitor_client.h
@@ -5,9 +5,9 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_PRESENCE_MONITOR_CLIENT_H_
 #define ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_CLIENT_PRESENCE_MONITOR_CLIENT_H_
 
-#include "ash/services/secure_channel/public/cpp/shared/presence_monitor.h"
 // TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/services/secure_channel/public/cpp/shared/presence_monitor.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/public/cpp/client/secure_channel_client.h b/ash/services/secure_channel/public/cpp/client/secure_channel_client.h
index 164db07..6968f492 100644
--- a/ash/services/secure_channel/public/cpp/client/secure_channel_client.h
+++ b/ash/services/secure_channel/public/cpp/client/secure_channel_client.h
@@ -8,7 +8,7 @@
 #include <string>
 
 // TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc b/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc
index 6166bb6..acedfe56 100644
--- a/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc
+++ b/ash/services/secure_channel/public/cpp/client/secure_channel_client_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/services/secure_channel/public/cpp/client/secure_channel_client_impl.h"
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/fake_channel.h"
 #include "ash/services/secure_channel/fake_secure_channel.h"
 #include "ash/services/secure_channel/public/cpp/client/client_channel_impl.h"
@@ -20,7 +21,6 @@
 #include "base/test/null_task_runner.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_simple_task_runner.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/services/secure_channel/public/cpp/shared/BUILD.gn b/ash/services/secure_channel/public/cpp/shared/BUILD.gn
index 522d06f..9fd079ae 100644
--- a/ash/services/secure_channel/public/cpp/shared/BUILD.gn
+++ b/ash/services/secure_channel/public/cpp/shared/BUILD.gn
@@ -16,11 +16,10 @@
   ]
 
   public_deps = [
-    "//base",
-
     # TODO(https://crbug.com/1164001): remove dep. when multidevice is migrated
     # to namespace ash and the included header can be changed to a forward
     # declaration.
-    "//chromeos/components/multidevice",
+    "//ash/components/multidevice",
+    "//base",
   ]
 }
diff --git a/ash/services/secure_channel/public/cpp/shared/presence_monitor.h b/ash/services/secure_channel/public/cpp/shared/presence_monitor.h
index 14f001f..4d85e12 100644
--- a/ash/services/secure_channel/public/cpp/shared/presence_monitor.h
+++ b/ash/services/secure_channel/public/cpp/shared/presence_monitor.h
@@ -5,10 +5,9 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_SHARED_PRESENCE_MONITOR_H_
 #define ASH_SERVICES_SECURE_CHANNEL_PUBLIC_CPP_SHARED_PRESENCE_MONITOR_H_
 
-#include "base/callback.h"
-
 // TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device.h"
+#include "base/callback.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/public/mojom/BUILD.gn b/ash/services/secure_channel/public/mojom/BUILD.gn
index 400ffd73..8db7f85be 100644
--- a/ash/services/secure_channel/public/mojom/BUILD.gn
+++ b/ash/services/secure_channel/public/mojom/BUILD.gn
@@ -15,7 +15,7 @@
   ]
 
   public_deps = [
-    "//chromeos/components/multidevice/mojom",
+    "//ash/components/multidevice/mojom",
     "//mojo/public/mojom/base",
   ]
 
diff --git a/ash/services/secure_channel/public/mojom/secure_channel.mojom b/ash/services/secure_channel/public/mojom/secure_channel.mojom
index 4d3d4084..56b657e 100644
--- a/ash/services/secure_channel/public/mojom/secure_channel.mojom
+++ b/ash/services/secure_channel/public/mojom/secure_channel.mojom
@@ -4,9 +4,9 @@
 
 module ash.secure_channel.mojom;
 
+import "ash/components/multidevice/mojom/multidevice_types.mojom";
 import "ash/services/secure_channel/public/mojom/nearby_connector.mojom";
 import "ash/services/secure_channel/public/mojom/secure_channel_types.mojom";
-import "chromeos/components/multidevice/mojom/multidevice_types.mojom";
 
 enum ConnectionAttemptFailureReason {
   // The local device could not authenticate with the remote device. This likely
diff --git a/ash/services/secure_channel/secure_channel.cc b/ash/services/secure_channel/secure_channel.cc
index 91ea806..92e4f27 100644
--- a/ash/services/secure_channel/secure_channel.cc
+++ b/ash/services/secure_channel/secure_channel.cc
@@ -6,14 +6,14 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/secure_channel/file_transfer_update_callback.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
 #include "ash/services/secure_channel/wire_message.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/secure_channel.h b/ash/services/secure_channel/secure_channel.h
index 01f6acf..bea0d4fb 100644
--- a/ash/services/secure_channel/secure_channel.h
+++ b/ash/services/secure_channel/secure_channel.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SERVICES_SECURE_CHANNEL_SECURE_CHANNEL_H_
 #define ASH_SERVICES_SECURE_CHANNEL_SECURE_CHANNEL_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/secure_channel/authenticator.h"
 #include "ash/services/secure_channel/connection.h"
 #include "ash/services/secure_channel/connection_observer.h"
@@ -15,7 +16,6 @@
 #include "base/callback.h"
 #include "base/containers/queue.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/secure_channel_disconnector_impl.cc b/ash/services/secure_channel/secure_channel_disconnector_impl.cc
index a1082c5..ec7aa2b 100644
--- a/ash/services/secure_channel/secure_channel_disconnector_impl.cc
+++ b/ash/services/secure_channel/secure_channel_disconnector_impl.cc
@@ -4,8 +4,8 @@
 
 #include "ash/services/secure_channel/secure_channel_disconnector_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc b/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc
index 3bab62e..b26558c4 100644
--- a/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc
+++ b/ash/services/secure_channel/secure_channel_disconnector_impl_unittest.cc
@@ -6,13 +6,13 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/fake_connection.h"
 #include "ash/services/secure_channel/fake_secure_channel_connection.h"
 #include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/unguessable_token.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/secure_channel_impl.cc b/ash/services/secure_channel/secure_channel_impl.cc
index 8d37281..906e5539 100644
--- a/ash/services/secure_channel/secure_channel_impl.cc
+++ b/ash/services/secure_channel/secure_channel_impl.cc
@@ -7,6 +7,7 @@
 #include <ostream>
 #include <sstream>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/active_connection_manager_impl.h"
 #include "ash/services/secure_channel/authenticated_channel.h"
 #include "ash/services/secure_channel/ble_connection_manager_impl.h"
@@ -20,7 +21,6 @@
 #include "ash/services/secure_channel/secure_channel_disconnector_impl.h"
 #include "ash/services/secure_channel/timer_factory_impl.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/secure_channel_impl.h b/ash/services/secure_channel/secure_channel_impl.h
index 466ec956..4dd8d6fd 100644
--- a/ash/services/secure_channel/secure_channel_impl.h
+++ b/ash/services/secure_channel/secure_channel_impl.h
@@ -10,14 +10,14 @@
 #include <tuple>
 #include <vector>
 
+// TODO(https://crbug.com/1164001): move to forward declaration.
+#include "ash/components/multidevice/remote_device_cache.h"
 #include "ash/services/secure_channel/active_connection_manager.h"
 #include "ash/services/secure_channel/connection_attempt_details.h"
 #include "ash/services/secure_channel/pending_connection_manager.h"
 #include "ash/services/secure_channel/public/cpp/shared/connection_priority.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel.mojom.h"
 #include "base/containers/flat_map.h"
-// TODO(https://crbug.com/1164001): move to forward declaration.
-#include "chromeos/components/multidevice/remote_device_cache.h"
 
 namespace device {
 class BluetoothAdapter;
diff --git a/ash/services/secure_channel/secure_channel_initializer.cc b/ash/services/secure_channel/secure_channel_initializer.cc
index 3ebee30b..38bb4ab 100644
--- a/ash/services/secure_channel/secure_channel_initializer.cc
+++ b/ash/services/secure_channel/secure_channel_initializer.cc
@@ -4,10 +4,10 @@
 
 #include "ash/services/secure_channel/secure_channel_initializer.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/secure_channel_impl.h"
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 
diff --git a/ash/services/secure_channel/secure_channel_service_unittest.cc b/ash/services/secure_channel/secure_channel_service_unittest.cc
index 756859a..92f6885 100644
--- a/ash/services/secure_channel/secure_channel_service_unittest.cc
+++ b/ash/services/secure_channel/secure_channel_service_unittest.cc
@@ -4,6 +4,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/secure_channel/active_connection_manager_impl.h"
 #include "ash/services/secure_channel/ble_connection_manager_impl.h"
 #include "ash/services/secure_channel/ble_scanner_impl.h"
@@ -37,8 +39,6 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_simple_task_runner.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
diff --git a/ash/services/secure_channel/secure_channel_unittest.cc b/ash/services/secure_channel/secure_channel_unittest.cc
index 4d5babd..c6139776 100644
--- a/ash/services/secure_channel/secure_channel_unittest.cc
+++ b/ash/services/secure_channel/secure_channel_unittest.cc
@@ -7,6 +7,10 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/fake_secure_message_delegate.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/secure_message_delegate_impl.h"
 #include "ash/services/secure_channel/fake_authenticator.h"
 #include "ash/services/secure_channel/fake_connection.h"
 #include "ash/services/secure_channel/fake_secure_context.h"
@@ -17,10 +21,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/test/bind.h"
-#include "chromeos/components/multidevice/fake_secure_message_delegate.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::secure_channel {
diff --git a/ash/services/secure_channel/shared_resource_scheduler.cc b/ash/services/secure_channel/shared_resource_scheduler.cc
index 85e968dd..52fb4059 100644
--- a/ash/services/secure_channel/shared_resource_scheduler.cc
+++ b/ash/services/secure_channel/shared_resource_scheduler.cc
@@ -4,10 +4,10 @@
 
 #include "ash/services/secure_channel/shared_resource_scheduler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/notreached.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/services/secure_channel/wire_message.cc b/ash/services/secure_channel/wire_message.cc
index 10fafd41..3c0cf14 100644
--- a/ash/services/secure_channel/wire_message.cc
+++ b/ash/services/secure_channel/wire_message.cc
@@ -9,6 +9,7 @@
 
 #include <limits>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/base64url.h"
 #include "base/big_endian.h"
 #include "base/containers/contains.h"
@@ -17,7 +18,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash::secure_channel {
 
diff --git a/ash/shell.cc b/ash/shell.cc
index 463d9ef..7ebc967 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -127,6 +127,7 @@
 #include "ash/system/network/sms_observer.h"
 #include "ash/system/night_light/night_light_controller_impl.h"
 #include "ash/system/pcie_peripheral/pcie_peripheral_notification_controller.h"
+#include "ash/system/power/adaptive_charging_controller.h"
 #include "ash/system/power/backlights_forced_off_setter.h"
 #include "ash/system/power/peripheral_battery_notifier.h"
 #include "ash/system/power/power_button_controller.h"
@@ -739,6 +740,8 @@
   // Accesses root window containers.
   logout_confirmation_controller_.reset();
 
+  adaptive_charging_controller_.reset();
+
   // Drag-and-drop must be canceled prior to close all windows.
   drag_drop_controller_.reset();
 
@@ -1197,6 +1200,11 @@
   AddPreTargetHandler(mouse_cursor_filter_.get(),
                       ui::EventTarget::Priority::kAccessibility);
 
+  if (features::IsAdaptiveChargingEnabled()) {
+    adaptive_charging_controller_ =
+        std::make_unique<AdaptiveChargingController>();
+  }
+
   // Create Controllers that may need root window.
   // TODO(oshima): Move as many controllers before creating
   // RootWindowController as possible.
diff --git a/ash/shell.h b/ash/shell.h
index 9e20e2c..4fa6c2d 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -86,6 +86,7 @@
 class AccessibilityControllerImpl;
 class AccessibilityDelegate;
 class AccessibilityFocusRingControllerImpl;
+class AdaptiveChargingController;
 class AmbientController;
 class AppListControllerImpl;
 class AppListFeatureUsageMetrics;
@@ -742,6 +743,7 @@
   std::unique_ptr<AccessibilityDelegate> accessibility_delegate_;
   std::unique_ptr<AccessibilityFocusRingControllerImpl>
       accessibility_focus_ring_controller_;
+  std::unique_ptr<AdaptiveChargingController> adaptive_charging_controller_;
   std::unique_ptr<AmbientController> ambient_controller_;
   std::unique_ptr<AppListControllerImpl> app_list_controller_;
   std::unique_ptr<AppListFeatureUsageMetrics> app_list_feature_usage_metrics_;
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index d4d5280f..d1e9423 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -16,6 +16,7 @@
 #include "services/device/public/mojom/bluetooth_system.mojom-forward.h"
 #include "services/device/public/mojom/fingerprint.mojom-forward.h"
 #include "services/media_session/public/cpp/media_session_service.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "ui/gfx/native_widget_types.h"
 #include "url/gurl.h"
 
@@ -65,6 +66,11 @@
   virtual std::unique_ptr<DesksTemplatesDelegate> CreateDesksTemplatesDelegate()
       const = 0;
 
+  // Returns the geolocation loader factory used to initialize geolocation
+  // provider.
+  virtual scoped_refptr<network::SharedURLLoaderFactory>
+  GetGeolocationSharedURLLoaderFactory() const = 0;
+
   // Check whether the current tab of the browser window can go back.
   virtual bool CanGoBack(gfx::NativeWindow window) const = 0;
 
diff --git a/ash/system/geolocation/test_geolocation_url_loader_factory.cc b/ash/system/geolocation/test_geolocation_url_loader_factory.cc
new file mode 100644
index 0000000..18b3c9d
--- /dev/null
+++ b/ash/system/geolocation/test_geolocation_url_loader_factory.cc
@@ -0,0 +1,71 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/geolocation/test_geolocation_url_loader_factory.h"
+
+#include <memory>
+
+#include "base/json/json_string_value_serializer.h"
+
+namespace ash {
+
+namespace {
+
+// Creates a serialized dictionary string of the geoposition.
+std::string CreateResponseBody(const Geoposition& position) {
+  base::Value::Dict value;
+  if (position.accuracy)
+    value.Set("accuracy", position.accuracy);
+
+  if (position.latitude && position.longitude) {
+    base::Value location(base::Value::Type::DICTIONARY);
+    location.SetDoubleKey("lat", position.latitude);
+    location.SetDoubleKey("lng", position.longitude);
+    value.Set("location", std::move(location));
+  }
+
+  if (position.error_code) {
+    base::Value error(base::Value::Type::DICTIONARY);
+    error.SetIntKey("error_code", position.error_code);
+    value.Set("error", std::move(error));
+  }
+
+  std::string serialized_response;
+  JSONStringValueSerializer serializer(&serialized_response);
+  serializer.Serialize(value);
+  return serialized_response;
+}
+
+}  // namespace
+
+TestGeolocationUrlLoaderFactory::TestGeolocationUrlLoaderFactory() = default;
+
+void TestGeolocationUrlLoaderFactory::CreateLoaderAndStart(
+    mojo::PendingReceiver<network::mojom::URLLoader> receiver,
+    int32_t request_id,
+    uint32_t options,
+    const network::ResourceRequest& url_request,
+    mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  test_url_loader_factory_.CreateLoaderAndStart(
+      std::move(receiver), request_id, options, url_request, std::move(client),
+      traffic_annotation);
+  test_url_loader_factory_.AddResponse(url_request.url.spec(),
+                                       CreateResponseBody(position_));
+}
+
+void TestGeolocationUrlLoaderFactory::Clone(
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
+  NOTREACHED();
+}
+
+std::unique_ptr<network::PendingSharedURLLoaderFactory>
+TestGeolocationUrlLoaderFactory::Clone() {
+  NOTREACHED();
+  return nullptr;
+}
+
+TestGeolocationUrlLoaderFactory::~TestGeolocationUrlLoaderFactory() = default;
+
+}  // namespace ash
diff --git a/ash/system/geolocation/test_geolocation_url_loader_factory.h b/ash/system/geolocation/test_geolocation_url_loader_factory.h
new file mode 100644
index 0000000..0653ee5
--- /dev/null
+++ b/ash/system/geolocation/test_geolocation_url_loader_factory.h
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_GEOLOCATION_TEST_GEOLOCATION_URL_LOADER_FACTORY_H_
+#define ASH_SYSTEM_GEOLOCATION_TEST_GEOLOCATION_URL_LOADER_FACTORY_H_
+
+#include "ash/components/geolocation/geoposition.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+
+namespace ash {
+
+// A fake SharedURLLoaderFactory that always responds with 200 OK status with a
+// `geoposition_` chosen by the test client.
+class TestGeolocationUrlLoaderFactory : public network::SharedURLLoaderFactory {
+ public:
+  TestGeolocationUrlLoaderFactory();
+
+  TestGeolocationUrlLoaderFactory(const TestGeolocationUrlLoaderFactory&) =
+      delete;
+  TestGeolocationUrlLoaderFactory& operator=(
+      const TestGeolocationUrlLoaderFactory&) = delete;
+
+  // network::SharedURLLoaderFactory
+  void CreateLoaderAndStart(
+      mojo::PendingReceiver<network::mojom::URLLoader> receiver,
+      int32_t request_id,
+      uint32_t options,
+      const network::ResourceRequest& url_request,
+      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
+      override;
+  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
+      override;
+  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override;
+
+  void set_position(Geoposition position) { position_ = position; }
+  const Geoposition& position() const { return position_; }
+
+ protected:
+  ~TestGeolocationUrlLoaderFactory() override;
+
+ private:
+  // Used to control a server response data corresponding to a request url.
+  network::TestURLLoaderFactory test_url_loader_factory_;
+
+  // The geoposition to be responded from this factory when a client makes a
+  // request to any url.
+  Geoposition position_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_GEOLOCATION_TEST_GEOLOCATION_URL_LOADER_FACTORY_H_
diff --git a/ash/system/phonehub/camera_roll_thumbnail.cc b/ash/system/phonehub/camera_roll_thumbnail.cc
index 72a0d88..356b892 100644
--- a/ash/system/phonehub/camera_roll_thumbnail.cc
+++ b/ash/system/phonehub/camera_roll_thumbnail.cc
@@ -4,12 +4,12 @@
 
 #include "ash/system/phonehub/camera_roll_thumbnail.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/camera_roll_manager.h"
 #include "ash/components/phonehub/user_action_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/bind.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/highlight_path_generator.h"
 
diff --git a/ash/system/phonehub/continue_browsing_chip.cc b/ash/system/phonehub/continue_browsing_chip.cc
index 18e601e8..394cd3f 100644
--- a/ash/system/phonehub/continue_browsing_chip.cc
+++ b/ash/system/phonehub/continue_browsing_chip.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/phonehub/continue_browsing_chip.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/user_action_recorder.h"
 #include "ash/public/cpp/new_window_delegate.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -17,7 +18,6 @@
 #include "base/bind.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/focus_ring.h"
diff --git a/ash/system/phonehub/multidevice_feature_opt_in_view.cc b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
index ad71cbb..fc9300d 100644
--- a/ash/system/phonehub/multidevice_feature_opt_in_view.cc
+++ b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/new_window_delegate.h"
@@ -14,7 +15,6 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/phonehub/phone_hub_metrics.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 
 namespace ash {
diff --git a/ash/system/phonehub/phone_hub_notification_controller.cc b/ash/system/phonehub/phone_hub_notification_controller.cc
index 02ea9d8..6a57be5d9 100644
--- a/ash/system/phonehub/phone_hub_notification_controller.cc
+++ b/ash/system/phonehub/phone_hub_notification_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/phonehub/phone_hub_notification_controller.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/notification.h"
 #include "ash/components/phonehub/notification_interaction_handler.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
@@ -27,7 +28,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/timer/timer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/display/types/display_constants.h"
diff --git a/ash/system/power/adaptive_charging_controller.cc b/ash/system/power/adaptive_charging_controller.cc
new file mode 100644
index 0000000..8aa1a08
--- /dev/null
+++ b/ash/system/power/adaptive_charging_controller.cc
@@ -0,0 +1,12 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/power/adaptive_charging_controller.h"
+
+namespace ash {
+
+AdaptiveChargingController::AdaptiveChargingController() = default;
+AdaptiveChargingController::~AdaptiveChargingController() = default;
+
+}  // namespace ash
diff --git a/ash/system/power/adaptive_charging_controller.h b/ash/system/power/adaptive_charging_controller.h
new file mode 100644
index 0000000..c0af8a8
--- /dev/null
+++ b/ash/system/power/adaptive_charging_controller.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_CONTROLLER_H_
+#define ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_CONTROLLER_H_
+
+namespace ash {
+
+// The controller responsible for the adaptive charging toast and notifications,
+// and communication with the power daemon.
+//
+// Is currently a stub. TODO(b:216035280): add in real logic.
+class AdaptiveChargingController {
+ public:
+  AdaptiveChargingController();
+  AdaptiveChargingController(const AdaptiveChargingController&) = delete;
+  AdaptiveChargingController& operator=(const AdaptiveChargingController&) =
+      delete;
+  ~AdaptiveChargingController();
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_CONTROLLER_H_
diff --git a/ash/system/unified/hps_notify_controller.cc b/ash/system/unified/hps_notify_controller.cc
index 4f58533..8d42a55b 100644
--- a/ash/system/unified/hps_notify_controller.cc
+++ b/ash/system/unified/hps_notify_controller.cc
@@ -84,7 +84,7 @@
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
   registry->RegisterBooleanPref(
       prefs::kSnoopingProtectionNotificationSuppressionEnabled,
-      /*default_value=*/false,
+      /*default_value=*/true,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
 }
 
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc
index f4a2ed6..bbcda14 100644
--- a/ash/test_shell_delegate.cc
+++ b/ash/test_shell_delegate.cc
@@ -10,6 +10,7 @@
 #include "ash/capture_mode/test_capture_mode_delegate.h"
 #include "ash/public/cpp/test/test_desks_templates_delegate.h"
 #include "ash/public/cpp/test/test_nearby_share_delegate.h"
+#include "ash/system/geolocation/test_geolocation_url_loader_factory.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/wm/gestures/back_gesture/test_back_gesture_contextual_nudge_delegate.h"
 #include "ui/gfx/image/image.h"
@@ -51,6 +52,12 @@
   return std::make_unique<TestDesksTemplatesDelegate>();
 }
 
+scoped_refptr<network::SharedURLLoaderFactory>
+TestShellDelegate::GetGeolocationSharedURLLoaderFactory() const {
+  return static_cast<scoped_refptr<network::SharedURLLoaderFactory>>(
+      base::MakeRefCounted<TestGeolocationUrlLoaderFactory>());
+}
+
 bool TestShellDelegate::CanGoBack(gfx::NativeWindow window) const {
   return can_go_back_;
 }
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h
index 7f8ef21..10afec5 100644
--- a/ash/test_shell_delegate.h
+++ b/ash/test_shell_delegate.h
@@ -44,6 +44,8 @@
       NearbyShareController* controller) const override;
   std::unique_ptr<DesksTemplatesDelegate> CreateDesksTemplatesDelegate()
       const override;
+  scoped_refptr<network::SharedURLLoaderFactory>
+  GetGeolocationSharedURLLoaderFactory() const override;
   bool CanGoBack(gfx::NativeWindow window) const override;
   void SetTabScrubberChromeOSEnabled(bool enabled) override;
   bool ShouldWaitForTouchPressAck(gfx::NativeWindow window) override;
diff --git a/ash/webui/eche_app_ui/BUILD.gn b/ash/webui/eche_app_ui/BUILD.gn
index a1b621c..dcb6a24 100644
--- a/ash/webui/eche_app_ui/BUILD.gn
+++ b/ash/webui/eche_app_ui/BUILD.gn
@@ -76,6 +76,8 @@
   deps = [
     ":eche_app_ui_pref",
     "//ash",
+    "//ash/components/multidevice:multidevice",
+    "//ash/components/multidevice/logging",
     "//ash/components/phonehub",
     "//ash/constants",
     "//ash/public/cpp",
@@ -88,8 +90,6 @@
     "//ash/webui/eche_app_ui/proto",
     "//ash/webui/resources:eche_app_resources",
     "//ash/webui/resources:eche_bundle_resources",
-    "//chromeos/components/multidevice:multidevice",
-    "//chromeos/components/multidevice/logging",
     "//components/prefs",
     "//content/public/browser",
     "//mojo/public/js:resources",
@@ -145,6 +145,8 @@
     ":eche_app_ui_pref",
     ":test_support",
     "//ash:test_support",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:test_support",
     "//ash/components/phonehub",
     "//ash/components/phonehub:debug",
     "//ash/public/cpp",
@@ -159,8 +161,6 @@
     "//ash/webui/eche_app_ui/proto",
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:test_support",
     "//components/prefs:test_support",
     "//components/sync_preferences:test_support",
     "//dbus",
diff --git a/ash/webui/eche_app_ui/apps_access_manager.cc b/ash/webui/eche_app_ui/apps_access_manager.cc
index b434d88..fd41607 100644
--- a/ash/webui/eche_app_ui/apps_access_manager.cc
+++ b/ash/webui/eche_app_ui/apps_access_manager.cc
@@ -4,10 +4,10 @@
 
 #include "ash/webui/eche_app_ui/apps_access_manager.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
 #include "base/memory/ptr_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/apps_access_manager_impl.cc b/ash/webui/eche_app_ui/apps_access_manager_impl.cc
index 016d80a..aa1860d 100644
--- a/ash/webui/eche_app_ui/apps_access_manager_impl.cc
+++ b/ash/webui/eche_app_ui/apps_access_manager_impl.cc
@@ -4,12 +4,12 @@
 
 #include "ash/webui/eche_app_ui/apps_access_manager_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "ash/webui/eche_app_ui/pref_names.h"
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/ash/webui/eche_app_ui/eche_app_manager_unittest.cc b/ash/webui/eche_app_ui/eche_app_manager_unittest.cc
index 7525f8c..c981523 100644
--- a/ash/webui/eche_app_ui/eche_app_manager_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_app_manager_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/phonehub/fake_phone_hub_manager.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
@@ -20,8 +22,6 @@
 #include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/dbus/fake_bluetooth_debug_manager_client.h"
diff --git a/ash/webui/eche_app_ui/eche_connector_impl.cc b/ash/webui/eche_app_ui/eche_connector_impl.cc
index dead723f..917b18ff 100644
--- a/ash/webui/eche_app_ui/eche_connector_impl.cc
+++ b/ash/webui/eche_app_ui/eche_connector_impl.cc
@@ -4,13 +4,13 @@
 
 #include "ash/webui/eche_app_ui/eche_connector_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/services/secure_channel/public/cpp/client/connection_manager.h"
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_display_stream_handler.cc b/ash/webui/eche_app_ui/eche_display_stream_handler.cc
index 7d2f1ee..0618a38 100644
--- a/ash/webui/eche_app_ui/eche_display_stream_handler.cc
+++ b/ash/webui/eche_app_ui/eche_display_stream_handler.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/eche_app_ui/eche_display_stream_handler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/webui/eche_app_ui/launch_app_helper.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_feature_status_provider.cc b/ash/webui/eche_app_ui/eche_feature_status_provider.cc
index 780bf76..27217aa7 100644
--- a/ash/webui/eche_app_ui/eche_feature_status_provider.cc
+++ b/ash/webui/eche_app_ui/eche_feature_status_provider.cc
@@ -4,13 +4,13 @@
 
 #include "ash/webui/eche_app_ui/eche_feature_status_provider.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/components/phonehub/feature_status.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc b/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc
index abbe884..799dae75 100644
--- a/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc
@@ -7,13 +7,13 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/phonehub/fake_phone_hub_manager.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_connection_manager.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/webui/eche_app_ui/eche_notification_click_handler.cc b/ash/webui/eche_app_ui/eche_notification_click_handler.cc
index 6b58bbe8..ffd190e 100644
--- a/ash/webui/eche_app_ui/eche_notification_click_handler.cc
+++ b/ash/webui/eche_app_ui/eche_notification_click_handler.cc
@@ -4,13 +4,13 @@
 
 #include "ash/webui/eche_app_ui/eche_notification_click_handler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/system/eche/eche_tray.h"
 #include "ash/webui/eche_app_ui/launch_app_helper.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_notification_generator.cc b/ash/webui/eche_app_ui/eche_notification_generator.cc
index 7b184a5..b395d46 100644
--- a/ash/webui/eche_app_ui/eche_notification_generator.cc
+++ b/ash/webui/eche_app_ui/eche_notification_generator.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/eche_app_ui/eche_notification_generator.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/webui/eche_app_ui/launch_app_helper.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_presence_manager.cc b/ash/webui/eche_app_ui/eche_presence_manager.cc
index 79b75a9..8ed50fd 100644
--- a/ash/webui/eche_app_ui/eche_presence_manager.cc
+++ b/ash/webui/eche_app_ui/eche_presence_manager.cc
@@ -4,12 +4,12 @@
 
 #include "ash/webui/eche_app_ui/eche_presence_manager.h"
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/secure_channel/public/cpp/client/presence_monitor_client.h"
 #include "ash/webui/eche_app_ui/eche_connector.h"
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
index 0c12c82..e54ba588 100644
--- a/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/webui/eche_app_ui/eche_presence_manager.h"
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
@@ -14,7 +15,6 @@
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
index ae5fe2c..9198674 100644
--- a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
+++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
@@ -4,12 +4,12 @@
 
 #include "ash/webui/eche_app_ui/eche_recent_app_click_handler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/system/eche/eche_tray.h"
 #include "ash/webui/eche_app_ui/launch_app_helper.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_signaler.cc b/ash/webui/eche_app_ui/eche_signaler.cc
index 9e4bdfc..6f0680a0 100644
--- a/ash/webui/eche_app_ui/eche_signaler.cc
+++ b/ash/webui/eche_app_ui/eche_signaler.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/eche_app_ui/eche_signaler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace eche_app {
diff --git a/ash/webui/eche_app_ui/eche_signaler_unittest.cc b/ash/webui/eche_app_ui/eche_signaler_unittest.cc
index e4581534..4fa274fe 100644
--- a/ash/webui/eche_app_ui/eche_signaler_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_signaler_unittest.cc
@@ -7,11 +7,11 @@
 #include <memory>
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/public/cpp/client/fake_connection_manager.h"
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/task_environment.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/ash/webui/eche_app_ui/eche_uid_provider.cc b/ash/webui/eche_app_ui/eche_uid_provider.cc
index 5fec7cd..af83df8a 100644
--- a/ash/webui/eche_app_ui/eche_uid_provider.cc
+++ b/ash/webui/eche_app_ui/eche_uid_provider.cc
@@ -8,8 +8,8 @@
 #include <openssl/base64.h>
 #include <cstring>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/check.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_service.h"
 #include "crypto/random.h"
 
diff --git a/ash/webui/eche_app_ui/system_info_provider.cc b/ash/webui/eche_app_ui/system_info_provider.cc
index 46fe1c7..9878cc20 100644
--- a/ash/webui/eche_app_ui/system_info_provider.cc
+++ b/ash/webui/eche_app_ui/system_info_provider.cc
@@ -4,6 +4,7 @@
 
 #include "ash/webui/eche_app_ui/system_info_provider.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/tablet_mode.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
@@ -12,7 +13,6 @@
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 
 namespace ash {
diff --git a/ash/webui/multidevice_debug/BUILD.gn b/ash/webui/multidevice_debug/BUILD.gn
index 1ffd71f..e6dc35a6 100644
--- a/ash/webui/multidevice_debug/BUILD.gn
+++ b/ash/webui/multidevice_debug/BUILD.gn
@@ -17,6 +17,7 @@
   ]
 
   deps = [
+    "//ash/components/multidevice/logging",
     "//ash/constants",
     "//ash/services/device_sync/proto",
     "//ash/services/device_sync/proto:util",
@@ -27,7 +28,6 @@
     "//ash/webui/resources:multidevice_debug_resources",
     "//base",
     "//base:i18n",
-    "//chromeos/components/multidevice/logging",
     "//components/prefs",
     "//components/resources",
     "//content/public/browser",
diff --git a/ash/webui/multidevice_debug/OWNERS b/ash/webui/multidevice_debug/OWNERS
index 7027ab73..18377141 100644
--- a/ash/webui/multidevice_debug/OWNERS
+++ b/ash/webui/multidevice_debug/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc b/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc
index 9b50a8cf..0f9bf7f4 100644
--- a/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc
+++ b/ash/webui/multidevice_debug/proximity_auth_webui_handler.cc
@@ -9,6 +9,8 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/services/device_sync/proto/enum_util.h"
 #include "base/base64url.h"
 #include "base/bind.h"
@@ -17,8 +19,6 @@
 #include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_ui.h"
diff --git a/ash/webui/multidevice_debug/proximity_auth_webui_handler.h b/ash/webui/multidevice_debug/proximity_auth_webui_handler.h
index ce651772..eb12f15 100644
--- a/ash/webui/multidevice_debug/proximity_auth_webui_handler.h
+++ b/ash/webui/multidevice_debug/proximity_auth_webui_handler.h
@@ -5,11 +5,11 @@
 #ifndef ASH_WEBUI_MULTIDEVICE_DEBUG_PROXIMITY_AUTH_WEBUI_HANDLER_H_
 #define ASH_WEBUI_MULTIDEVICE_DEBUG_PROXIMITY_AUTH_WEBUI_HANDLER_H_
 
+#include "ash/components/multidevice/logging/log_buffer.h"
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/log_buffer.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
diff --git a/ash/webui/multidevice_debug/resources/proximity_auth.html b/ash/webui/multidevice_debug/resources/proximity_auth.html
index 1a5beb2..19340f37 100644
--- a/ash/webui/multidevice_debug/resources/proximity_auth.html
+++ b/ash/webui/multidevice_debug/resources/proximity_auth.html
@@ -15,7 +15,7 @@
   </script>
   <script src="chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js">
   </script>
-  <script src="chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js">
+  <script src="chrome://resources/mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js">
   </script>
   <script src="chrome://resources/mojo/ash/services/device_sync/public/mojom/device_sync.mojom-lite.js">
   </script>
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
index d637089..5b8b207b 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -369,7 +369,7 @@
   }
 
   getImageUrlForEmptyTile_(tile: ImageTile): string {
-    return `//personalization/common/${
+    return `chrome://personalization/common/${
         (this.isGooglePhotosTile_(tile) ? 'google_photos.svg' :
                                           'no_images.svg')}`;
   }
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 9b6d841..3b50e99 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -49,7 +49,6 @@
 #include "base/containers/contains.h"
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/cxx17_backports.h"
-#include "base/guid.h"
 #include "base/i18n/number_formatting.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -266,12 +265,6 @@
   int count_ = 0;
 };
 
-DesksController::Call::Call()
-    : data(std::make_unique<app_restore::RestoreData>()) {}
-DesksController::Call::Call(DesksController::Call&&) = default;
-DesksController::Call& DesksController::Call::operator=(Call&&) = default;
-DesksController::Call::~Call() = default;
-
 DesksController::DesksController()
     : metrics_helper_(std::make_unique<DeskTraversalsMetricsHelper>(this)) {
   Shell::Get()->activation_client()->AddObserver(this);
@@ -914,54 +907,9 @@
     aura::Window* root_window_to_show) const {
   DCHECK(current_account_id_.is_valid());
 
-  // Construct RestoreData for |desk_template|.
-  const auto current_serial = serial_++;
-  auto emplace_result = calls_.emplace(current_serial, Call{});
-  DCHECK(emplace_result.second);
-  Call& call = emplace_result.first->second;
-
-  auto* shell = Shell::Get();
-  auto mru_windows =
-      shell->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
-  auto* delegate = shell->desks_templates_delegate();
-  for (auto* window : mru_windows) {
-    if (!delegate->IsWindowSupportedForDeskTemplate(window) &&
-        !wm::GetTransientParent(window)) {
-      call.unsupported_apps.push_back(window);
-      continue;
-    }
-
-    // Exclude window that does not asscociate with a full restore app id,
-    // silently omitting them.
-    const std::string app_id = full_restore::GetAppId(window);
-    if (app_id.empty())
-      continue;
-
-    // We need to copy |app_launch_info->app_id| to |app_id| as the below
-    // function AddAppLaunchInfo() will destroy |app_launch_info|.
-    const int32_t window_id = window->GetProperty(app_restore::kWindowIdKey);
-    std::unique_ptr<app_restore::WindowInfo> window_info = BuildWindowInfo(
-        window, /*activation_index=*/absl::nullopt, mru_windows);
-    // Clear WindowInfo's `desk_id` in the template. It will later be set to the
-    // id of a newly created desk when launching.
-    window_info->desk_id.reset();
-
-    // The delegate may call back OnAppLaunchDataReceived() synchronously, which
-    // will decrement the counter, that is why we should increment it before
-    // placing the call.
-    ++call.pending_request_count;
-    delegate->GetAppLaunchDataForDeskTemplate(
-        window, base::BindOnce(&DesksController::OnAppLaunchDataReceived,
-                               base::Unretained(this), current_serial, app_id,
-                               window_id, std::move(window_info)));
-  }
-
-  call.callback = std::move(callback);
-
-  // If all requests in the loop above returned data synchronously, then we have
-  // no pending requests and send the data right away.
-  if (call.pending_request_count == 0)
-    SendRestoreData(current_serial, root_window_to_show);
+  restore_data_collector_.CaptureActiveDeskAsTemplate(
+      std::move(callback), base::UTF16ToUTF8(active_desk_->name()),
+      root_window_to_show);
 }
 
 void DesksController::CreateAndActivateNewDeskForTemplate(
@@ -1633,63 +1581,4 @@
       &DesksController::RecordAndResetNumberOfWeeklyActiveDesks);
 }
 
-void DesksController::OnAppLaunchDataReceived(
-    uint32_t serial,
-    const std::string app_id,
-    const int32_t window_id,
-    std::unique_ptr<app_restore::WindowInfo> window_info,
-    std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info) const {
-  auto call_it = calls_.find(serial);
-  DCHECK(call_it != calls_.end());
-  Call& call = call_it->second;
-
-  DCHECK(call.data);
-  DCHECK_GT(call.pending_request_count, 0u);
-
-  --call.pending_request_count;
-
-  // nullptr means that this app does not have data to save.
-  if (app_launch_info) {
-    call.data->AddAppLaunchInfo(std::move(app_launch_info));
-    call.data->ModifyWindowInfo(app_id, window_id, *window_info);
-  }
-
-  // Null callback here means that the loop in CaptureActiveDeskAsTemplate() has
-  // not yet finished polling the windows.  Non-zero pending request count means
-  // that some of preceding requests were asynchronous.
-  if (call.pending_request_count > 0 || call.callback.is_null())
-    return;
-
-  // TODO(crbug.com/1268741): get a better root window.  Originally it is taken
-  // from the desk template UI that is active when the user clicks the button,
-  // but here we are in the asynchronous handler, and that window may have been
-  // destroyed already.
-  SendRestoreData(serial, Shell::Get()->GetPrimaryRootWindow());
-}
-
-void DesksController::SendRestoreData(uint32_t serial,
-                                      aura::Window* root_window_to_show) const {
-  auto call_it = calls_.find(serial);
-  DCHECK(call_it != calls_.end());
-  Call& call = call_it->second;
-
-  auto desk_template = std::make_unique<DeskTemplate>(
-      base::GUID::GenerateRandomV4().AsLowercaseString(),
-      DeskTemplateSource::kUser, base::UTF16ToUTF8(active_desk_->name()),
-      base::Time::Now());
-  desk_template->set_desk_restore_data(std::move(call.data));
-
-  if (!call.unsupported_apps.empty() &&
-      Shell::Get()->overview_controller()->InOverviewSession()) {
-    // There were some unsupported apps in the active desk so open up a dialog
-    // to let the user know.
-    DesksTemplatesDialogController::Get()->ShowUnsupportedAppsDialog(
-        root_window_to_show, call.unsupported_apps, std::move(call.callback),
-        std::move(desk_template));
-  } else {
-    std::move(call.callback).Run(std::move(desk_template));
-  }
-  calls_.erase(serial);
-}
-
 }  // namespace ash
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index f21214b..d74a686 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -14,6 +14,7 @@
 #include "ash/public/cpp/session/session_observer.h"
 #include "ash/wm/desks/desks_histogram_enums.h"
 #include "ash/wm/desks/root_window_desk_switch_animator.h"
+#include "ash/wm/desks/templates/restore_data_collector.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/observer_list.h"
@@ -309,19 +310,6 @@
   friend class DeskRemovalAnimation;
   friend class DesksTemplatesTest;
 
-  // Keeps the state for the asynchronous call for AppLaunchData to the clients.
-  struct Call {
-    Call();
-    Call(Call&&);
-    Call& operator=(Call&&);
-    ~Call();
-
-    std::vector<aura::Window*> unsupported_apps;
-    std::unique_ptr<app_restore::RestoreData> data;
-    uint32_t pending_request_count = 0;
-    GetDeskTemplateCallback callback;
-  };
-
   void set_disable_app_id_check_for_desk_templates(
       bool disable_app_id_check_for_desk_templates) {
     disable_app_id_check_for_desk_templates_ =
@@ -379,23 +367,6 @@
   // |interacted_with_this_week_| field for each inactive desk in |desks_|.
   void RecordAndResetNumberOfWeeklyActiveDesks();
 
-  // Receives the AppLaunchInfo from the single client and puts it into the
-  // RestoreData record where data from all clients is accumulated.  If all data
-  // is collected, invokes SendAppLaunchData().
-  // TODO(crbug.com/1268741): extract this, together with the `calls_` and the
-  // relevant methods, into a separate class.
-  void OnAppLaunchDataReceived(
-      uint32_t serial,
-      const std::string app_id,
-      const int32_t window_id,
-      std::unique_ptr<app_restore::WindowInfo> window_info,
-      std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info) const;
-
-  // Sends the RestoreData to the consumer after all clients deliver their
-  // AppLaunchInfo.
-  void SendRestoreData(uint32_t serial,
-                       aura::Window* root_window_to_show) const;
-
   std::vector<std::unique_ptr<Desk>> desks_;
 
   Desk* active_desk_ = nullptr;
@@ -441,12 +412,8 @@
   // Scheduler for reporting the weekly active desks metric.
   base::OneShotTimer weekly_active_desks_scheduler_;
 
-  // Data to put into desk template.  Mutable because it is not part of the
-  // state but it can live between asynchronous calls.
-  // Because gathering the data is asynchronous, we maintain a map of requests
-  // identified by serial number of the request that comes from the UI.
-  mutable uint32_t serial_ = 0;
-  mutable base::flat_map<uint32_t, Call> calls_;
+  // Does the job for the `CaptureActiveDeskAsTemplate()` method.
+  mutable RestoreDataCollector restore_data_collector_;
 };
 
 }  // namespace ash
diff --git a/ash/wm/desks/templates/desks_templates_dialog_controller.cc b/ash/wm/desks/templates/desks_templates_dialog_controller.cc
index bac4b5de..c65e1ed1 100644
--- a/ash/wm/desks/templates/desks_templates_dialog_controller.cc
+++ b/ash/wm/desks/templates/desks_templates_dialog_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/desks/templates/desks_templates_dialog_controller.h"
 
+#include "ash/constants/app_types.h"
 #include "ash/public/cpp/desks_templates_delegate.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -15,6 +16,7 @@
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "base/bind.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -151,10 +153,17 @@
   unsupported_apps_template_ = std::move(desk_template);
 
   size_t incognito_window_count = 0;
+  bool contains_lacros_window = false;
   auto* delegate = Shell::Get()->desks_templates_delegate();
   // TODO(shidi): The caller of  ShowUnsupportedAppsDialog should provide us
   // with the incognito window count to avoid double looping.
   for (auto* window : unsupported_apps) {
+    if (static_cast<AppType>(window->GetProperty(aura::client::kAppType)) ==
+        AppType::LACROS) {
+      contains_lacros_window = true;
+      break;
+    }
+
     if (delegate->IsIncognitoWindow(window))
       ++incognito_window_count;
   }
@@ -163,7 +172,10 @@
   // are linux apps.
   std::u16string app_description;
   int app_description_id;
-  if (incognito_window_count == 0) {
+  if (contains_lacros_window) {
+    app_description_id =
+        IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION;
+  } else if (incognito_window_count == 0) {
     app_description_id =
         IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LINUX_APPS_DIALOG_DESCRIPTION;
   } else if (incognito_window_count != unsupported_apps.size()) {
diff --git a/ash/wm/desks/templates/restore_data_collector.cc b/ash/wm/desks/templates/restore_data_collector.cc
new file mode 100644
index 0000000..a38957b
--- /dev/null
+++ b/ash/wm/desks/templates/restore_data_collector.cc
@@ -0,0 +1,146 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/desks/templates/restore_data_collector.h"
+
+#include "ash/public/cpp/desks_templates_delegate.h"
+#include "ash/shell.h"
+#include "ash/wm/desks/templates/desks_templates_dialog_controller.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/window_restore/window_restore_util.h"
+#include "ash/wm/window_util.h"
+#include "base/guid.h"
+#include "components/app_restore/app_launch_info.h"
+#include "components/app_restore/full_restore_utils.h"
+#include "components/app_restore/restore_data.h"
+#include "components/app_restore/window_info.h"
+#include "components/app_restore/window_properties.h"
+
+namespace ash {
+
+RestoreDataCollector::Call::Call()
+    : data(std::make_unique<app_restore::RestoreData>()) {}
+RestoreDataCollector::Call::Call(RestoreDataCollector::Call&&) = default;
+RestoreDataCollector::Call& RestoreDataCollector::Call::operator=(Call&&) =
+    default;
+RestoreDataCollector::Call::~Call() = default;
+
+RestoreDataCollector::RestoreDataCollector() = default;
+RestoreDataCollector::~RestoreDataCollector() = default;
+
+void RestoreDataCollector::CaptureActiveDeskAsTemplate(
+    GetDeskTemplateCallback callback,
+    const std::string& template_name,
+    aura::Window* root_window_to_show) {
+  const auto current_serial = serial_++;
+  auto emplace_result = calls_.emplace(current_serial, Call{});
+  DCHECK(emplace_result.second);
+  Call& call = emplace_result.first->second;
+
+  if (root_window_to_show)
+    window_tracker_.Add(root_window_to_show);
+  call.root_window_to_show = root_window_to_show;
+  call.template_name = template_name;
+
+  auto* const shell = Shell::Get();
+  auto mru_windows =
+      shell->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
+  auto* delegate = shell->desks_templates_delegate();
+  for (auto* window : mru_windows) {
+    if (!delegate->IsWindowSupportedForDeskTemplate(window) &&
+        !wm::GetTransientParent(window)) {
+      call.unsupported_apps.push_back(window);
+      continue;
+    }
+
+    // Skip windows that do not associate with a full restore app id.
+    const std::string app_id = full_restore::GetAppId(window);
+    if (app_id.empty())
+      continue;
+
+    const int32_t window_id = window->GetProperty(app_restore::kWindowIdKey);
+    std::unique_ptr<app_restore::WindowInfo> window_info = BuildWindowInfo(
+        window, /*activation_index=*/absl::nullopt, mru_windows);
+    // Clear the desk ID in the WindowInfo that is to be stored in the template.
+    // It will be set to the ID of a newly created desk when launching.
+    window_info->desk_id.reset();
+
+    ++call.pending_request_count;
+    delegate->GetAppLaunchDataForDeskTemplate(
+        window, base::BindOnce(&RestoreDataCollector::OnAppLaunchDataReceived,
+                               base::Unretained(this), current_serial, app_id,
+                               window_id, std::move(window_info)));
+  }
+
+  call.callback = std::move(callback);
+
+  // If all requests in the loop above returned data synchronously, then we have
+  // no pending requests and send the data right away.  Otherwise it will be
+  // sent after the last pending request is handled.
+  if (call.pending_request_count == 0)
+    SendDeskTemplate(current_serial);
+}
+
+void RestoreDataCollector::OnAppLaunchDataReceived(
+    uint32_t serial,
+    const std::string app_id,
+    const int32_t window_id,
+    std::unique_ptr<app_restore::WindowInfo> window_info,
+    std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info) {
+  auto call_it = calls_.find(serial);
+  DCHECK(call_it != calls_.end());
+  Call& call = call_it->second;
+
+  DCHECK(call.data);
+  DCHECK_GT(call.pending_request_count, 0u);
+
+  --call.pending_request_count;
+
+  // nullptr means that this app does not have any data to save.
+  if (app_launch_info) {
+    call.data->AddAppLaunchInfo(std::move(app_launch_info));
+    call.data->ModifyWindowInfo(app_id, window_id, *window_info);
+  }
+
+  // Null callback here means that the loop in `CaptureActiveDeskAsTemplate()`
+  // has not yet finished polling the windows.  Non-zero pending request count
+  // means that some of preceding requests were asynchronous.
+  if (call.pending_request_count == 0 && !call.callback.is_null())
+    SendDeskTemplate(serial);
+}
+
+void RestoreDataCollector::SendDeskTemplate(uint32_t serial) {
+  auto call_it = calls_.find(serial);
+  DCHECK(call_it != calls_.end());
+  Call& call = call_it->second;
+
+  auto desk_template = std::make_unique<DeskTemplate>(
+      base::GUID::GenerateRandomV4().AsLowercaseString(),
+      DeskTemplateSource::kUser, call.template_name, base::Time::Now());
+  desk_template->set_desk_restore_data(std::move(call.data));
+
+  if (!call.unsupported_apps.empty() &&
+      Shell::Get()->overview_controller()->InOverviewSession()) {
+    // The ideal root window may have gone by now.  In that case fall back to
+    // the primary root one.
+    auto* root_window_to_show = call.root_window_to_show;
+    if (root_window_to_show && window_tracker_.Contains(root_window_to_show))
+      window_tracker_.Remove(root_window_to_show);
+    else
+      root_window_to_show = Shell::Get()->GetPrimaryRootWindow();
+
+    // There were some unsupported apps in the active desk so open up a dialog
+    // to let the user know.
+    DesksTemplatesDialogController::Get()->ShowUnsupportedAppsDialog(
+        root_window_to_show, std::move(call.unsupported_apps),
+        std::move(call.callback), std::move(desk_template));
+  } else {
+    std::move(call.callback).Run(std::move(desk_template));
+  }
+
+  calls_.erase(call_it);
+}
+
+}  // namespace ash
diff --git a/ash/wm/desks/templates/restore_data_collector.h b/ash/wm/desks/templates/restore_data_collector.h
new file mode 100644
index 0000000..35740a0
--- /dev/null
+++ b/ash/wm/desks/templates/restore_data_collector.h
@@ -0,0 +1,88 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WM_DESKS_TEMPLATES_RESTORE_DATA_COLLECTOR_H_
+#define ASH_WM_DESKS_TEMPLATES_RESTORE_DATA_COLLECTOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/aura/window_tracker.h"
+
+namespace app_restore {
+class RestoreData;
+struct AppLaunchInfo;
+struct WindowInfo;
+}  // namespace app_restore
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+
+class DeskTemplate;
+
+// Collects `AppLaunchData` from all applications that are currently active, and
+// returns it in form of `DeskTemplate` record.
+class RestoreDataCollector {
+ public:
+  using GetDeskTemplateCallback =
+      base::OnceCallback<void(std::unique_ptr<DeskTemplate>)>;
+
+  RestoreDataCollector();
+  RestoreDataCollector(const RestoreDataCollector&) = delete;
+  RestoreDataCollector& operator=(const RestoreDataCollector&) = delete;
+  ~RestoreDataCollector();
+
+  // Captures the active desk and returns it as a `DeskTemplate` object via the
+  // `callback`.
+  void CaptureActiveDeskAsTemplate(GetDeskTemplateCallback callback,
+                                   const std::string& template_name,
+                                   aura::Window* root_window_to_show);
+
+ private:
+  // Keeps the state for the asynchronous call for `AppLaunchData` to the apps.
+  struct Call {
+    Call();
+    Call(Call&&);
+    Call& operator=(Call&&);
+    ~Call();
+
+    std::string template_name;
+    aura::Window* root_window_to_show;
+    std::vector<aura::Window*> unsupported_apps;
+    std::unique_ptr<app_restore::RestoreData> data;
+    uint32_t pending_request_count = 0;
+    GetDeskTemplateCallback callback;
+  };
+
+  // Receives the `AppLaunchInfo` for the single app and puts it into the
+  // RestoreData record where data from all clients is accumulated.  If all data
+  // is collected, invokes the `SendDeskTemplate()` method.
+  void OnAppLaunchDataReceived(
+      uint32_t serial,
+      const std::string app_id,
+      const int32_t window_id,
+      std::unique_ptr<app_restore::WindowInfo> window_info,
+      std::unique_ptr<app_restore::AppLaunchInfo> app_launch_info);
+
+  // Creates a `DeskTemplate` object and sends it to the consumer after all apps
+  // have delivered their `AppLaunchInfo`.
+  void SendDeskTemplate(uint32_t serial);
+
+  // Auxiliary data for maintaining the asynchronous polling of the windows.
+  uint32_t serial_ = 0;
+  base::flat_map<uint32_t, Call> calls_;
+  aura::WindowTracker window_tracker_;
+
+  base::WeakPtrFactory<RestoreDataCollector> weak_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // #define ASH_WM_DESKS_TEMPLATES_RESTORE_DATA_COLLECTOR_H_
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 96c74ad..c9ae1fda 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -1037,6 +1037,12 @@
   UpdateNoWindowsWidgetOnEachGrid();
 
   UpdateAccessibilityFocus();
+
+  // TODO(crbug.com/1307467): This doesn't need to be reset if it's an ancestor
+  // of the desks bar view. Also, add testing for this. Note that this isn't
+  // needed when hiding, because we either move the focus to the new desk, or
+  // delete all the grid templates items which would reset their highlights.
+  highlight_controller_->ResetHighlightedView();
 }
 
 void OverviewSession::HideDesksTemplatesGrids() {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index bec4315b..29f74fb8 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -4160,7 +4160,6 @@
       "android/java/src/org/chromium/base/annotations/CalledByNative.java",
       "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
       "android/java/src/org/chromium/base/annotations/CheckDiscard.java",
-      "android/java/src/org/chromium/base/annotations/DoNotClassMerge.java",
       "android/java/src/org/chromium/base/annotations/DoNotInline.java",
       "android/java/src/org/chromium/base/annotations/IdentifierNameString.java",
       "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
@@ -4393,6 +4392,7 @@
       "test/android/javatests/src/org/chromium/base/test/util/DisableIf.java",
       "test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java",
       "test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java",
+      "test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java",
       "test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java",
       "test/android/javatests/src/org/chromium/base/test/util/Feature.java",
       "test/android/javatests/src/org/chromium/base/test/util/FlakyTest.java",
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h
index 5ee5e2c..9419fa4c 100644
--- a/base/allocator/partition_allocator/partition_page.h
+++ b/base/allocator/partition_allocator/partition_page.h
@@ -63,8 +63,8 @@
 // CAUTION! |extent| must point to the extent of the first super page in the
 // range of consecutive super pages.
 template <bool thread_safe>
-ALWAYS_INLINE uintptr_t
-SuperPagesBeginFromExtent(PartitionSuperPageExtentEntry<thread_safe>* extent) {
+ALWAYS_INLINE uintptr_t SuperPagesBeginFromExtent(
+    const PartitionSuperPageExtentEntry<thread_safe>* extent) {
   PA_DCHECK(0 < extent->number_of_consecutive_super_pages);
   uintptr_t extent_as_uintptr = reinterpret_cast<uintptr_t>(extent);
   PA_DCHECK(IsManagedByNormalBuckets(extent_as_uintptr));
@@ -77,8 +77,8 @@
 // CAUTION! |extent| must point to the extent of the first super page in the
 // range of consecutive super pages.
 template <bool thread_safe>
-ALWAYS_INLINE uintptr_t
-SuperPagesEndFromExtent(PartitionSuperPageExtentEntry<thread_safe>* extent) {
+ALWAYS_INLINE uintptr_t SuperPagesEndFromExtent(
+    const PartitionSuperPageExtentEntry<thread_safe>* extent) {
   return SuperPagesBeginFromExtent(extent) +
          (extent->number_of_consecutive_super_pages * kSuperPageSize);
 }
diff --git a/base/allocator/partition_allocator/thread_cache.h b/base/allocator/partition_allocator/thread_cache.h
index d7c2974..0d77f4f 100644
--- a/base/allocator/partition_allocator/thread_cache.h
+++ b/base/allocator/partition_allocator/thread_cache.h
@@ -56,6 +56,7 @@
 constexpr size_t kThreadCacheNeedleArraySize = 4;
 extern uintptr_t kThreadCacheNeedleArray[kThreadCacheNeedleArraySize];
 
+class HeapDumper;
 class ThreadCacheInspector;
 
 }  // namespace partition_alloc::internal::tools
@@ -148,6 +149,7 @@
 
  private:
   friend class partition_alloc::internal::tools::ThreadCacheInspector;
+  friend class partition_alloc::internal::tools::HeapDumper;
   friend class NoDestructor<ThreadCacheRegistry>;
   // Not using base::Lock as the object's constructor must be constexpr.
   PartitionLock lock_;
@@ -319,6 +321,7 @@
       ThreadCacheLimits::kLargeSizeThreshold;
 
  private:
+  friend class partition_alloc::internal::tools::HeapDumper;
   friend class partition_alloc::internal::tools::ThreadCacheInspector;
 
   struct Bucket {
diff --git a/base/android/java/src/org/chromium/base/annotations/DoNotClassMerge.java b/base/android/java/src/org/chromium/base/annotations/DoNotClassMerge.java
deleted file mode 100644
index 73c0516..0000000
--- a/base/android/java/src/org/chromium/base/annotations/DoNotClassMerge.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * The annotated class should never be horizontally or vertically merged.
- *
- * The annotated classes are guaranteed not to be horizontally or vertically
- * merged by Proguard. Other optimizations may still apply.
- */
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.CLASS)
-public @interface DoNotClassMerge {}
diff --git a/base/android/proguard/chromium_code.flags b/base/android/proguard/chromium_code.flags
index b3273e2..81d446c 100644
--- a/base/android/proguard/chromium_code.flags
+++ b/base/android/proguard/chromium_code.flags
@@ -64,9 +64,9 @@
   static boolean isDebug() return false;
 }
 
-# Never inline classes, methods, or fields with this annotation, but allow
+# Never inline classes, methods, or fields  with this annotation, but allow
 # shrinking and obfuscation.
-# Relevant to fields when they are needed to store strong references to objects
+# Relevant to fields when they are needed to store strong refrences to objects
 # that are held as weak references by native code.
 -if @org.chromium.base.annotations.DoNotInline class * {
     *** *(...);
@@ -81,11 +81,6 @@
    @org.chromium.base.annotations.DoNotInline <fields>;
 }
 
-# Never merge classes horizontally or vertically with this annotation.
-# Relevant to classes being used as a key in maps or sets.
--nohorizontalclassmerging @org.chromium.base.annotations.DoNotClassMerge class *
--noverticalclassmerging @org.chromium.base.annotations.DoNotClassMerge class *
-
 # Keep all CREATOR fields within Parcelable that are kept.
 -keepclassmembers class org.chromium.** implements android.os.Parcelable {
   public static *** CREATOR;
diff --git a/base/memory/shared_memory_mapping.h b/base/memory/shared_memory_mapping.h
index 4d9f10a..254a28d1 100644
--- a/base/memory/shared_memory_mapping.h
+++ b/base/memory/shared_memory_mapping.h
@@ -8,7 +8,6 @@
 #include <cstddef>
 #include <type_traits>
 
-#include "base/containers/buffer_iterator.h"
 #include "base/containers/span.h"
 #include "base/unguessable_token.h"
 
@@ -151,12 +150,6 @@
     return span<const T>(static_cast<const T*>(raw_memory_ptr()), count);
   }
 
-  // Returns a BufferIterator of const T.
-  template <typename T>
-  BufferIterator<const T> GetMemoryAsBufferIterator() const {
-    return BufferIterator<const T>(GetMemoryAsSpan<T>());
-  }
-
  private:
   friend class ReadOnlySharedMemoryRegion;
   ReadOnlySharedMemoryMapping(void* address,
@@ -231,12 +224,6 @@
     return span<T>(static_cast<T*>(raw_memory_ptr()), count);
   }
 
-  // Returns a BufferIterator of T.
-  template <typename T>
-  BufferIterator<T> GetMemoryAsBufferIterator() {
-    return BufferIterator<T>(GetMemoryAsSpan<T>());
-  }
-
  private:
   friend WritableSharedMemoryMapping MapAtForTesting(
       subtle::PlatformSharedMemoryRegion* region,
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java b/base/test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java
new file mode 100644
index 0000000..512f4c80
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is for disabled tests that should not be run in Test Reviver. Tests that cause
+ * other tests to fail or caue problems to the testing the infrastructure should have this
+ * annotation. <p> This should be used in conjunction with @DisabledTest or @DisableIf to prevent a
+ * test from running on normal bots.
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DoNotRevive {
+    String reason();
+}
diff --git a/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java b/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java
index 3ccf9b6..e98c427 100644
--- a/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java
+++ b/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java
@@ -15,12 +15,14 @@
 public class ShadowBuildInfo {
     private static boolean sIsAtLeastS;
     private static boolean sIsAtLeastT;
+    private static boolean sTargetsAtLeastT;
 
     /** Rests the changes made to static state. */
     @Resetter
     public static void reset() {
         sIsAtLeastS = false;
         sIsAtLeastT = false;
+        sTargetsAtLeastT = false;
     }
 
     /** Whether the current build is considered to be at least S. */
@@ -35,6 +37,12 @@
         return sIsAtLeastT;
     }
 
+    /** Whether the current build is targeting at least T. */
+    @Implementation
+    public static boolean targetsAtLeastT() {
+        return sTargetsAtLeastT;
+    }
+
     /** Sets whether current Android version is at least S. */
     public static void setIsAtLeastS(boolean isAtLeastS) {
         sIsAtLeastS = isAtLeastS;
@@ -44,4 +52,9 @@
     public static void setIsAtLeastT(boolean isAtLeastT) {
         sIsAtLeastT = isAtLeastT;
     }
+
+    /** Sets whether the current build is targeting at least T. */
+    public static void setTargetsAtLeastT(boolean targetsAtLeastT) {
+        sTargetsAtLeastT = targetsAtLeastT;
+    }
 }
diff --git a/build/android/gyp/create_unwind_table.py b/build/android/gyp/create_unwind_table.py
index 8c9c347f..f4a54f3 100755
--- a/build/android/gyp/create_unwind_table.py
+++ b/build/android/gyp/create_unwind_table.py
@@ -8,8 +8,8 @@
 import argparse
 import collections
 import enum
+import json
 import logging
-import os
 import re
 import struct
 import subprocess
@@ -614,22 +614,21 @@
     complete_instruction_sequence=bytes([0b10000000, 0b00000000])), )
 
 
-def EncodeFunctionUnwinds(function_unwinds: Iterable[FunctionUnwind]
+def EncodeFunctionUnwinds(function_unwinds: Iterable[FunctionUnwind],
+                          text_section_start_address: int
                           ) -> Iterable[EncodedFunctionUnwind]:
   """Encodes the unwind state for all functions defined in the binary.
 
   This function
   - sorts the collection of `FunctionUnwind`s by address.
   - fills in gaps between functions with trivial unwind.
-  - fills the space in the space in last page after last function with refuse
+  - fills the space in the last page after last function with refuse to unwind.
+  - fills the space in the first page before the first function with refuse
     to unwind.
 
-  Note:
-    This function assumes that min function start address is the text section
-    start address.
-
   Args:
     function_unwinds: An iterable of function unwind states.
+    text_section_start_address: The address of .text section in ELF file.
 
   Returns:
     The encoded function unwind states with no gaps between functions, ordered
@@ -656,7 +655,11 @@
   sorted_function_unwinds: List[FunctionUnwind] = sorted(
       function_unwinds, key=lambda function_unwind: function_unwind.address)
 
-  text_section_start_address: int = sorted_function_unwinds[0].address
+  if sorted_function_unwinds[0].address > text_section_start_address:
+    yield EncodedFunctionUnwind(page_number=0,
+                                page_offset=0,
+                                address_unwinds=REFUSE_TO_UNWIND)
+
   prev_func_end_address: int = sorted_function_unwinds[0].address
 
   gaps = 0
@@ -1009,6 +1012,29 @@
           unwind_instruction_table)
 
 
+def ReadTextSectionStartAddress(readobj_path: str, libchrome_path: str) -> int:
+  """Reads the .text section start address of libchrome ELF.
+
+  Arguments:
+    readobj_path: Path to llvm-obj binary.
+    libchrome_path: Path to libchrome binary.
+
+  Returns:
+    The text section start address as a number.
+  """
+  proc = subprocess.Popen(
+      [readobj_path, '--sections', '--elf-output-style=JSON', libchrome_path],
+      stdout=subprocess.PIPE,
+      encoding='ascii')
+
+  elfs = json.loads(proc.stdout.read())[0]
+  assert len(elfs) == 1
+  sections = list(elfs.values())[0]['Sections']
+
+  return next(s['Section']['Address'] for s in sections
+              if s['Section']['Name']['Value'] == '.text')
+
+
 def main():
   build_utils.InitLogging('CREATE_UNWIND_TABLE_DEBUG')
   parser = argparse.ArgumentParser(description=__doc__)
@@ -1024,6 +1050,10 @@
                       required=True,
                       help='The path of the dump_syms binary.',
                       metavar='FILE')
+  parser.add_argument('--readobj_path',
+                      required=True,
+                      help='The path of the llvm-readobj binary.',
+                      metavar='FILE')
 
   args = parser.parse_args()
   proc = subprocess.Popen(['./' + args.dump_syms_path, args.input_path, '-v'],
@@ -1032,7 +1062,9 @@
 
   function_cfis = ReadFunctionCfi(proc.stdout)
   function_unwinds = GenerateUnwinds(function_cfis, parsers=ALL_PARSERS)
-  encoded_function_unwinds = EncodeFunctionUnwinds(function_unwinds)
+  encoded_function_unwinds = EncodeFunctionUnwinds(
+      function_unwinds,
+      ReadTextSectionStartAddress(args.readobj_path, args.input_path))
   (page_table, function_table, function_offset_table,
    unwind_instruction_table) = GenerateUnwindTables(encoded_function_unwinds)
   unwind_info: bytes = EncodeUnwindInfo(page_table, function_table,
diff --git a/build/android/gyp/create_unwind_table_tests.py b/build/android/gyp/create_unwind_table_tests.py
index 81e9b7d7..c46557f 100755
--- a/build/android/gyp/create_unwind_table_tests.py
+++ b/build/android/gyp/create_unwind_table_tests.py
@@ -311,10 +311,10 @@
                              FunctionUnwind(address=100,
                                             size=PAGE_SIZE - 100,
                                             address_unwinds=()),
-                             FunctionUnwind(address=0,
-                                            size=100,
-                                            address_unwinds=()),
-                         ])))
+                             FunctionUnwind(
+                                 address=0, size=100, address_unwinds=()),
+                         ],
+                                               text_section_start_address=0)))
 
   @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds')
   def testFillingGaps(self, MockEncodeAddressUnwinds):
@@ -332,38 +332,65 @@
     ],
                      list(
                          EncodeFunctionUnwinds([
-                             FunctionUnwind(address=0,
-                                            size=50,
-                                            address_unwinds=()),
+                             FunctionUnwind(
+                                 address=0, size=50, address_unwinds=()),
                              FunctionUnwind(address=100,
                                             size=PAGE_SIZE - 100,
                                             address_unwinds=()),
-                         ])))
+                         ],
+                                               text_section_start_address=0)))
 
   @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds')
   def testFillingLastPage(self, MockEncodeAddressUnwinds):
     MockEncodeAddressUnwinds.return_value = EncodedAddressUnwind(0, b'\x00')
 
-    self.assertEqual([
-        EncodedFunctionUnwind(page_number=0,
-                              page_offset=0,
-                              address_unwinds=EncodedAddressUnwind(0, b'\x00')),
-        EncodedFunctionUnwind(page_number=0,
-                              page_offset=100 >> 1,
-                              address_unwinds=EncodedAddressUnwind(0, b'\x00')),
-        EncodedFunctionUnwind(page_number=0,
-                              page_offset=200 >> 1,
-                              address_unwinds=REFUSE_TO_UNWIND),
-    ],
-                     list(
-                         EncodeFunctionUnwinds([
-                             FunctionUnwind(address=1100,
-                                            size=100,
-                                            address_unwinds=()),
-                             FunctionUnwind(address=1200,
-                                            size=100,
-                                            address_unwinds=()),
-                         ])))
+    self.assertEqual(
+        [
+            EncodedFunctionUnwind(page_number=0,
+                                  page_offset=0,
+                                  address_unwinds=EncodedAddressUnwind(
+                                      0, b'\x00')),
+            EncodedFunctionUnwind(page_number=0,
+                                  page_offset=100 >> 1,
+                                  address_unwinds=EncodedAddressUnwind(
+                                      0, b'\x00')),
+            EncodedFunctionUnwind(page_number=0,
+                                  page_offset=200 >> 1,
+                                  address_unwinds=REFUSE_TO_UNWIND),
+        ],
+        list(
+            EncodeFunctionUnwinds([
+                FunctionUnwind(address=1100, size=100, address_unwinds=()),
+                FunctionUnwind(address=1200, size=100, address_unwinds=()),
+            ],
+                                  text_section_start_address=1100)))
+
+  @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds')
+  def testFillingFirstPage(self, MockEncodeAddressUnwinds):
+    MockEncodeAddressUnwinds.return_value = EncodedAddressUnwind(0, b'\x00')
+
+    self.assertEqual(
+        [
+            EncodedFunctionUnwind(
+                page_number=0, page_offset=0, address_unwinds=REFUSE_TO_UNWIND),
+            EncodedFunctionUnwind(page_number=0,
+                                  page_offset=100 >> 1,
+                                  address_unwinds=EncodedAddressUnwind(
+                                      0, b'\x00')),
+            EncodedFunctionUnwind(page_number=0,
+                                  page_offset=200 >> 1,
+                                  address_unwinds=EncodedAddressUnwind(
+                                      0, b'\x00')),
+            EncodedFunctionUnwind(page_number=0,
+                                  page_offset=300 >> 1,
+                                  address_unwinds=REFUSE_TO_UNWIND),
+        ],
+        list(
+            EncodeFunctionUnwinds([
+                FunctionUnwind(address=1100, size=100, address_unwinds=()),
+                FunctionUnwind(address=1200, size=100, address_unwinds=()),
+            ],
+                                  text_section_start_address=1000)))
 
   @unittest.mock.patch('create_unwind_table.EncodeAddressUnwinds')
   def testOverlappedFunctions(self, _):
@@ -374,7 +401,8 @@
             EncodeFunctionUnwinds([
                 FunctionUnwind(address=0, size=100, address_unwinds=()),
                 FunctionUnwind(address=50, size=100, address_unwinds=()),
-            ])))
+            ],
+                                  text_section_start_address=0)))
 
 
 class _TestNullParser(unittest.TestCase):
diff --git a/build/android/pylib/constants/__init__.py b/build/android/pylib/constants/__init__.py
index 050b99e..615307a 100644
--- a/build/android/pylib/constants/__init__.py
+++ b/build/android/pylib/constants/__init__.py
@@ -72,10 +72,6 @@
     chrome.PackageInfo('com.google.android.webview',
                        'com.android.cts.webkit.WebViewStartupCtsActivity',
                        'webview-command-line', None),
-    'android_system_webview_shell':
-    chrome.PackageInfo('org.chromium.webview_shell',
-                       'org.chromium.webview_shell.WebViewBrowserActivity',
-                       'webview-command-line', None),
     'android_webview_ui_test':
     chrome.PackageInfo('org.chromium.webview_ui_test',
                        'org.chromium.webview_ui_test.WebViewUiTestActivity',
diff --git a/build/chromeos/gen_skylab_runner.py b/build/chromeos/gen_skylab_runner.py
deleted file mode 100755
index 3a5e0c0..0000000
--- a/build/chromeos/gen_skylab_runner.py
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env vpython3
-#
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import argparse
-import os
-import sys
-
-
-class SkylabClientTestTest:
-
-  # The basic shell script for client test run in Skylab. The arguments listed
-  # here will be fed by autotest at the run time.
-  #
-  # * test-launcher-summary-output: the path for the json result. It will be
-  #     assigned by autotest, who will upload it to GCS upon test completion.
-  # * test-launcher-shard-index: the index for this test run.
-  # * test-launcher-total-shards: the total test shards.
-  # * test_args: arbitrary runtime arguments configured in test_suites.pyl,
-  #     attached after '--'.
-  BASIC_SHELL_SCRIPT = """
-#!/bin/sh
-
-while [[ $# -gt 0 ]]; do
-    case "$1" in
-        --test-launcher-summary-output)
-            summary_output=$2
-            shift 2
-            ;;
-
-        --test-launcher-shard-index)
-            shard_index=$2
-            shift 2
-            ;;
-
-        --test-launcher-total-shards)
-            total_shards=$2
-            shift 2
-            ;;
-
-        --)
-            test_args=$2
-            break
-            ;;
-
-        *)
-            break
-            ;;
-    esac
-done
-
-if [ ! -d $(dirname $summary_output) ] ; then
-    mkdir -p $(dirname $summary_output)
-fi
-
-cd `dirname $0` && cd ..
-  """
-
-  def __init__(self, args):
-    self.test_exe = args.test_exe
-    self.output = args.output
-
-  @property
-  def suite_name(self):
-    return self.test_exe
-
-  def build_test_script(self):
-    # Build the shell script that will be used on the device to invoke the test.
-    # Stored here as a list of lines.
-    device_test_script_contents = self.BASIC_SHELL_SCRIPT.split('\n')
-
-    test_invocation = ('LD_LIBRARY_PATH=./ ./%s '
-                       ' --test-launcher-summary-output=$summary_output'
-                       ' --test-launcher-shard-index=$shard_index'
-                       ' --test-launcher-total-shards=$total_shards'
-                       ' $test_args' % self.test_exe)
-
-    device_test_script_contents.append(test_invocation)
-    with open(self.output, 'w') as w:
-      w.write('\n'.join(device_test_script_contents) + '\n')
-      os.chmod(self.output, 0o755)
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-      '--test-exe',
-      type=str,
-      required=True,
-      help='Path to test executable to run inside the device.')
-  parser.add_argument('--verbose', '-v', action='store_true')
-  parser.add_argument(
-      '--output',
-      required=True,
-      type=str,
-      help='Path to create the runner script.')
-
-  args = parser.parse_args()
-
-  test = SkylabClientTestTest(args)
-  test.build_test_script()
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/chromeos/generate_skylab_deps.py b/build/chromeos/generate_skylab_deps.py
new file mode 100755
index 0000000..908956e
--- /dev/null
+++ b/build/chromeos/generate_skylab_deps.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env vpython3
+#
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import os
+import sys
+
+# The basic shell script for client test run in Skylab. The arguments listed
+# here will be fed by autotest at the run time.
+#
+# * test-launcher-summary-output: the path for the json result. It will be
+#     assigned by autotest, who will upload it to GCS upon test completion.
+# * test-launcher-shard-index: the index for this test run.
+# * test-launcher-total-shards: the total test shards.
+# * test_args: arbitrary runtime arguments configured in test_suites.pyl,
+#     attached after '--'.
+BASIC_SHELL_SCRIPT = """
+#!/bin/sh
+
+while [[ $# -gt 0 ]]; do
+    case "$1" in
+        --test-launcher-summary-output)
+            summary_output=$2
+            shift 2
+            ;;
+
+        --test-launcher-shard-index)
+            shard_index=$2
+            shift 2
+            ;;
+
+        --test-launcher-total-shards)
+            total_shards=$2
+            shift 2
+            ;;
+
+        --)
+            test_args=$2
+            break
+            ;;
+
+        *)
+            break
+            ;;
+    esac
+done
+
+if [ ! -d $(dirname $summary_output) ] ; then
+    mkdir -p $(dirname $summary_output)
+fi
+
+cd `dirname $0` && cd ..
+  """
+
+
+def build_test_script(args):
+  # Build the shell script that will be used on the device to invoke the test.
+  # Stored here as a list of lines.
+  device_test_script_contents = BASIC_SHELL_SCRIPT.split('\n')
+
+  test_invocation = ('LD_LIBRARY_PATH=./ ./%s '
+                     ' --test-launcher-summary-output=$summary_output'
+                     ' --test-launcher-shard-index=$shard_index'
+                     ' --test-launcher-total-shards=$total_shards'
+                     ' $test_args' % args.test_exe)
+
+  device_test_script_contents.append(test_invocation)
+  with open(args.output, 'w') as w:
+    w.write('\n'.join(device_test_script_contents) + '\n')
+    os.chmod(args.output, 0o755)
+
+
+def build_filter_file(args):
+  with open(args.output, 'w') as w:
+    if args.disabled_tests is not None:
+      w.write('\n'.join('-{0}'.format(test) for test in args.disabled_tests) +
+              '\n')
+    if args.tests is not None:
+      w.write('\n'.join(args.tests) + '\n')
+    os.chmod(args.output, 0o755)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  subparsers = parser.add_subparsers(dest='command')
+
+  script_gen_parser = subparsers.add_parser('generate-runner')
+  script_gen_parser.add_argument(
+      '--test-exe',
+      type=str,
+      required=True,
+      help='Path to test executable to run inside the device.')
+  script_gen_parser.add_argument('--verbose', '-v', action='store_true')
+  script_gen_parser.add_argument(
+      '--output',
+      required=True,
+      type=str,
+      help='Path to create the runner script.')
+  script_gen_parser.set_defaults(func=build_test_script)
+
+  filter_gen_parser = subparsers.add_parser('generate-filter')
+  filter_gen_parser.add_argument(
+      '--disabled-tests',
+      type=str,
+      required=False,
+      action='append',
+      help='Space separated test names to prevent running. This generates the \
+          negative filter')
+  filter_gen_parser.add_argument(
+      '--tests',
+      type=str,
+      required=False,
+      action='append',
+      help='Space separated test names to be run. This generates a positive \
+          filter.')
+  filter_gen_parser.add_argument(
+      '--output',
+      required=True,
+      type=str,
+      help='Path to create the plain text filter file.')
+  filter_gen_parser.set_defaults(func=build_filter_file)
+
+  args = parser.parse_args()
+
+  if (args.command == "generate-filter" and args.disabled_tests is None and
+      args.tests is None):
+    parser.error('disabled_tests or tests must be provided to generate-filter')
+  args.func(args)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/config/android/create_unwind_table.gni b/build/config/android/create_unwind_table.gni
index 15f8fd9..0e553ea 100644
--- a/build/config/android/create_unwind_table.gni
+++ b/build/config/android/create_unwind_table.gni
@@ -30,6 +30,8 @@
       rebase_path(_asset_path, root_build_dir),
       "--dump_syms_path",
       rebase_path("$root_out_dir/dump_syms", root_build_dir),
+      "--readobj_path",
+      rebase_path("$clang_base_path/bin/llvm-readobj", root_build_dir),
     ]
     deps = invoker.deps
     deps += [ "//third_party/breakpad:dump_syms" ]
diff --git a/build/config/chromeos/rules.gni b/build/config/chromeos/rules.gni
index 60a21ab..c4d7e7cc 100644
--- a/build/config/chromeos/rules.gni
+++ b/build/config/chromeos/rules.gni
@@ -102,6 +102,93 @@
   }
 }
 
+# Creates dependencies required by skylab testing. If passed the
+# generated_script and test_exe this will generate the skylab runner script.
+# If passed tast_tests or tast_disabled_tests this will generate a filter file
+# to enable or disable the appropriate tests in skylab.
+# Args:
+#   tast_disabled_tests: Names of tests to disable in tast. All other tests that
+#       match the tast expression will still run.
+#   tast_tests: Names of tests to enable in tast. All other tests will be
+#       disabled that are not listed.
+#   generated_script: Name of the generated runner script created for test_exe
+#   test_exe: Name of the executable to run with the generated script. This
+#       argument
+template("generate_skylab_deps") {
+  forward_variables_from(invoker,
+                         [
+                           "tast_disabled_tests",
+                           "tast_tests",
+                           "generated_script",
+                           "test_exe",
+                         ])
+  if (defined(test_exe) || defined(generated_script)) {
+    assert(defined(test_exe) && defined(generated_script),
+           "The test_exe and generated_script must both be defined when " +
+               "generating the skylab runner script")
+    action(target_name) {
+      script = "//build/chromeos/generate_skylab_deps.py"
+      outputs = [ generated_script ]
+      args = [
+        "generate-runner",
+        "--test-exe",
+        test_exe,
+        "--output",
+        rebase_path(generated_script, root_build_dir),
+      ]
+
+      deps = [ "//testing/buildbot/filters:chromeos_filters" ]
+      if (defined(invoker.deps)) {
+        deps += invoker.deps
+      }
+
+      data = [ generated_script ]
+      if (defined(invoker.data)) {
+        data += invoker.data
+      }
+
+      data_deps = [ "//testing:test_scripts_shared" ]
+      if (defined(invoker.data_deps)) {
+        data_deps += invoker.data_deps
+      }
+    }
+  }
+  if (defined(tast_tests) || defined(tast_disabled_tests)) {
+    _generated_filter = "$root_build_dir/bin/${target_name}.filter"
+    _skylab_args = [
+      "generate-filter",
+      "--output",
+      rebase_path(_generated_filter),
+    ]
+    if (defined(tast_disabled_tests)) {
+      foreach(_test, tast_disabled_tests) {
+        _skylab_args += [
+          "--disabled-tests",
+          _test,
+        ]
+      }
+    }
+    if (defined(tast_tests)) {
+      foreach(_test, tast_tests) {
+        _skylab_args += [
+          "--tests",
+          _test,
+        ]
+      }
+    }
+    action(target_name) {
+      script = "//build/chromeos/generate_skylab_deps.py"
+      outputs = [ _generated_filter ]
+      args = _skylab_args
+      data_deps = invoker.data_deps
+      data = [ _generated_filter ]
+      if (defined(invoker.data)) {
+        data += invoker.data
+      }
+    }
+  }
+}
+
 # Creates a script at $generated_script that can be used to launch a cros VM
 # and optionally run a test within it.
 # Args:
@@ -432,6 +519,24 @@
   assert(defined(tast_attr_expr) != defined(tast_tests),
          "Specify one of tast_tests or tast_attr_expr.")
 
+  _lacros_data_deps = [
+    "//chrome",  # Builds the browser.
+
+    # Tools used to symbolize Chrome crash dumps.
+    # TODO(crbug.com/1156772): Remove these if/when all tests pick them up by
+    # default.
+    "//third_party/breakpad:dump_syms",
+    "//third_party/breakpad:minidump_dump",
+    "//third_party/breakpad:minidump_stackwalk",
+  ]
+
+  _lacros_data = [
+    "//components/crash/content/tools/generate_breakpad_symbols.py",
+
+    # A script needed to launch Lacros in Lacros Tast tests.
+    "//build/lacros/mojo_connection_lacros_launcher.py",
+  ]
+
   # Append any disabled tests to the expression.
   if (defined(tast_disabled_tests)) {
     assert(defined(tast_attr_expr),
@@ -444,74 +549,33 @@
     tast_attr_expr = "( " + tast_attr_expr + " )"
   }
 
-  generate_runner_script(target_name) {
-    testonly = true
-    deploy_lacros = true
-    generated_script = "$root_build_dir/bin/run_${target_name}"
-    runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps"
-
-    # At build time, Lacros tests don't know whether they'll run on VM or HW,
-    # and instead, these flags are specified at runtime when invoking the
-    # generated runner script.
-    skip_generating_board_args = true
-
-    # By default, tast tests download a lacros-chrome from a gcs location and
-    # use it for testing. To support running lacros tast tests from Chromium CI,
-    # a Var is added to support pointing the tast tests to use a specified
-    # pre-deployed lacros-chrome. The location is decided by:
-    # https://source.chromium.org/chromium/chromium/src/+/main:third_party/chromite/scripts/deploy_chrome.py;l=80;drc=86f1234a4be8e9574442e076cdc835897f7bea61
-    tast_vars = [ "lacrosDeployedBinary=/usr/local/lacros-chrome" ]
-
-    data_deps = [
-      "//chrome",  # Builds the browser.
-
-      # Tools used to symbolize Chrome crash dumps.
-      # TODO(crbug.com/1156772): Remove these if/when all tests pick them up by
-      # default.
-      "//third_party/breakpad:dump_syms",
-      "//third_party/breakpad:minidump_dump",
-      "//third_party/breakpad:minidump_stackwalk",
-    ]
-
-    data = [
-      "//components/crash/content/tools/generate_breakpad_symbols.py",
-
-      # A script needed to launch Lacros in Lacros Tast tests.
-      "//build/lacros/mojo_connection_lacros_launcher.py",
-    ]
-  }
-}
-
-template("generate_skylab_runner_script") {
-  forward_variables_from(invoker,
-                         [
-                           "generated_script",
-                           "test_exe",
-                         ])
-
-  action(target_name) {
-    script = "//build/chromeos/gen_skylab_runner.py"
-    outputs = [ generated_script ]
-    args = [
-      "--test-exe",
-      test_exe,
-      "--output",
-      rebase_path(generated_script, root_build_dir),
-    ]
-
-    deps = [ "//testing/buildbot/filters:chromeos_filters" ]
-    if (defined(invoker.deps)) {
-      deps += invoker.deps
+  if (is_skylab) {
+    generate_skylab_deps(target_name) {
+      data = _lacros_data
+      data_deps = _lacros_data_deps
     }
+  } else {
+    generate_runner_script(target_name) {
+      testonly = true
+      deploy_lacros = true
+      generated_script = "$root_build_dir/bin/run_${target_name}"
+      runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps"
 
-    data = [ generated_script ]
-    if (defined(invoker.data)) {
-      data += invoker.data
-    }
+      # At build time, Lacros tests don't know whether they'll run on VM or HW,
+      # and instead, these flags are specified at runtime when invoking the
+      # generated runner script.
+      skip_generating_board_args = true
 
-    data_deps = [ "//testing:test_scripts_shared" ]
-    if (defined(invoker.data_deps)) {
-      data_deps += invoker.data_deps
+      # By default, tast tests download a lacros-chrome from a gcs location and
+      # use it for testing. To support running lacros tast tests from Chromium CI,
+      # a Var is added to support pointing the tast tests to use a specified
+      # pre-deployed lacros-chrome. The location is decided by:
+      # https://source.chromium.org/chromium/chromium/src/+/main:third_party/chromite/scripts/deploy_chrome.py;l=80;drc=86f1234a4be8e9574442e076cdc835897f7bea61
+      tast_vars = [ "lacrosDeployedBinary=/usr/local/lacros-chrome" ]
+
+      data_deps = _lacros_data_deps
+
+      data = _lacros_data
     }
   }
 }
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 48aa424..60165f1 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -820,13 +820,15 @@
     "-Coverflow-checks=on",
 
     # To make Rust .d files compatible with ninja
-    "-Z",
-    "dep-info-omit-d-target",
+    "-Zdep-info-omit-d-target",
 
     # If a macro panics during compilation, show which macro and where it is
     # defined.
-    "-Z",
-    "macro-backtrace",
+    "-Zmacro-backtrace",
+
+    # For deterministic builds, keep the local machine's current working
+    # directory from appearing in build outputs.
+    "-Zremap-cwd-prefix=.",
   ]
   if (rust_abi_target != "") {
     rustflags += [ "--target=$rust_abi_target" ]
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index dfbe3ef..03e9699 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220317.1.1
+7.20220318.0.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index dfbe3ef..ae4ad6b 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220317.1.1
+7.20220318.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index dfbe3ef..03e9699 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220317.1.1
+7.20220318.0.1
diff --git a/build/rust/BUILD.gn b/build/rust/BUILD.gn
index 09da28ee..4477db4 100644
--- a/build/rust/BUILD.gn
+++ b/build/rust/BUILD.gn
@@ -22,10 +22,13 @@
     "//third_party/rust/cxx/v1/crate/src/cxx.cc",
   ]
 
-  defines = [
-    "RUST_CXX_NO_EXCEPTIONS",
-    "CXX_RS_EXPORT=__attribute__((visibility(\"default\")))",
-  ]
+  defines = [ "RUST_CXX_NO_EXCEPTIONS" ]
+
+  if (is_win) {
+    defines += [ "CXX_RS_EXPORT=__declspec(dllexport)" ]
+  } else {
+    defines += [ "CXX_RS_EXPORT=__attribute__((visibility(\"default\")))" ]
+  }
 
   # Depending on the C++ bindings side of cxx then requires also depending
   # on the Rust bindings, since one calls the other. And the Rust bindings
diff --git a/build/rust/tests/BUILD.gn b/build/rust/tests/BUILD.gn
index 418f322..ffeb9d2f 100644
--- a/build/rust/tests/BUILD.gn
+++ b/build/rust/tests/BUILD.gn
@@ -76,7 +76,7 @@
         "test_rust_exe",
         "test_rust_multiple_dep_versions_exe",
         "test_simple_rust_exe",
-        "//third_party/rust/autocxx_gen/v0_16:autocxx_gen",
+        "//third_party/rust/autocxx_gen/v0_17:autocxx_gen",
         "//third_party/rust/bindgen/v0_59:bindgen",
       ]
     }
diff --git a/buildtools/mac/clang-format.arm64.sha1 b/buildtools/mac/clang-format.arm64.sha1
new file mode 100644
index 0000000..6b3f7a91
--- /dev/null
+++ b/buildtools/mac/clang-format.arm64.sha1
@@ -0,0 +1 @@
+5ba974b3b37f9f4e3b44fdde11d7ef2ab71619ab
diff --git a/buildtools/mac/clang-format.x64.sha1 b/buildtools/mac/clang-format.x64.sha1
new file mode 100644
index 0000000..6b3f7a91
--- /dev/null
+++ b/buildtools/mac/clang-format.x64.sha1
@@ -0,0 +1 @@
+5ba974b3b37f9f4e3b44fdde11d7ef2ab71619ab
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 50b5b29..a7636e45 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -4911,8 +4911,8 @@
                     element_id));
   const ScrollNode* scroll_node =
       property_trees->scroll_tree().FindNodeFromElementId(element_id);
-  // TODO(flackr): We should aim to prevent this condition from happening
-  // and either remove this check or make it fatal.
+  // TODO(crbug.com/1307498): We should aim to prevent this condition from
+  // happening and either remove this check or make it fatal.
   DCHECK(scroll_node);
   if (!scroll_node)
     return;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 2e9caef1..ca71933 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -61,17 +61,6 @@
 
 namespace cc {
 namespace {
-
-template <class T>
-bool CheckPropertyNodeExists(const T& property_tree, ElementId element_id) {
-  size_t nodes_with_element_id =
-      property_tree.element_id_to_node_index().count(element_id);
-  DCHECK_EQ(1u, nodes_with_element_id);
-  // TODO(flackr): We should aim to prevent this condition from happening
-  // and either remove this check or make it fatal.
-  return nodes_with_element_id > 0;
-}
-
 // Small helper class that saves the current viewport location as the user sees
 // it and resets to the same location.
 class ViewportAnchor {
@@ -962,9 +951,9 @@
 
 void LayerTreeImpl::SetTransformMutated(ElementId element_id,
                                         const gfx::Transform& transform) {
-  if (!CheckPropertyNodeExists(property_trees()->transform_tree(), element_id))
-    return;
-
+  DCHECK_EQ(1u,
+            property_trees()->transform_tree().element_id_to_node_index().count(
+                element_id));
   if (IsSyncTree() || IsRecycleTree())
     element_id_to_transform_animations_[element_id] = transform;
   if (property_trees()->transform_tree_mutable().OnTransformAnimated(element_id,
@@ -973,9 +962,9 @@
 }
 
 void LayerTreeImpl::SetOpacityMutated(ElementId element_id, float opacity) {
-  if (!CheckPropertyNodeExists(property_trees()->effect_tree(), element_id))
-    return;
-
+  DCHECK_EQ(1u,
+            property_trees()->effect_tree().element_id_to_node_index().count(
+                element_id));
   if (IsSyncTree() || IsRecycleTree())
     element_id_to_opacity_animations_[element_id] = opacity;
   if (property_trees()->effect_tree_mutable().OnOpacityAnimated(element_id,
@@ -985,9 +974,9 @@
 
 void LayerTreeImpl::SetFilterMutated(ElementId element_id,
                                      const FilterOperations& filters) {
-  if (!CheckPropertyNodeExists(property_trees()->effect_tree(), element_id))
-    return;
-
+  DCHECK_EQ(1u,
+            property_trees()->effect_tree().element_id_to_node_index().count(
+                element_id));
   if (IsSyncTree() || IsRecycleTree())
     element_id_to_filter_animations_[element_id] = filters;
   if (property_trees()->effect_tree_mutable().OnFilterAnimated(element_id,
@@ -998,9 +987,9 @@
 void LayerTreeImpl::SetBackdropFilterMutated(
     ElementId element_id,
     const FilterOperations& backdrop_filters) {
-  if (!CheckPropertyNodeExists(property_trees()->effect_tree(), element_id))
-    return;
-
+  DCHECK_EQ(1u,
+            property_trees()->effect_tree().element_id_to_node_index().count(
+                element_id));
   if (IsSyncTree() || IsRecycleTree())
     element_id_to_backdrop_filter_animations_[element_id] = backdrop_filters;
   if (property_trees()->effect_tree_mutable().OnBackdropFilterAnimated(
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index feb6874a..36fdc34 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -152,6 +152,10 @@
                                         const gfx::Transform& transform) {
   TransformNode* node = FindNodeFromElementId(element_id);
   DCHECK(node);
+  // TODO(crbug.com/1307498): Remove this when we no longer animate
+  // non-existent nodes.
+  if (!node)
+    return false;
   if (node->local == transform)
     return false;
   node->local = transform;
@@ -472,8 +476,8 @@
   fixed_position_adjustment +=
       gfx::ScaleVector2d(overscroll_offset, 1.f / page_scale_factor());
 
-  ClipNode* clip_node = property_trees()->clip_tree_mutable().Node(
-      property_trees()->clip_tree_mutable().overscroll_node_id());
+  ClipTree& clip_tree = property_trees()->clip_tree_mutable();
+  ClipNode* clip_node = clip_tree.Node(clip_tree.overscroll_node_id());
 
   if (clip_node) {
     // Inflate the clip rect based on the overscroll direction.
@@ -486,7 +490,7 @@
         : outsets.set_bottom(fixed_position_adjustment.y());
 
     clip_node->clip.Outset(outsets);
-    property_trees()->clip_tree_mutable().set_needs_update(true);
+    clip_tree.set_needs_update(true);
   }
 }
 
@@ -887,6 +891,10 @@
 bool EffectTree::OnOpacityAnimated(ElementId id, float opacity) {
   EffectNode* node = FindNodeFromElementId(id);
   DCHECK(node);
+  // TODO(crbug.com/1307498): Remove this when we no longer animate
+  // non-existent nodes.
+  if (!node)
+    return false;
   if (node->opacity == opacity)
     return false;
   node->opacity = opacity;
@@ -900,6 +908,10 @@
                                   const FilterOperations& filters) {
   EffectNode* node = FindNodeFromElementId(id);
   DCHECK(node);
+  // TODO(crbug.com/1307498): Remove this when we no longer animate
+  // non-existent nodes.
+  if (!node)
+    return false;
   if (node->filters == filters)
     return false;
   node->filters = filters;
@@ -914,6 +926,10 @@
     const FilterOperations& backdrop_filters) {
   EffectNode* node = FindNodeFromElementId(id);
   DCHECK(node);
+  // TODO(crbug.com/1307498): Remove this when we no longer animate
+  // non-existent nodes.
+  if (!node)
+    return false;
   if (node->backdrop_filters == backdrop_filters)
     return false;
   node->backdrop_filters = backdrop_filters;
diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc
index e2fef3a..e31cbd7c 100644
--- a/cc/trees/property_tree_unittest.cc
+++ b/cc/trees/property_tree_unittest.cc
@@ -196,36 +196,52 @@
 }
 
 // Tests that the transform for fixed elements is translated based on the
-// overscroll nodes scroll_offset.
+// overscroll nodes scroll_offset and that the clip node has an outset based on
+// the overscroll distance.
 TEST(PropertyTreeTest, FixedElementInverseTranslation) {
   FakeProtectedSequenceSynchronizer synchronizer;
   PropertyTrees property_trees(synchronizer);
-  TransformTree& tree = property_trees.transform_tree_mutable();
+
+  ClipTree& clip_tree = property_trees.clip_tree_mutable();
+  const gfx::RectF clip_rect(0, 0, 100, 100);
+  ClipNode clip_node;
+  clip_node.id = 1;
+  clip_node.parent_id = 0;
+  clip_node.clip = clip_rect;
+  clip_tree.Insert(clip_node, 0);
+  clip_tree.set_overscroll_node_id(clip_node.id);
+
+  TransformTree& transform_tree = property_trees.transform_tree_mutable();
   TransformNode contents_root;
   contents_root.local.Translate(2, 2);
-  contents_root.id = tree.Insert(contents_root, 0);
-  tree.UpdateTransforms(1);
+  contents_root.id = transform_tree.Insert(contents_root, 0);
+  transform_tree.UpdateTransforms(1);
 
   const gfx::PointF overscroll_offset(0, 10);
   TransformNode overscroll_node;
   overscroll_node.scroll_offset = overscroll_offset;
-  overscroll_node.id = tree.Insert(overscroll_node, 1);
+  overscroll_node.id = transform_tree.Insert(overscroll_node, 1);
 
-  tree.set_overscroll_node_id(overscroll_node.id);
-  tree.set_fixed_elements_dont_overscroll(true);
+  transform_tree.set_overscroll_node_id(overscroll_node.id);
+  transform_tree.set_fixed_elements_dont_overscroll(true);
 
   TransformNode fixed_node;
   fixed_node.is_fixed_position = true;
-  fixed_node.id = tree.Insert(fixed_node, 2);
+  fixed_node.id = transform_tree.Insert(fixed_node, 2);
 
-  EXPECT_TRUE(tree.ShouldUndoOverscroll(&fixed_node));
+  EXPECT_TRUE(transform_tree.ShouldUndoOverscroll(&fixed_node));
 
-  tree.UpdateTransforms(2);  // overscroll_node
-  tree.UpdateTransforms(3);  // fixed_node
+  transform_tree.UpdateTransforms(2);  // overscroll_node
+  transform_tree.UpdateTransforms(3);  // fixed_node
 
   gfx::Transform expected;
   expected.Translate(overscroll_offset.OffsetFromOrigin());
-  EXPECT_TRANSFORM_EQ(expected, tree.Node(fixed_node.id)->to_parent);
+  EXPECT_TRANSFORM_EQ(expected, transform_tree.Node(fixed_node.id)->to_parent);
+
+  gfx::RectF expected_clip_rect(clip_rect);
+  expected_clip_rect.set_height(clip_rect.height() + overscroll_offset.y());
+  EXPECT_EQ(clip_tree.Node(clip_tree.overscroll_node_id())->clip,
+            expected_clip_rect);
 }
 
 TEST(PropertyTreeTest, TransformsWithFlattening) {
diff --git a/chrome/MAJOR_BRANCH_DATE b/chrome/MAJOR_BRANCH_DATE
index bf2682dc..6fddc50 100644
--- a/chrome/MAJOR_BRANCH_DATE
+++ b/chrome/MAJOR_BRANCH_DATE
@@ -1 +1 @@
-MAJOR_BRANCH_DATE=2022-02-18
+MAJOR_BRANCH_DATE=2022-03-18
diff --git a/chrome/VERSION b/chrome/VERSION
index 8609131..e1efff5 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
-MAJOR=101
+MAJOR=102
 MINOR=0
-BUILD=4951
+BUILD=4952
 PATCH=0
diff --git a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
index 8d4070d..35a58e1 100644
--- a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
@@ -207,9 +207,9 @@
   static boolean isDebug() return false;
 }
 
-# Never inline classes, methods, or fields with this annotation, but allow
+# Never inline classes, methods, or fields  with this annotation, but allow
 # shrinking and obfuscation.
-# Relevant to fields when they are needed to store strong references to objects
+# Relevant to fields when they are needed to store strong refrences to objects
 # that are held as weak references by native code.
 -if @org.chromium.base.annotations.DoNotInline class * {
     *** *(...);
@@ -224,11 +224,6 @@
    @org.chromium.base.annotations.DoNotInline <fields>;
 }
 
-# Never merge classes horizontally or vertically with this annotation.
-# Relevant to classes being used as a key in maps or sets.
--nohorizontalclassmerging @org.chromium.base.annotations.DoNotClassMerge class *
--noverticalclassmerging @org.chromium.base.annotations.DoNotClassMerge class *
-
 # Keep all CREATOR fields within Parcelable that are kept.
 -keepclassmembers class org.chromium.** implements android.os.Parcelable {
   public static *** CREATOR;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java
index 3b37c0d7..85c693000 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/helper/FaviconHelper.java
@@ -22,7 +22,7 @@
  * Provides default favicons and helps to fetch and set favicons.
  */
 public class FaviconHelper {
-    private final Resources mResources;
+    private final Context mContext;
     private final RoundedIconGenerator mIconGenerator;
     private final int mDesiredSize;
 
@@ -52,15 +52,17 @@
      * @param context The {@link Context} used to fetch resources and create Drawables.
      */
     protected FaviconHelper(Context context) {
-        mResources = context.getResources();
+        mContext = context;
+        final Resources resources = mContext.getResources();
         mDesiredSize =
-                mResources.getDimensionPixelSize(R.dimen.keyboard_accessory_suggestion_icon_size);
-        mIconGenerator = FaviconUtils.createCircularIconGenerator(mResources);
+                resources.getDimensionPixelSize(R.dimen.keyboard_accessory_suggestion_icon_size);
+        mIconGenerator = FaviconUtils.createCircularIconGenerator(mContext.getResources());
     }
 
     public Drawable getDefaultIcon(String origin) {
         return FaviconUtils.getIconDrawableWithoutFilter(null, origin,
-                R.color.default_favicon_background_color, mIconGenerator, mResources, mDesiredSize);
+                mContext.getColor(R.color.default_favicon_background_color), mIconGenerator,
+                mContext.getResources(), mDesiredSize);
     }
 
     /**
@@ -76,7 +78,7 @@
         mIconBridge.getLargeIconForUrl(gurlOrigin, mDesiredSize,
                 (icon, fallbackColor, isFallbackColorDefault, iconType) -> {
                     Drawable drawable = FaviconUtils.getIconDrawableWithoutFilter(icon, gurlOrigin,
-                            fallbackColor, mIconGenerator, mResources, mDesiredSize);
+                            fallbackColor, mIconGenerator, mContext.getResources(), mDesiredSize);
                     setIconCallback.onResult(drawable);
                 });
     }
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java
index ddf898ea..d615863 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetRenderTest.java
@@ -99,7 +99,7 @@
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
 
     public AccessorySheetRenderTest(boolean nightModeEnabled, boolean useRtlLayout) {
         FeatureList.setTestFeatures(
diff --git a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
index 453f91d..0b1fb5cd 100644
--- a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
+++ b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
@@ -273,12 +273,26 @@
      *         created from the new Tab menu or "+" button, and it hasn't navigate to any URL yet.
      */
     public static boolean shouldHandleAsNtp(Tab tab) {
+        // TODO(https://crbug.com/1305397): Rule out a null url here and assert that it's non-null.
         if (tab == null || tab.getUrl() == null) return false;
 
         return tab.getUrl().isEmpty() && StartSurfaceUserData.getCreatedAsNtp(tab);
     }
 
     /**
+     * @return Whether the given tab with the given url should be treated as chrome://newTab. This
+     *         function returns true only when {@link OMNIBOX_FOCUSED_ON_NEW_TAB} is enabled, the
+     *         tab is newly created from the new Tab menu or "+" button, and it hasn't navigate to
+     *         any URL yet.
+     */
+    // TODO(https://crbug.com/1305374): migrate to GURL.
+    public static boolean shouldHandleAsNtp(Tab tab, String url) {
+        if (tab == null || url == null) return false;
+
+        return url.isEmpty() && StartSurfaceUserData.getCreatedAsNtp(tab);
+    }
+
+    /**
      * Sets the UserData if the Tab is a newly created empty Tab when
      * {@link OMNIBOX_FOCUSED_ON_NEW_TAB} is enabled.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
index df82b97..085f85b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
@@ -27,7 +27,6 @@
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
-import org.chromium.components.browser_ui.notifications.ForegroundServiceUtils;
 import org.chromium.components.browser_ui.notifications.NotificationMetadata;
 import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder;
 
@@ -369,7 +368,6 @@
      */
     private boolean canStartForeground() {
         if (!BuildInfo.isAtLeastS()) return true;
-        if (!ForegroundServiceUtils.canStartForegroundServiceExcludingMedia()) return true;
         // If foreground service is started, startForeground() must be called.
         return ApplicationStatus.hasVisibleActivities()
                 || (mIsServiceBound && !mStartForegroundCalled);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java
index 1870587..88afb798 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java
@@ -30,10 +30,10 @@
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.omnibox.LocationBarLayout;
 import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.test.util.ToolbarTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
@@ -42,13 +42,15 @@
 import org.chromium.components.security_state.SecurityStateModel;
 import org.chromium.components.security_state.SecurityStateModelJni;
 import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /**
- * Unit tests for {@link LocationBarLayout} class.
+ * Instrumentation tests for the toolbar security icon.
  */
 @RunWith(BaseJUnit4ClassRunner.class)
 @Batch(Batch.UNIT_TESTS)
-@Features.DisableFeatures(ChromeFeatureList.OMNIBOX_UPDATED_CONNECTION_SECURITY_INDICATORS)
+@Features.DisableFeatures({ChromeFeatureList.OMNIBOX_UPDATED_CONNECTION_SECURITY_INDICATORS,
+        ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS})
 public final class ToolbarSecurityIconTest {
     private static final boolean IS_SMALL_DEVICE = true;
     private static final boolean IS_OFFLINE_PAGE = true;
@@ -76,6 +78,8 @@
 
     @Mock
     private PrefService mMockPrefService;
+    @Mock
+    private Profile mMockProfile;
 
     /**
      * Set up the lock icon policy for Mock PrefService.
@@ -106,7 +110,8 @@
                         (url) -> url.getSpec(), (window) -> null, ToolbarTestUtils.OFFLINE_STATUS,
                         mSearchEngineLogoUtils));
         // clang-format on
-        mLocationBarModel.initializeWithNative();
+        Profile.setLastUsedProfileForTesting(mMockProfile);
+        TestThreadUtils.runOnUiThreadBlocking(() -> mLocationBarModel.initializeWithNative());
 
         doReturn(mMockPrefService).when(mLocationBarModel).getPrefService();
         setupLockIconPolicyForTests(false);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java
index be77670f..9ac1719 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/LocationBarModelUnitTest.java
@@ -4,41 +4,82 @@
 
 package org.chromium.chrome.browser.toolbar;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.text.SpannableStringBuilder;
+import android.util.LruCache;
 import android.view.ContextThemeWrapper;
 
+import androidx.annotation.Nullable;
 import androidx.test.filters.MediumTest;
 
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.incognito.IncognitoCctProfileManager;
 import org.chromium.chrome.browser.incognito.IncognitoUtils;
+import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifier;
+import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifierJni;
 import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
 import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TrustedCdn;
+import org.chromium.chrome.browser.toolbar.LocationBarModelUnitTest.ShadowTrustedCdn;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.components.dom_distiller.core.DomDistillerUrlUtilsJni;
+import org.chromium.components.omnibox.OmniboxUrlEmphasizerJni;
+import org.chromium.components.url_formatter.UrlFormatter;
+import org.chromium.components.url_formatter.UrlFormatterJni;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
+import org.chromium.url.ShadowGURL;
 
 /**
  * Unit tests for the LocationBarModel.
  */
 @RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowGURL.class, ShadowTrustedCdn.class})
+@DisableFeatures({ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS})
 public class LocationBarModelUnitTest {
+    @Implements(TrustedCdn.class)
+    static class ShadowTrustedCdn {
+        @Implementation
+        public static String getPublisherUrl(@Nullable Tab tab) {
+            return null;
+        }
+    }
+
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+    @Rule
+    public JniMocker mJniMocker = new JniMocker();
+
     @Mock
     private Tab mIncognitoTabMock;
 
@@ -63,11 +104,27 @@
     private LocationBarDataProvider.Observer mLocationBarDataObserver;
     @Mock
     private SearchEngineLogoUtils mSearchEngineLogoUtils;
+    @Mock
+    private LocationBarModel.Natives mLocationBarModelJni;
+    @Mock
+    private ChromeAutocompleteSchemeClassifier.Natives mChromeAutocompleteSchemeClassifierJni;
+    @Mock
+    private UrlFormatter.Natives mUrlFormatterJniMock;
+    @Mock
+    private DomDistillerUrlUtilsJni mDomDistillerUrlUtilsJni;
+    @Mock
+    private OmniboxUrlEmphasizerJni mOmniboxUrlEmphasizerJni;
+    @Mock
+    private GURL mMockGurl;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         Profile.setLastUsedProfileForTesting(mRegularProfileMock);
+        mJniMocker.mock(LocationBarModelJni.TEST_HOOKS, mLocationBarModelJni);
+        mJniMocker.mock(UrlFormatterJni.TEST_HOOKS, mUrlFormatterJniMock);
+        mJniMocker.mock(DomDistillerUrlUtilsJni.TEST_HOOKS, mDomDistillerUrlUtilsJni);
+        mJniMocker.mock(OmniboxUrlEmphasizerJni.TEST_HOOKS, mOmniboxUrlEmphasizerJni);
         IncognitoCctProfileManager.setIncognitoCctProfileManagerForTesting(
                 mIncognitoCctProfileManagerMock);
         when(mIncognitoCctProfileManagerMock.getProfile()).thenReturn(mNonPrimaryOTRProfileMock);
@@ -88,12 +145,12 @@
             new LocationBarModel.OfflineStatus() {
                 @Override
                 public boolean isShowingTrustedOfflinePage(WebContents webContents) {
-                    return true;
+                    return false;
                 }
 
                 @Override
                 public boolean isOfflinePage(Tab tab) {
-                    return true;
+                    return false;
                 }
             };
 
@@ -127,6 +184,7 @@
         when(mIncognitoCctProfileManagerMock.getProfile()).thenReturn(null);
         LocationBarModel incognitoLocationBarModel =
                 new TestIncognitoLocationBarModel(mIncognitoTabMock, mSearchEngineLogoUtils);
+        incognitoLocationBarModel.initializeWithNative();
         Profile otrProfile = incognitoLocationBarModel.getProfile();
         Assert.assertEquals(mPrimaryOTRProfileMock, otrProfile);
     }
@@ -136,6 +194,7 @@
     public void getProfile_IncognitoCCT_ReturnsNonPrimaryOTRProfile() {
         LocationBarModel incognitoLocationBarModel =
                 new TestIncognitoLocationBarModel(mIncognitoTabMock, mSearchEngineLogoUtils);
+        incognitoLocationBarModel.initializeWithNative();
         Profile otrProfile = incognitoLocationBarModel.getProfile();
         Assert.assertEquals(mNonPrimaryOTRProfileMock, otrProfile);
     }
@@ -145,6 +204,7 @@
     public void getProfile_NullTab_ReturnsPrimaryOTRProfile() {
         LocationBarModel incognitoLocationBarModel =
                 new TestIncognitoLocationBarModel(null, mSearchEngineLogoUtils);
+        incognitoLocationBarModel.initializeWithNative();
         Profile otrProfile = incognitoLocationBarModel.getProfile();
         Assert.assertEquals(mPrimaryOTRProfileMock, otrProfile);
     }
@@ -154,6 +214,7 @@
     public void getProfile_RegularTab_ReturnsRegularProfile() {
         LocationBarModel regularLocationBarModel =
                 new TestRegularLocationBarModel(mRegularTabMock, mSearchEngineLogoUtils);
+        regularLocationBarModel.initializeWithNative();
         Profile profile = regularLocationBarModel.getProfile();
         Assert.assertEquals(mRegularProfileMock, profile);
     }
@@ -163,6 +224,7 @@
     public void getProfile_NullTab_ReturnsRegularProfile() {
         LocationBarModel regularLocationBarModel =
                 new TestRegularLocationBarModel(null, mSearchEngineLogoUtils);
+        regularLocationBarModel.initializeWithNative();
         Profile profile = regularLocationBarModel.getProfile();
         Assert.assertEquals(mRegularProfileMock, profile);
     }
@@ -235,4 +297,38 @@
         verify(mLocationBarDataObserver).onPrimaryColorChanged();
         verify(mLocationBarDataObserver).onSecurityStateChanged();
     }
+
+    @EnableFeatures({ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS})
+    @Test
+    @MediumTest
+    public void testSpannableCache() {
+        doReturn(123L).when(mLocationBarModelJni).init(Mockito.any());
+        mJniMocker.mock(ChromeAutocompleteSchemeClassifierJni.TEST_HOOKS,
+                mChromeAutocompleteSchemeClassifierJni);
+        LocationBarModel regularLocationBarModel =
+                new TestRegularLocationBarModel(mRegularTabMock, mSearchEngineLogoUtils);
+        doReturn(true).when(mRegularTabMock).isInitialized();
+        regularLocationBarModel.initializeWithNative();
+        LruCache<LocationBarModel.SpannableDisplayTextCacheKey, SpannableStringBuilder> cache =
+                regularLocationBarModel.getCacheForTesting();
+        Assert.assertEquals(cache.size(), 0);
+
+        String url = "http://www.example.com/";
+        doReturn(url).when(mMockGurl).getSpec();
+        doReturn(mMockGurl).when(mRegularTabMock).getUrl();
+        doReturn(url)
+                .when(mLocationBarModelJni)
+                .getFormattedFullURL(Mockito.anyLong(), Mockito.any());
+        doReturn(url).when(mLocationBarModelJni).getURLForDisplay(Mockito.anyLong(), Mockito.any());
+        doReturn(mMockGurl).when(mUrlFormatterJniMock).fixupUrl(url);
+        doReturn(new int[] {0, 7, 7, 15})
+                .when(mOmniboxUrlEmphasizerJni)
+                .parseForEmphasizeComponents(any(), any());
+        regularLocationBarModel.getUrlBarData();
+        Assert.assertEquals(cache.size(), 1);
+        regularLocationBarModel.getUrlBarData();
+        Assert.assertEquals(cache.hitCount(), 1);
+
+        regularLocationBarModel.destroy();
+    }
 }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 46695e1..261c4a3a 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-101.0.4948.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-101.0.4950.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 2ae848c..c5a62ce 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -225,7 +225,7 @@
 #define IDC_CLOSE_SIGN_IN_PROMO        40258
 #define IDC_SHOW_FULL_URLS             40259
 #define IDC_CARET_BROWSING_TOGGLE      40260
-#define IDC_TOGGLE_COMMANDER     40261
+#define IDC_TOGGLE_QUICK_COMMANDS     40261
 #define IDC_CHROME_TIPS                40263
 #define IDC_CHROME_WHATS_NEW           40264
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 42f3041..3491c3b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1177,8 +1177,8 @@
           <message name="IDS_MOVE_TAB_TO_NEW_WINDOW" desc="The text label of the Move Tab to Window menu item.">
             Move tab to new window
           </message>
-          <message name="IDS_TOGGLE_COMMANDER" desc="The text label of the Toggle Quick Commands menu item">
-          Toggle Quick Commands
+          <message name="IDS_TOGGLE_QUICK_COMMANDS" desc="The text label of the Toggle Quick Commands menu item">
+          Quick commands
           </message>
         </if>
         <if expr="use_titlecase">
@@ -1260,8 +1260,8 @@
           <message name="IDS_MOVE_TAB_TO_NEW_WINDOW" desc="In Title Case: The text label of the Move Tab to New Window menu item.">
             Move Tab to New Window
           </message>
-          <message name="IDS_TOGGLE_COMMANDER" desc="In Title Case:The text label of the Toggle Quick Commands menu item">
-          Toggle Quick Commands
+          <message name="IDS_TOGGLE_QUICK_COMMANDS" desc="In Title Case:The text label of the Toggle Quick Commands menu item">
+          Quick Commands
           </message>
         </if>
       </if>
@@ -12454,9 +12454,17 @@
              desc="Title of the prompt to choose a new name for a window.">
       Name this window
     </message>
-    <message name="IDS_COMMANDER_LABEL"
-             desc="Label to identify the Commander feature in Task Manager">
-      Commander
+    <message name="IDS_QUICK_COMMANDS_LABEL"
+             desc="Label to identify the Quick Commands feature in Task Manager">
+      Quick Commands
+    </message>
+    <message name="IDS_QUICK_COMMANDS_PLACEHOLDER"
+             desc="Placeholder text for the Quick Commands text input">
+      Enter a keyword like "tabs" or "windows" to find an action
+    </message>
+    <message name="IDS_QUICK_COMMANDS_NO_RESULTS"
+             desc="Shown when there are no results in Quick Commands">
+      No commands found
     </message>
 
     <!-- ChromeLabs bubble -->
diff --git a/chrome/app/generated_resources_grd/IDS_COMMANDER_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_COMMANDER_LABEL.png.sha1
deleted file mode 100644
index 7909895..0000000
--- a/chrome/app/generated_resources_grd/IDS_COMMANDER_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bb35663c6800e28b389ec3ed50789794131bff82
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_LABEL.png.sha1
new file mode 100644
index 0000000..6bfa2fc
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_LABEL.png.sha1
@@ -0,0 +1 @@
+e305501bbd68e9722c49b0da2128b9abc4c56f30
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_NO_RESULTS.png.sha1 b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_NO_RESULTS.png.sha1
new file mode 100644
index 0000000..fd544a5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_NO_RESULTS.png.sha1
@@ -0,0 +1 @@
+185849eea17493d7cad20948a2f24afb927bf31e
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_PLACEHOLDER.png.sha1 b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_PLACEHOLDER.png.sha1
new file mode 100644
index 0000000..527942ba
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_QUICK_COMMANDS_PLACEHOLDER.png.sha1
@@ -0,0 +1 @@
+d35ab5350649d7d682cd092c813acc9ab361163a
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TOGGLE_COMMANDER.png.sha1 b/chrome/app/generated_resources_grd/IDS_TOGGLE_COMMANDER.png.sha1
deleted file mode 100644
index 24ceb35..0000000
--- a/chrome/app/generated_resources_grd/IDS_TOGGLE_COMMANDER.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2d630d3c9160d9dc531a91ff4f01b0042ae9d952
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TOGGLE_QUICK_COMMANDS.png.sha1 b/chrome/app/generated_resources_grd/IDS_TOGGLE_QUICK_COMMANDS.png.sha1
new file mode 100644
index 0000000..e2edf8a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_TOGGLE_QUICK_COMMANDS.png.sha1
@@ -0,0 +1 @@
+fad73dc15316d3dfad90ebc691577fb7cb76944e
\ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 951af578..e573027 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -208,6 +208,10 @@
     sources += [
       "autocorrect_undo.icon",
       "full_restore_notification.icon",
+      "mouse_left_click_edit.icon",
+      "mouse_left_click_view.icon",
+      "mouse_right_click_edit.icon",
+      "mouse_right_click_view.icon",
       "notification_battery.icon",
       "notification_captive_portal.icon",
       "notification_cellular_alert.icon",
diff --git a/chrome/app/vector_icons/mouse_left_click_edit.icon b/chrome/app/vector_icons/mouse_left_click_edit.icon
new file mode 100644
index 0000000..3e9764b5
--- /dev/null
+++ b/chrome/app/vector_icons/mouse_left_click_edit.icon
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24,
+MOVE_TO, 10, 2,
+CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f,
+R_V_LINE_TO, 4.8f,
+R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f,
+R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f,
+V_LINE_TO, 7.6f,
+CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2,
+CLOSE,
+R_MOVE_TO, 4, 5.5f,
+V_LINE_TO, 8,
+R_H_LINE_TO, -3,
+V_LINE_TO, 4,
+R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f,
+CLOSE,
+MOVE_TO, 9, 4,
+R_V_LINE_TO, 4,
+H_LINE_TO, 6,
+R_V_LINE_TO, -0.4f,
+CUBIC_TO, 6, 6, 7, 4, 9, 4,
+CLOSE,
+R_MOVE_TO, 1, 12,
+R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f,
+V_LINE_TO, 10,
+R_H_LINE_TO, 8,
+R_V_LINE_TO, 2.4f,
+R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24,
+MOVE_TO, 6, 4,
+R_H_LINE_TO, 3,
+R_V_LINE_TO, 4,
+H_LINE_TO, 6,
+CLOSE
diff --git a/chrome/app/vector_icons/mouse_left_click_view.icon b/chrome/app/vector_icons/mouse_left_click_view.icon
new file mode 100644
index 0000000..13fbbbc
--- /dev/null
+++ b/chrome/app/vector_icons/mouse_left_click_view.icon
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF,
+MOVE_TO, 10, 2,
+CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f,
+R_V_LINE_TO, 4.8f,
+R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f,
+R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f,
+V_LINE_TO, 7.6f,
+CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2,
+CLOSE,
+R_MOVE_TO, 4, 5.5f,
+V_LINE_TO, 8,
+R_H_LINE_TO, -3,
+V_LINE_TO, 4,
+R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f,
+CLOSE,
+MOVE_TO, 9, 4,
+R_V_LINE_TO, 4,
+H_LINE_TO, 6,
+R_V_LINE_TO, -0.4f,
+CUBIC_TO, 6, 6, 7, 4, 9, 4,
+CLOSE,
+R_MOVE_TO, 1, 12,
+R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f,
+V_LINE_TO, 10,
+R_H_LINE_TO, 8,
+R_V_LINE_TO, 2.4f,
+R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF,
+MOVE_TO, 6, 4,
+R_H_LINE_TO, 3,
+R_V_LINE_TO, 4,
+H_LINE_TO, 6,
+CLOSE
diff --git a/chrome/app/vector_icons/mouse_right_click_edit.icon b/chrome/app/vector_icons/mouse_right_click_edit.icon
new file mode 100644
index 0000000..5ac40cc7
--- /dev/null
+++ b/chrome/app/vector_icons/mouse_right_click_edit.icon
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24,
+MOVE_TO, 10, 2,
+CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f,
+R_V_LINE_TO, 4.8f,
+R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f,
+R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f,
+V_LINE_TO, 7.6f,
+CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2,
+CLOSE,
+R_MOVE_TO, 4, 5.5f,
+V_LINE_TO, 8,
+R_H_LINE_TO, -3,
+V_LINE_TO, 4,
+R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f,
+CLOSE,
+MOVE_TO, 9, 4,
+R_V_LINE_TO, 4,
+H_LINE_TO, 6,
+R_V_LINE_TO, -0.4f,
+CUBIC_TO, 6, 6, 7, 4, 9, 4,
+CLOSE,
+R_MOVE_TO, 1, 12,
+R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f,
+V_LINE_TO, 10,
+R_H_LINE_TO, 8,
+R_V_LINE_TO, 2.4f,
+R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0x20, 0x21, 0x24,
+MOVE_TO, 11, 4,
+R_H_LINE_TO, 3,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, -3,
+CLOSE
diff --git a/chrome/app/vector_icons/mouse_right_click_view.icon b/chrome/app/vector_icons/mouse_right_click_view.icon
new file mode 100644
index 0000000..fb042292
--- /dev/null
+++ b/chrome/app/vector_icons/mouse_right_click_view.icon
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF,
+MOVE_TO, 10, 2,
+CUBIC_TO, 6.68f, 2, 4, 4.5f, 4, 7.6f,
+R_V_LINE_TO, 4.8f,
+R_CUBIC_TO, 0, 3.1f, 2.68f, 5.6f, 6, 5.6f,
+R_CUBIC_TO, 3.32f, 0, 6, -2.5f, 6, -5.6f,
+V_LINE_TO, 7.6f,
+CUBIC_TO, 16, 4.5f, 13.32f, 2, 10, 2,
+CLOSE,
+R_MOVE_TO, 4, 5.5f,
+V_LINE_TO, 8,
+R_H_LINE_TO, -3,
+V_LINE_TO, 4,
+R_CUBIC_TO, 2, 0, 3, 2, 3, 3.5f,
+CLOSE,
+MOVE_TO, 9, 4,
+R_V_LINE_TO, 4,
+H_LINE_TO, 6,
+R_V_LINE_TO, -0.4f,
+CUBIC_TO, 6, 6, 7, 4, 9, 4,
+CLOSE,
+R_MOVE_TO, 1, 12,
+R_CUBIC_TO, -2.37f, 0, -4, -1.39f, -4, -3.6f,
+V_LINE_TO, 10,
+R_H_LINE_TO, 8,
+R_V_LINE_TO, 2.4f,
+R_CUBIC_TO, 0, 2.21f, -1.63f, 3.6f, -4, 3.6f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF,
+MOVE_TO, 11, 4,
+R_H_LINE_TO, 3,
+R_V_LINE_TO, 4,
+R_H_LINE_TO, -3,
+CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a6b6811..1e48a3b6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -396,11 +396,6 @@
     "domain_reliability/service_factory.h",
     "download/background_download_service_factory.cc",
     "download/background_download_service_factory.h",
-    "download/bubble/download_display.cc",
-    "download/bubble/download_display.h",
-    "download/bubble/download_display_controller.cc",
-    "download/bubble/download_display_controller.h",
-    "download/bubble/download_icon_state.h",
     "download/chrome_download_manager_delegate.cc",
     "download/chrome_download_manager_delegate.h",
     "download/deferred_client_wrapper.cc",
@@ -1496,6 +1491,8 @@
     "security_events/security_event_sync_bridge_impl.h",
     "segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.cc",
     "segmentation_platform/chrome_browser_main_extra_parts_segmentation_platform.h",
+    "segmentation_platform/model_provider_factory_impl.cc",
+    "segmentation_platform/model_provider_factory_impl.h",
     "segmentation_platform/segmentation_platform_config.cc",
     "segmentation_platform/segmentation_platform_config.h",
     "segmentation_platform/segmentation_platform_profile_observer.cc",
@@ -3594,6 +3591,8 @@
       "apps/intent_helper/intent_picker_auto_display_service_factory.cc",
       "apps/intent_helper/intent_picker_auto_display_service_factory.h",
       "apps/intent_helper/intent_picker_constants.h",
+      "apps/intent_helper/intent_picker_features.cc",
+      "apps/intent_helper/intent_picker_features.h",
       "apps/intent_helper/intent_picker_helpers.cc",
       "apps/intent_helper/intent_picker_helpers.h",
       "apps/intent_helper/intent_picker_internal.cc",
@@ -3620,8 +3619,6 @@
       "cart/cart_discount_link_fetcher.h",
       "cart/cart_discount_metric_collector.cc",
       "cart/cart_discount_metric_collector.h",
-      "cart/cart_features.cc",
-      "cart/cart_features.h",
       "cart/cart_handler.cc",
       "cart/cart_handler.h",
       "cart/cart_metrics_tracker.cc",
@@ -3648,6 +3645,8 @@
       "commerce/coupons/coupon_service_observer.h",
       "component_updater/commerce_heuristics_component_installer.cc",
       "component_updater/commerce_heuristics_component_installer.h",
+      "component_updater/desktop_screenshot_editor_component_installer.cc",
+      "component_updater/desktop_screenshot_editor_component_installer.h",
       "component_updater/desktop_sharing_hub_component_installer.cc",
       "component_updater/desktop_sharing_hub_component_installer.h",
       "component_updater/intervention_policy_database_component_installer.cc",
@@ -3695,6 +3694,15 @@
       "diagnostics/recon_diagnostics.h",
       "diagnostics/sqlite_diagnostics.cc",
       "diagnostics/sqlite_diagnostics.h",
+      "download/bubble/download_bubble_controller.cc",
+      "download/bubble/download_bubble_controller.h",
+      "download/bubble/download_bubble_prefs.cc",
+      "download/bubble/download_bubble_prefs.h",
+      "download/bubble/download_display.cc",
+      "download/bubble/download_display.h",
+      "download/bubble/download_display_controller.cc",
+      "download/bubble/download_display_controller.h",
+      "download/bubble/download_icon_state.h",
       "download/default_download_dir_policy_handler.cc",
       "download/default_download_dir_policy_handler.h",
       "download/download_commands.cc",
@@ -5208,6 +5216,8 @@
       "lacros/lacros_url_handling.h",
       "lacros/launcher_search/search_controller_lacros.cc",
       "lacros/launcher_search/search_controller_lacros.h",
+      "lacros/launcher_search/search_util.cc",
+      "lacros/launcher_search/search_util.h",
       "lacros/metrics_reporting_observer.cc",
       "lacros/metrics_reporting_observer.h",
       "lacros/net/lacros_extension_proxy_tracker.cc",
@@ -7311,6 +7321,7 @@
       "permissions/prediction_model_handler_factory.cc",
       "permissions/prediction_model_handler_factory.h",
     ]
+    deps += [ "//components/segmentation_platform/internal:optimization_guide_segmentation_handler" ]
   }
 }
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index bdb3367..80c69ea7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -210,6 +210,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chromeos/constants/chromeos_features.h"
 #endif
 
@@ -1001,6 +1002,15 @@
      std::size(kPageContentAnnotationsTitleParams), nullptr},
 };
 const FeatureEntry::FeatureParam
+    kJourneysSortClustersWithinBatchForQueryParams[] = {
+        {"JourneysSortClustersWithinBatchForQuery", "true"},
+};
+const FeatureEntry::FeatureVariation kJourneysVariations[] = {
+    {"Sort Clusters Within Batch for Query",
+     kJourneysSortClustersWithinBatchForQueryParams,
+     std::size(kJourneysSortClustersWithinBatchForQueryParams), nullptr},
+};
+const FeatureEntry::FeatureParam
     kJourneysOnDeviceClusteringLabelingNoContentClusteringParams[] = {
         {"should_label_clusters", "true"},
         {"content_clustering_enabled", "false"},
@@ -1508,11 +1518,11 @@
 
 // The following are consent v2 variations in the Chrome Cart module.
 const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpStringChange[] = {
-    {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "1"}};
+    {commerce::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "1"}};
 const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpInline[] = {
-    {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "2"}};
+    {commerce::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "2"}};
 const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpDialog[] = {
-    {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "3"}};
+    {commerce::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "3"}};
 const FeatureEntry::FeatureVariation kDiscountConsentV2Variations[] = {
     {"Changing string", kDiscountConsentNtpStringChange,
      std::size(kDiscountConsentNtpStringChange), nullptr},
@@ -2935,6 +2945,13 @@
     {"100", &kUnthrottledNestedTimeout_NestingLevel, 1, nullptr},
 };
 
+constexpr FeatureEntry::FeatureParam kLensStandaloneWithSidePanel[] = {
+    {"enable-side-panel", "true"}};
+constexpr FeatureEntry::FeatureVariation kLensStandaloneVariations[] = {
+    {"With Side Panel", kLensStandaloneWithSidePanel,
+     std::size(kLensStandaloneWithSidePanel), nullptr},
+};
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the entry is the internal name.
@@ -4098,6 +4115,18 @@
      flag_descriptions::kCastStreamingVp9Description, kOsDesktop,
      FEATURE_VALUE_TYPE(mirroring::features::kCastStreamingVp9)},
 
+    {"enable-cast-remoting-query-blocklist",
+     flag_descriptions::kCastUseBlocklistForRemotingQueryName,
+     flag_descriptions::kCastUseBlocklistForRemotingQueryDescription,
+     kOsDesktop,
+     FEATURE_VALUE_TYPE(
+         mirroring::features::kCastUseBlocklistForRemotingQuery)},
+
+    {"force-enable-cast-remoting-query",
+     flag_descriptions::kCastForceEnableRemotingQueryName,
+     flag_descriptions::kCastForceEnableRemotingQueryDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(mirroring::features::kCastForceEnableRemotingQuery)},
+
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
@@ -5073,7 +5102,9 @@
 
     {"history-journeys", flag_descriptions::kJourneysName,
      flag_descriptions::kJourneysDescription, kOsDesktop | kOsAndroid,
-     FEATURE_VALUE_TYPE(history_clusters::internal::kJourneys)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(history_clusters::internal::kJourneys,
+                                    kJourneysVariations,
+                                    "HistoryJourneys")},
 
     {"history-journeys-omnibox-action",
      flag_descriptions::kJourneysOmniboxActionName,
@@ -5770,28 +5801,29 @@
          "CCTResizableThirdPartiesDefaultPolicy")},
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
     {"allow-dsp-based-aec", flag_descriptions::kCrOSDspBasedAecAllowedName,
-     flag_descriptions::kCrOSDspBasedAecAllowedDescription, kOsCrOS,
+     flag_descriptions::kCrOSDspBasedAecAllowedDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(features::kCrOSDspBasedAecAllowed)},
     {"allow-dsp-based-ns", flag_descriptions::kCrOSDspBasedNsAllowedName,
-     flag_descriptions::kCrOSDspBasedNsAllowedDescription, kOsCrOS,
+     flag_descriptions::kCrOSDspBasedNsAllowedDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(features::kCrOSDspBasedNsAllowed)},
     {"allow-dsp-based-agc", flag_descriptions::kCrOSDspBasedAgcAllowedName,
-     flag_descriptions::kCrOSDspBasedAgcAllowedDescription, kOsCrOS,
+     flag_descriptions::kCrOSDspBasedAgcAllowedDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(features::kCrOSDspBasedAgcAllowed)},
     {"enforce-system-aec", flag_descriptions::kCrOSEnforceSystemAecName,
-     flag_descriptions::kCrOSEnforceSystemAecDescription, kOsCrOS,
+     flag_descriptions::kCrOSEnforceSystemAecDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAec)},
     {"enforce-system-aec-agc", flag_descriptions::kCrOSEnforceSystemAecAgcName,
-     flag_descriptions::kCrOSEnforceSystemAecAgcDescription, kOsCrOS,
+     flag_descriptions::kCrOSEnforceSystemAecAgcDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAecAgc)},
     {"enforce-system-aec-ns-agc",
      flag_descriptions::kCrOSEnforceSystemAecNsAgcName,
-     flag_descriptions::kCrOSEnforceSystemAecNsAgcDescription, kOsCrOS,
+     flag_descriptions::kCrOSEnforceSystemAecNsAgcDescription,
+     kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAecNsAgc)},
     {"enforce-system-aec-ns", flag_descriptions::kCrOSEnforceSystemAecNsName,
-     flag_descriptions::kCrOSEnforceSystemAecNsDescription, kOsCrOS,
+     flag_descriptions::kCrOSEnforceSystemAecNsDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(features::kCrOSEnforceSystemAecNs)},
 #endif
 
@@ -6486,6 +6518,11 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kLensCameraAssistedSearch,
                                     kLensCameraAssistedSearchVariations,
                                     "LensCameraAssistedSearch")},
+
+    {"location-bar-model-optimizations",
+     flag_descriptions::kLocationBarModelOptimizationsName,
+     flag_descriptions::kLocationBarModelOptimizationsDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kLocationBarModelOptimizations)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -7637,10 +7674,12 @@
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillSuggestVirtualCardsOnIncompleteForm)},
 
-    {flag_descriptions::kEnableLensRegionSearchFlagId,
-     flag_descriptions::kEnableLensRegionSearchName,
-     flag_descriptions::kEnableLensRegionSearchDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(lens::features::kLensRegionSearch)},
+    {flag_descriptions::kEnableLensStandaloneFlagId,
+     flag_descriptions::kEnableLensStandaloneName,
+     flag_descriptions::kEnableLensStandaloneDescription, kOsDesktop,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(lens::features::kLensStandalone,
+                                    kLensStandaloneVariations,
+                                    "GoogleLensDesktopContextMenuSearch")},
 
     {"enable-penetrating-image-selection",
      flag_descriptions::kEnablePenetratingImageSelectionName,
@@ -7811,8 +7850,13 @@
      FEATURE_VALUE_TYPE(net::features::kSamePartyCookiesConsideredFirstParty)},
 
     {"partitioned-cookies", flag_descriptions::kPartitionedCookiesName,
-     flag_descriptions::kPartitionedCookiesDescription, kOsDesktop | kOsAndroid,
+     flag_descriptions::kPartitionedCookiesDescription, kOsAll,
      FEATURE_VALUE_TYPE(net::features::kPartitionedCookies)},
+    // TODO(crbug.com/1296161): Remove this flag when the CHIPS OT ends.
+    {"partitioned-cookies-bypass-origin-trial",
+     flag_descriptions::kPartitionedCookiesBypassOriginTrialName,
+     flag_descriptions::kPartitionedCookiesBypassOriginTrialDescription, kOsAll,
+     FEATURE_VALUE_TYPE(net::features::kPartitionedCookiesBypassOriginTrial)},
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {kBorealisBigGlInternalName, flag_descriptions::kBorealisBigGlName,
@@ -8158,7 +8202,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
     {"link-capturing-ui-update", flag_descriptions::kLinkCapturingUiUpdateName,
      flag_descriptions::kLinkCapturingUiUpdateDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(features::kLinkCapturingUiUpdate)},
+     FEATURE_VALUE_TYPE(apps::features::kLinkCapturingUiUpdate)},
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
@@ -8365,6 +8409,17 @@
      flag_descriptions::kOmitCorsClientCertDescription, kOsAll,
      FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)},
 
+#if BUILDFLAG(IS_CHROMEOS)
+    {"link-capturing-infobar", flag_descriptions::kLinkCapturingInfoBarName,
+     flag_descriptions::kLinkCapturingInfoBarDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(apps::features::kLinkCapturingInfoBar)},
+
+    {"intent-chip-skips-intent-picker",
+     flag_descriptions::kIntentChipSkipsPickerName,
+     flag_descriptions::kIntentChipSkipsPickerDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(apps::features::kIntentChipSkipsPicker)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.cc b/chrome/browser/apps/app_service/app_service_proxy_base.cc
index e6eb6d8..b816ae8 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.cc
@@ -20,7 +20,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "components/services/app_service/app_service_mojom_impl.h"
-#include "components/services/app_service/public/cpp/intent_constants.h"
+#include "components/services/app_service/public/cpp/intent.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/cpp/types_util.h"
@@ -46,9 +47,9 @@
   bool is_generic;
 };
 
-std::string GetActivityLabel(const apps::mojom::IntentFilterPtr& filter,
-                             const apps::AppUpdate& update) {
-  if (filter->activity_label && !filter->activity_label->empty()) {
+std::string GetActivityLabel(const IntentFilterPtr& filter,
+                             const AppUpdate& update) {
+  if (filter->activity_label.has_value() && !filter->activity_label->empty()) {
     return filter->activity_label.value();
   } else {
     return update.Name();
@@ -497,12 +498,17 @@
 }
 
 std::vector<IntentLaunchInfo> AppServiceProxyBase::GetAppsForIntent(
-    const apps::mojom::IntentPtr& intent,
+    const apps::mojom::IntentPtr& mojom_intent,
     bool exclude_browsers,
     bool exclude_browser_tab_apps) {
   std::vector<IntentLaunchInfo> intent_launch_info;
-  if (apps_util::OnlyShareToDrive(intent) ||
-      !apps_util::IsIntentValid(intent)) {
+  if (apps_util::OnlyShareToDrive(mojom_intent) ||
+      !apps_util::IsIntentValid(mojom_intent)) {
+    return intent_launch_info;
+  }
+
+  auto intent = ConvertMojomIntentToIntent(mojom_intent);
+  if (!intent) {
     return intent_launch_info;
   }
 
@@ -526,14 +532,14 @@
       }
       // |activity_label| -> {index, is_generic}
       std::map<std::string, IndexAndGeneric> best_handler_map;
-      bool is_file_handling_intent =
-          intent->files.has_value() && intent->files->size() > 0;
+      bool is_file_handling_intent = !intent->files.empty();
       size_t index = 0;
       for (const auto& filter : update.IntentFilters()) {
-        if (exclude_browsers && apps_util::IsBrowserFilter(filter)) {
+        DCHECK(filter);
+        if (exclude_browsers && filter->IsBrowserFilter()) {
           continue;
         }
-        if (apps_util::IntentMatchesFilter(intent, filter)) {
+        if (intent->MatchFilter(filter)) {
           // Return the first non-generic match if it exists, otherwise the
           // first generic match.
           bool generic = false;
@@ -553,16 +559,14 @@
       }
       const auto& filters = update.IntentFilters();
       for (const auto& handler_entry : best_handler_map) {
-        const mojom::IntentFilterPtr& filter =
-            filters[handler_entry.second.index];
+        const IntentFilterPtr& filter = filters[handler_entry.second.index];
         IntentLaunchInfo entry;
         entry.app_id = update.AppId();
         entry.activity_label = GetActivityLabel(filter, update);
         entry.activity_name = filter->activity_name.value_or("");
         entry.is_generic_file_handler =
             apps_util::IsGenericFileHandler(intent, filter);
-        entry.is_file_extension_match =
-            apps_util::FilterIsForFileExtensions(filter);
+        entry.is_file_extension_match = filter->IsFileExtensionsFilter();
         intent_launch_info.push_back(entry);
       }
     });
@@ -598,7 +602,7 @@
   // Treat kUseBrowserForLink like an app with a single supported link, so
   // that any apps with overlapping supported links will have their preference
   // removed correctly.
-  if (app_id == apps::kUseBrowserForLink) {
+  if (app_id == apps_util::kUseBrowserForLink) {
     std::vector<apps::mojom::IntentFilterPtr> filters;
     filters.push_back(std::move(intent_filter));
     app_service_->SetSupportedLinksPreference(apps::mojom::AppType::kUnknown,
@@ -630,7 +634,7 @@
       app_id, [&app_id, &filters](const AppUpdate& app) {
         for (auto& filter : app.IntentFilters()) {
           if (apps_util::IsSupportedLinkForApp(app_id, filter)) {
-            filters.push_back(std::move(filter));
+            filters.push_back(ConvertIntentFilterToMojomIntentFilter(filter));
           }
         }
       });
@@ -701,25 +705,30 @@
 }
 
 apps::mojom::IntentFilterPtr AppServiceProxyBase::FindBestMatchingFilter(
-    const apps::mojom::IntentPtr& intent) {
+    const apps::mojom::IntentPtr& mojom_intent) {
   apps::mojom::IntentFilterPtr best_matching_intent_filter;
-  if (!app_service_.is_bound()) {
+  if (!app_service_.is_bound() || !mojom_intent) {
     return best_matching_intent_filter;
   }
 
-  int best_match_level = apps_util::IntentFilterMatchLevel::kNone;
+  auto intent = ConvertMojomIntentToIntent(mojom_intent);
+  if (!intent) {
+    return best_matching_intent_filter;
+  }
+  int best_match_level = static_cast<int>(IntentFilterMatchLevel::kNone);
   app_registry_cache_.ForEachApp(
       [&intent, &best_match_level,
        &best_matching_intent_filter](const apps::AppUpdate& update) {
         for (const auto& filter : update.IntentFilters()) {
-          if (!apps_util::IntentMatchesFilter(intent, filter)) {
+          if (!intent->MatchFilter(filter)) {
             continue;
           }
-          auto match_level = apps_util::GetFilterMatchLevel(filter);
+          auto match_level = filter->GetFilterMatchLevel();
           if (match_level <= best_match_level) {
             continue;
           }
-          best_matching_intent_filter = filter->Clone();
+          best_matching_intent_filter =
+              ConvertIntentFilterToMojomIntentFilter(filter);
           best_match_level = match_level;
         }
       });
diff --git a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
index c726d84..3a218d1 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_lacros.cc
@@ -462,12 +462,17 @@
 }
 
 std::vector<IntentLaunchInfo> AppServiceProxyLacros::GetAppsForIntent(
-    const apps::mojom::IntentPtr& intent,
+    const apps::mojom::IntentPtr& mojom_intent,
     bool exclude_browsers,
     bool exclude_browser_tab_apps) {
   std::vector<IntentLaunchInfo> intent_launch_info;
-  if (apps_util::OnlyShareToDrive(intent) ||
-      !apps_util::IsIntentValid(intent)) {
+  if (apps_util::OnlyShareToDrive(mojom_intent) ||
+      !apps_util::IsIntentValid(mojom_intent)) {
+    return intent_launch_info;
+  }
+
+  auto intent = ConvertMojomIntentToIntent(mojom_intent);
+  if (!intent) {
     return intent_launch_info;
   }
 
@@ -485,10 +490,11 @@
           }
           std::set<std::string> existing_activities;
           for (const auto& filter : update.IntentFilters()) {
-            if (exclude_browsers && apps_util::IsBrowserFilter(filter)) {
+            DCHECK(filter);
+            if (exclude_browsers && filter->IsBrowserFilter()) {
               continue;
             }
-            if (apps_util::IntentMatchesFilter(intent, filter)) {
+            if (intent->MatchFilter(filter)) {
               IntentLaunchInfo entry;
               entry.app_id = update.AppId();
               std::string activity_label;
diff --git a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
index e65f0ddd..958316d5 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
@@ -16,7 +16,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
-#include "components/services/app_service/public/cpp/intent_constants.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
 #include "components/services/app_service/public/cpp/intent_test_util.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
@@ -483,24 +482,24 @@
 
   // Setting "use browser" for a URL currently handled by App 1 should unset
   // both of App 1's links.
-  proxy()->AddPreferredApp(apps::kUseBrowserForLink, kTestUrl1);
+  proxy()->AddPreferredApp(apps_util::kUseBrowserForLink, kTestUrl1);
   proxy()->FlushMojoCallsForTesting();
 
-  ASSERT_EQ(apps::kUseBrowserForLink,
+  ASSERT_EQ(apps_util::kUseBrowserForLink,
             proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl1));
   ASSERT_EQ(absl::nullopt,
             proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl2));
 
-  proxy()->AddPreferredApp(apps::kUseBrowserForLink, kTestUrl3);
+  proxy()->AddPreferredApp(apps_util::kUseBrowserForLink, kTestUrl3);
   proxy()->FlushMojoCallsForTesting();
-  ASSERT_EQ(apps::kUseBrowserForLink,
+  ASSERT_EQ(apps_util::kUseBrowserForLink,
             proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl3));
 
   // Changing the setting back from "use browser" to App 1 should only update
   // that "use-browser" setting, settings for other URLs are unchanged.
   proxy()->AddPreferredApp(kTestAppId1, kTestUrl1);
   proxy()->FlushMojoCallsForTesting();
-  ASSERT_EQ(apps::kUseBrowserForLink,
+  ASSERT_EQ(apps_util::kUseBrowserForLink,
             proxy()->PreferredApps().FindPreferredAppForUrl(kTestUrl3));
 }
 
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
index 719875db..c66d037 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
@@ -130,7 +130,7 @@
     apps::AppServiceProxyFactory::GetForProfile(profile())
         ->AppRegistryCache()
         .ForApp(app_id, [&target](const apps::AppUpdate& update) {
-          target = update.GetIntentFilters();
+          target = update.IntentFilters();
         });
 
     EXPECT_EQ(source.size(), target.size());
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
index c32e4a1e..3dfd3889 100644
--- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -326,7 +326,7 @@
     apps::AppServiceProxyFactory::GetForProfile(profile())
         ->AppRegistryCache()
         .ForApp(app_id, [&target](const apps::AppUpdate& update) {
-          target = update.GetIntentFilters();
+          target = update.IntentFilters();
         });
 
     EXPECT_EQ(source.size(), target.size());
diff --git a/chrome/browser/apps/app_service/webapk/webapk_manager.cc b/chrome/browser/apps/app_service/webapk/webapk_manager.cc
index 3f326c4..3ef17c89 100644
--- a/chrome/browser/apps/app_service/webapk/webapk_manager.cc
+++ b/chrome/browser/apps/app_service/webapk/webapk_manager.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
+#include "components/services/app_service/public/cpp/intent.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/mojom/types.mojom-shared.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -32,11 +33,10 @@
 constexpr char kGeneratedWebApkPackagePrefix[] = "org.chromium.webapk.";
 
 bool HasShareIntentFilter(const apps::AppUpdate& app) {
-  auto intent = apps::mojom::Intent::New();
-  intent->action = apps_util::kIntentActionSend;
+  auto intent = std::make_unique<apps::Intent>(apps_util::kIntentActionSend);
   for (const auto& filter : app.IntentFilters()) {
     for (const auto& condition : filter->conditions) {
-      if (apps_util::IntentMatchesCondition(intent, condition)) {
+      if (intent->MatchCondition(condition)) {
         return true;
       }
     }
diff --git a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
index 5b40f452..5193dae 100644
--- a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
+++ b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac.cc
@@ -230,10 +230,8 @@
 
     // Validate that the scheme is something that could be registered by the PWA
     // via the manifest.
-    bool has_custom_scheme_prefix = false;
-    if (!blink::IsValidCustomHandlerScheme(url.scheme(),
-                                           /* allow_ext_plus_prefix */ false,
-                                           has_custom_scheme_prefix)) {
+    if (!blink::IsValidCustomHandlerScheme(
+            url.scheme(), blink::ProtocolHandlerSecurityLevel::kStrict)) {
       DLOG(ERROR) << "Protocol is not a valid custom handler scheme.";
       continue;
     }
diff --git a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
index 785a8d6..db419252 100644
--- a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
@@ -9,21 +9,20 @@
 
 #include "base/bind.h"
 #include "base/debug/dump_without_crashing.h"
-#include "base/feature_list.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_internal.h"
 #include "chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h"
 #include "chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
-#include "chrome/common/chrome_features.h"
-#include "components/services/app_service/public/cpp/intent_constants.h"
+#include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/visibility.h"
@@ -51,7 +50,7 @@
   const GURL& url = navigation_handle->GetURL();
 
   // Disable Auto-display when the Intent Chip is enabled.
-  if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate))
+  if (features::LinkCapturingUiUpdateEnabled())
     return false;
 
   if (apps_for_picker.empty())
@@ -88,7 +87,7 @@
   if (proxy) {
     auto preferred_app_id = proxy->PreferredApps().FindPreferredAppForUrl(url);
     if (preferred_app_id.has_value() &&
-        preferred_app_id.value() == apps::kUseBrowserForLink) {
+        preferred_app_id.value() == apps_util::kUseBrowserForLink) {
       return false;
     }
   }
@@ -218,7 +217,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     // TODO(crbug.com/1293173): Lacros support for the infobar UI.
-    if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) {
+    if (features::LinkCapturingInfoBarEnabled()) {
       SupportedLinksInfoBarDelegate::MaybeShowSupportedLinksInfoBar(
           web_contents, launch_name);
     }
diff --git a/chrome/browser/apps/intent_helper/intent_picker_features.cc b/chrome/browser/apps/intent_helper/intent_picker_features.cc
new file mode 100644
index 0000000..3803422f
--- /dev/null
+++ b/chrome/browser/apps/intent_helper/intent_picker_features.cc
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
+#include "base/feature_list.h"
+
+namespace apps::features {
+
+const base::Feature kLinkCapturingUiUpdate{"LinkCapturingUiUpdate",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kLinkCapturingInfoBar{"LinkCapturingInfoBar",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kIntentChipSkipsPicker{"IntentChipSkipsPicker",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool LinkCapturingUiUpdateEnabled() {
+  return base::FeatureList::IsEnabled(kLinkCapturingUiUpdate);
+}
+
+bool LinkCapturingInfoBarEnabled() {
+  return LinkCapturingUiUpdateEnabled() &&
+         base::FeatureList::IsEnabled(kLinkCapturingInfoBar);
+}
+
+bool ShouldIntentChipSkipIntentPicker() {
+  return LinkCapturingUiUpdateEnabled() &&
+         base::FeatureList::IsEnabled(kIntentChipSkipsPicker);
+}
+
+}  // namespace apps::features
diff --git a/chrome/browser/apps/intent_helper/intent_picker_features.h b/chrome/browser/apps/intent_helper/intent_picker_features.h
new file mode 100644
index 0000000..4407f76
--- /dev/null
+++ b/chrome/browser/apps/intent_helper/intent_picker_features.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_INTENT_HELPER_INTENT_PICKER_FEATURES_H_
+#define CHROME_BROWSER_APPS_INTENT_HELPER_INTENT_PICKER_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace apps::features {
+
+extern const base::Feature kLinkCapturingUiUpdate;
+extern const base::Feature kLinkCapturingInfoBar;
+extern const base::Feature kIntentChipSkipsPicker;
+
+// Returns true if the overall link capturing UI update feature is enabled.
+bool LinkCapturingUiUpdateEnabled();
+
+// Returns true if clicking the Intent Chip should skip the Intent Picker when
+// there is only one relevant app. Only returns true if
+// LinkCapturingUiUpdateEnabled() returns true.
+bool ShouldIntentChipSkipIntentPicker();
+
+// Returns true if the Link Capturing Info Bar should be shown when launching a
+// web app through the Intent Picker. Only returns true if
+// LinkCapturingUiUpdateEnabled() returns true.
+bool LinkCapturingInfoBarEnabled();
+
+}  // namespace apps::features
+
+#endif  // CHROME_BROWSER_APPS_INTENT_HELPER_INTENT_PICKER_FEATURES_H_
diff --git a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
index e1f8af2..750c39e1 100644
--- a/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/intent_picker_helpers.cc
@@ -7,17 +7,16 @@
 #include <string>
 #include <utility>
 
-#include "base/feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_internal.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
-#include "chrome/common/chrome_features.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 
@@ -183,8 +182,7 @@
       apps::IntentHandlingMetrics::IntentPickerIconEvent::kIconClicked);
 #endif
 
-  if (apps.size() == 1 &&
-      base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) {
+  if (apps.size() == 1 && apps::features::ShouldIntentChipSkipIntentPicker()) {
     LaunchAppFromIntentPicker(web_contents, url, apps[0].launch_name,
                               apps[0].type);
     return;
diff --git a/chrome/browser/apps/platform_apps/api/BUILD.gn b/chrome/browser/apps/platform_apps/api/BUILD.gn
index d0f483cd..4faa6e2 100644
--- a/chrome/browser/apps/platform_apps/api/BUILD.gn
+++ b/chrome/browser/apps/platform_apps/api/BUILD.gn
@@ -77,9 +77,9 @@
     ]
 
     deps += [
+      "//ash/components/multidevice/logging",
       "//ash/components/proximity_auth",
       "//chrome:strings",
-      "//chromeos/components/multidevice/logging",
       "//components/account_id",
       "//components/strings:components_strings_grit",
       "//components/user_manager",
diff --git a/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc b/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc
index 7aa87e76..7fa4fb1 100644
--- a/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc
+++ b/chrome/browser/ash/android_sms/android_sms_app_manager_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
@@ -17,7 +18,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc
index c44fd4b..478aa49 100644
--- a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc
+++ b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
@@ -18,7 +19,6 @@
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
diff --git a/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc b/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc
index 43049071..3f93d078 100644
--- a/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc
+++ b/chrome/browser/ash/android_sms/android_sms_pairing_state_tracker_impl.cc
@@ -7,12 +7,12 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ash/android_sms/android_sms_urls.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "net/cookies/canonical_cookie.h"
diff --git a/chrome/browser/ash/android_sms/connection_manager.cc b/chrome/browser/ash/android_sms/connection_manager.cc
index ccb57306..d7df82d7 100644
--- a/chrome/browser/ash/android_sms/connection_manager.cc
+++ b/chrome/browser/ash/android_sms/connection_manager.cc
@@ -6,10 +6,10 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "chrome/browser/ash/android_sms/android_sms_urls.h"
 #include "chrome/browser/ash/android_sms/connection_establisher.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/service_worker_context.h"
diff --git a/chrome/browser/ash/android_sms/fcm_connection_establisher.cc b/chrome/browser/ash/android_sms/fcm_connection_establisher.cc
index 1534f2e..3c29133 100644
--- a/chrome/browser/ash/android_sms/fcm_connection_establisher.cc
+++ b/chrome/browser/ash/android_sms/fcm_connection_establisher.cc
@@ -6,9 +6,9 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/common/messaging/string_message_codec.h"
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
index 629438ef..b71d3d4c 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
@@ -7,6 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/arc/input_overlay/actions/action.h"
 #include "chrome/browser/ash/arc/input_overlay/touch_id_manager.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/gfx/geometry/point.h"
@@ -20,6 +21,8 @@
 constexpr char kTargetArea[] = "target_area";
 constexpr char kTopLeft[] = "top_left";
 constexpr char kBottomRight[] = "bottom_right";
+// TODO(cuicuiruan): remove this and replace it with image asset.
+constexpr char kMouseCursorLock[] = "mouse cursor lock (esc)";
 
 constexpr int kAxisSize = 2;
 constexpr int kDirection[kActionMoveKeysSize][kAxisSize] = {{0, -1},
@@ -28,7 +31,7 @@
                                                             {1, 0}};
 // UI specs.
 // Offset by label center.
-constexpr int kLabelOffset = 49;
+constexpr int kTagOffset = 49;
 
 std::unique_ptr<Position> ParseApplyAreaPosition(const base::Value& value,
                                                  base::StringPiece key) {
@@ -53,14 +56,13 @@
                       DisplayOverlayController* display_overlay_controller,
                       const gfx::RectF& content_bounds)
       : ActionView(action, display_overlay_controller) {
-    auto label = std::make_unique<ActionLabel>(u"mouse cursor lock (esc)");
-    label->set_editable(false);
-    auto label_size = label->GetPreferredSize();
-    label->SetSize(label_size);
-    SetSize(label_size);
-    center_.set_x(label_size.width() / 2);
-    center_.set_y(label_size.height() / 2);
-    labels_.emplace_back(AddChildView(std::move(label)));
+    auto tag = ActionTag::CreateTextActionTag(kMouseCursorLock);
+    auto tag_size = tag->GetPreferredSize();
+    tag->SetSize(tag_size);
+    SetSize(tag_size);
+    center_.set_x(tag_size.width() / 2);
+    center_.set_y(tag_size.height() / 2);
+    tags_.emplace_back(AddChildView(std::move(tag)));
   }
 
   ActionMoveMouseView(const ActionMoveMouseView&) = delete;
@@ -87,17 +89,16 @@
     auto keys = action->current_binding()->keys();
     for (int i = 0; i < keys.size(); i++) {
       auto text = GetDisplayText(keys[i]);
-      auto label = std::make_unique<ActionLabel>(base::UTF8ToUTF16(text));
-      label->set_editable(true);
-      auto label_size = label->GetPreferredSize();
-      label->SetSize(label_size);
+      auto tag = ActionTag::CreateTextActionTag(text);
+      auto tag_size = tag->GetPreferredSize();
+      tag->SetSize(tag_size);
       int x = kDirection[i][0];
       int y = kDirection[i][1];
       auto pos = gfx::Point(
-          radius + x * (radius - kLabelOffset) - label_size.width() / 2,
-          radius + y * (radius - kLabelOffset) - label_size.height() / 2);
-      label->SetPosition(pos);
-      labels_.emplace_back(AddChildView(std::move(label)));
+          radius + x * (radius - kTagOffset) - tag_size.width() / 2,
+          radius + y * (radius - kTagOffset) - tag_size.height() / 2);
+      tag->SetPosition(pos);
+      tags_.emplace_back(AddChildView(std::move(tag)));
     }
   }
 
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
index b4e4a56..3123872 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
@@ -5,8 +5,9 @@
 #include "chrome/browser/ash/arc/input_overlay/actions/action_tap.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ash/arc/input_overlay/actions/input_element.h"
 #include "chrome/browser/ash/arc/input_overlay/touch_id_manager.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
 #include "ui/aura/window.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -18,8 +19,8 @@
 namespace input_overlay {
 namespace {
 // UI specs.
-constexpr int kLabelPositionToSide = 36;
-constexpr int kLabelMargin = 2;
+constexpr int kTagPositionToSide = 36;
+constexpr int kTagMargin = 2;
 }  // namespace
 
 class ActionTap::ActionTapView : public ActionView {
@@ -30,39 +31,38 @@
       : ActionView(action, display_overlay_controller) {
     int radius = action->GetUIRadius(content_bounds);
     auto circle = std::make_unique<ActionCircle>(radius);
-    std::string text;
+    std::unique_ptr<ActionTag> tag;
     if (action->IsKeyboardBound()) {
-      text = GetDisplayText(action->current_binding()->keys()[0]);
+      tag = ActionTag::CreateTextActionTag(
+          GetDisplayText(action->current_binding()->keys()[0]));
     } else if (action->IsMouseBound()) {
-      text = action->current_binding()->mouse_action();
+      tag = ActionTag::CreateImageActionTag(
+          action->current_binding()->mouse_action());
     } else {
-      text = "?";
+      tag = ActionTag::CreateTextActionTag("?");
     }
-    auto label = std::make_unique<ActionLabel>(base::UTF8ToUTF16(text));
-    label->set_editable(true);
-    auto label_size = label->GetPreferredSize();
-    label->SetSize(label_size);
-    int width = std::max(
-        radius * 2, radius * 2 - kLabelPositionToSide + label_size.width());
+    auto tag_size = tag->GetPreferredSize();
+    tag->SetSize(tag_size);
+    int width = std::max(radius * 2,
+                         radius * 2 - kTagPositionToSide + tag_size.width());
     SetSize(gfx::Size(width, radius * 2));
     if (action->on_left_or_middle_side()) {
       circle->SetPosition(gfx::Point());
-      label->SetPosition(
-          gfx::Point(label_size.width() > kLabelPositionToSide
-                         ? width - label_size.width()
-                         : width - kLabelPositionToSide,
-                     radius * 2 - label_size.height() - kLabelMargin));
+      tag->SetPosition(gfx::Point(tag_size.width() > kTagPositionToSide
+                                      ? width - tag_size.width()
+                                      : width - kTagPositionToSide,
+                                  radius * 2 - tag_size.height() - kTagMargin));
       center_.set_x(radius);
       center_.set_y(radius);
     } else {
       circle->SetPosition(gfx::Point(width - radius * 2, 0));
-      label->SetPosition(
-          gfx::Point(0, radius * 2 - label_size.height() - kLabelMargin));
+      tag->SetPosition(
+          gfx::Point(0, radius * 2 - tag_size.height() - kTagMargin));
       center_.set_x(width - radius);
       center_.set_y(radius);
     }
     circle_ = AddChildView(std::move(circle));
-    labels_.emplace_back(AddChildView(std::move(label)));
+    tags_.emplace_back(AddChildView(std::move(tag)));
   }
 
   ActionTapView(const ActionTapView&) = delete;
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
index 4d0ad3e..e8aad2b 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
@@ -6,7 +6,7 @@
 
 #include <set>
 
-#include "chrome/browser/ash/arc/input_overlay/ui/action_view.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/gfx/color_palette.h"
@@ -17,8 +17,8 @@
 namespace {
 // UI specs.
 constexpr int kWidthPadding = 10;
-constexpr gfx::Size kMinimumLabelSize(32, 32);
-constexpr int kCornerRadius = 6;
+constexpr gfx::Size kMinimumViewLabelSize(32, 32);
+constexpr int kCornerRadiusView = 6;
 
 constexpr SkColor kViewModeBgColor = SkColorSetA(SK_ColorGRAY, 0x99);
 constexpr SkColor kEditModeBgColor = SK_ColorWHITE;
@@ -53,39 +53,15 @@
 
 ActionLabel::ActionLabel() : views::Label() {}
 ActionLabel::ActionLabel(const std::u16string& text) : views::Label(text) {
-  SetDisplayMode(DisplayMode::kView);
+  SetToViewMode();
 }
 
 ActionLabel::~ActionLabel() = default;
 
-void ActionLabel::SetDisplayMode(DisplayMode mode) {
-  switch (mode) {
-    case DisplayMode::kMenu:
-    case DisplayMode::kView:
-      SetToView();
-      break;
-    case DisplayMode::kEdit:
-      SetToEditDefault();
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-}
-
-void ActionLabel::SetPositionFromCenterPosition(gfx::PointF& center_position) {
-  auto size = GetPreferredSize();
-  SetSize(size);
-  int left = std::max(0, (int)(center_position.x() - size.width() / 2));
-  int top = std::max(0, (int)(center_position.y() - size.height() / 2));
-  // SetPosition function needs the top-left position.
-  SetPosition(gfx::Point(left, top));
-}
-
 gfx::Size ActionLabel::CalculatePreferredSize() const {
   auto size = Label::CalculatePreferredSize();
   size.set_width(size.width() + kWidthPadding);
-  size.SetToMax(kMinimumLabelSize);
+  size.SetToMax(kMinimumViewLabelSize);
   return size;
 }
 
@@ -97,34 +73,36 @@
   SetToEditFocus();
   SelectAll();
   Label::OnFocus();
-  static_cast<ActionView*>(parent())->RemoveEditMenu();
+  static_cast<ActionTag*>(parent())->OnActionLabelFocused();
 }
 
 void ActionLabel::OnBlur() {
-  SetToEditDefault();
+  SetToEditMode();
   ClearSelection();
   Label::OnBlur();
 }
 
-void ActionLabel::SetToView() {
+void ActionLabel::SetToViewMode() {
   SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL, kViewFontSize,
                             gfx::Font::Weight::BOLD));
   SetFocusBehavior(FocusBehavior::NEVER);
   SetBackground(
-      views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadius));
+      views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadiusView));
   SetAutoColorReadabilityEnabled(false);
   SetEnabledColor(kViewTextColor);
+  SetSize(GetPreferredSize());
   SetSelectable(false);
   SetEnabled(false);
 }
 
-void ActionLabel::SetToEditDefault() {
+void ActionLabel::SetToEditMode() {
   SetFontList(gfx::FontList({kFontSytle}, gfx::Font::NORMAL, kUnFocusFontSize,
                             gfx::Font::Weight::BOLD));
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetBackground(
-      views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadius));
+      views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadiusView));
   SetEnabledColor(kEditTextColor);
+  SetSize(GetPreferredSize());
   SetSelectable(true);
   SetEnabled(true);
 }
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.h b/chrome/browser/ash/arc/input_overlay/ui/action_label.h
index adc96b1..568e7d7 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_label.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.h
@@ -17,8 +17,7 @@
 // on different keyboard layout.
 std::string GetDisplayText(const ui::DomCode code);
 
-// ActionLabel is the basic UI label for the action. It can set default view
-// mode and edit mode.
+// ActionLabel shows text mapping hint for each action.
 class ActionLabel : public views::Label {
  public:
   ActionLabel();
@@ -28,11 +27,8 @@
   ActionLabel& operator=(const ActionLabel&) = delete;
   ~ActionLabel() override;
 
-  void set_editable(bool editable) { editable_ = editable; }
-
-  void SetDisplayMode(const DisplayMode mode);
-  // Set position from its center position.
-  void SetPositionFromCenterPosition(gfx::PointF& center_position);
+  void SetToViewMode();
+  void SetToEditMode();
 
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
@@ -41,12 +37,7 @@
   void OnBlur() override;
 
  private:
-  void SetToView();
-  void SetToEditDefault();
   void SetToEditFocus();
-
- private:
-  bool editable_ = false;
 };
 }  // namespace input_overlay
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc b/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc
new file mode 100644
index 0000000..19553f9
--- /dev/null
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_tag.cc
@@ -0,0 +1,144 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
+
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ash/arc/input_overlay/actions/input_element.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_view.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/button/image_button.h"
+
+namespace arc {
+namespace input_overlay {
+namespace {
+constexpr int kIconSize = 20;
+constexpr int kCornerRadius = 6;
+constexpr SkColor kEditModeBgColor = SK_ColorWHITE;
+constexpr SkColor kViewModeBgColor = SkColorSetA(SK_ColorGRAY, 0x99);
+constexpr gfx::Size kImageButtonSize(32, 32);
+}  // namespace
+
+class ActionTag::ActionImage : public views::ImageButton {
+ public:
+  explicit ActionImage(std::string mouse_action)
+      : views::ImageButton(), mouse_action_(mouse_action) {
+    SetToViewMode();
+  }
+  ~ActionImage() override = default;
+
+  void SetDisplayMode(DisplayMode mode) {
+    switch (mode) {
+      case DisplayMode::kMenu:
+      case DisplayMode::kView:
+        SetToViewMode();
+        break;
+      case DisplayMode::kEdit:
+        SetToEditMode();
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  void SetToViewMode() {
+    if (mouse_action_ == kPrimaryClick) {
+      auto left_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseLeftClickViewIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, left_click_icon);
+    } else {
+      auto right_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseRightClickViewIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, right_click_icon);
+    }
+    SetBackground(
+        views::CreateRoundedRectBackground(kViewModeBgColor, kCornerRadius));
+  }
+
+  void SetToEditMode() {
+    if (mouse_action_ == kPrimaryClick) {
+      auto left_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseLeftClickEditIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, left_click_icon);
+    } else {
+      auto right_click_icon = gfx::CreateVectorIcon(
+          gfx::IconDescription(kMouseRightClickEditIcon, kIconSize));
+      SetImage(views::Button::STATE_NORMAL, right_click_icon);
+    }
+    SetBackground(
+        views::CreateRoundedRectBackground(kEditModeBgColor, kCornerRadius));
+  }
+
+ private:
+  std::string mouse_action_;
+};
+
+ActionTag::ActionTag() : views::View() {}
+ActionTag::~ActionTag() = default;
+
+// static
+std::unique_ptr<ActionTag> ActionTag::CreateTextActionTag(std::string text) {
+  auto tag = std::make_unique<ActionTag>();
+  auto label = std::make_unique<ActionLabel>(base::UTF8ToUTF16(text));
+  label->SetPosition(gfx::Point());
+  tag->label_ = tag->AddChildView(std::move(label));
+
+  return tag;
+}
+
+// static
+std::unique_ptr<ActionTag> ActionTag::CreateImageActionTag(
+    std::string mouse_action) {
+  DCHECK(mouse_action == kPrimaryClick || mouse_action == kSecondaryClick);
+  if (mouse_action != kPrimaryClick && mouse_action != kSecondaryClick)
+    return nullptr;
+  auto tag = std::make_unique<ActionTag>();
+  auto image = std::make_unique<ActionImage>(mouse_action);
+  image->SetAccessibleName(base::UTF8ToUTF16(image->GetClassName()));
+  image->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
+  image->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
+  image->SetToViewMode();
+  image->SetPosition(gfx::Point());
+  image->SetSize(kImageButtonSize);
+  tag->image_ = tag->AddChildView(std::move(image));
+
+  return tag;
+}
+
+void ActionTag::SetDisplayMode(DisplayMode mode) {
+  switch (mode) {
+    case DisplayMode::kMenu:
+    case DisplayMode::kView:
+      if (label_)
+        label_->SetToViewMode();
+      if (image_)
+        image_->SetToViewMode();
+      break;
+    case DisplayMode::kEdit:
+      if (label_)
+        label_->SetToEditMode();
+      if (image_)
+        image_->SetToEditMode();
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+void ActionTag::OnActionLabelFocused() {
+  static_cast<ActionView*>(parent())->RemoveEditMenu();
+}
+
+gfx::Size ActionTag::CalculatePreferredSize() const {
+  DCHECK((label_ && !image_) || (!label_ && image_));
+  if (image_)
+    return image_->size();
+  return label_ ? label_->GetPreferredSize() : gfx::Size();
+}
+
+}  // namespace input_overlay
+}  // namespace arc
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_tag.h b/chrome/browser/ash/arc/input_overlay/ui/action_tag.h
new file mode 100644
index 0000000..88fa2382
--- /dev/null
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_tag.h
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_
+#define CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_
+
+#include "chrome/browser/ash/arc/input_overlay/constants.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
+#include "ui/views/view.h"
+
+namespace arc {
+namespace input_overlay {
+
+// ActionTag is used to showing the mapping hint for each action. It can show
+// text hint or image hint.
+class ActionTag : public views::View {
+ public:
+  ActionTag();
+  ~ActionTag() override;
+
+  static std::unique_ptr<ActionTag> CreateTextActionTag(std::string text);
+  static std::unique_ptr<ActionTag> CreateImageActionTag(
+      std::string mouse_action);
+
+  void SetDisplayMode(DisplayMode mode);
+  void OnActionLabelFocused();
+
+  // views::View:
+  gfx::Size CalculatePreferredSize() const override;
+
+ private:
+  class ActionImage;
+
+  void InitTextTag();
+  void InitImageTag();
+
+  ActionImage* image_ = nullptr;
+  ActionLabel* label_ = nullptr;
+};
+
+}  // namespace input_overlay
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_UI_ACTION_TAG_H_
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
index df488e5d..9f48b2f 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ash/arc/input_overlay/ui/action_view.h"
 
 #include "base/bind.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
 
 namespace arc {
 namespace input_overlay {
@@ -34,8 +33,8 @@
     AddEditButton();
     if (circle_)
       circle_->SetDisplayMode(mode);
-    for (auto* label : labels_)
-      label->SetDisplayMode(mode);
+    for (auto* tag : tags_)
+      tag->SetDisplayMode(mode);
   }
 }
 
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.h b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
index fc51ce7a..894f9b9 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
@@ -11,7 +11,7 @@
 #include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h"
 #include "chrome/browser/ash/arc/input_overlay/ui/action_circle.h"
 #include "chrome/browser/ash/arc/input_overlay/ui/action_edit_button.h"
-#include "chrome/browser/ash/arc/input_overlay/ui/action_label.h"
+#include "chrome/browser/ash/arc/input_overlay/ui/action_tag.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/views/view.h"
@@ -56,7 +56,7 @@
   // The circle view shows up for editing the action.
   ActionCircle* circle_ = nullptr;
   // Labels for mapping hints.
-  std::vector<ActionLabel*> labels_;
+  std::vector<ActionTag*> tags_;
   // Current display mode.
   DisplayMode current_display_mode_ = DisplayMode::kNone;
   // Center position of the circle view.
diff --git a/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc b/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc
index 6b9878e..9490985 100644
--- a/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc
+++ b/chrome/browser/ash/arc/optin/arc_optin_preference_handler.cc
@@ -27,12 +27,17 @@
 
   // Metrics mode should be propagated to owner if current user is not the owner
   // OR ownership has not been taken.
-  const bool should_use_owner =
+  const bool is_owner =
       user_manager::UserManager::Get()->IsCurrentUserOwner() ||
       ash::DeviceSettingsService::Get()->GetOwnershipStatus() ==
           ash::DeviceSettingsService::OWNERSHIP_NONE;
 
-  return !should_use_owner;
+  if (is_owner)
+    return false;
+
+  // Per user metrics should be disabled if the device metrics was disabled by
+  // the owner.
+  return ash::StatsReportingController::Get()->IsEnabled();
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index 4b1104e..2fb79574 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -45,7 +45,6 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
-#include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/system/sys_info.h"
@@ -169,6 +168,7 @@
 #include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
+#include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/metrics/chrome_feature_list_creator.h"
 #include "chrome/browser/metrics/structured/chrome_structured_metrics_recorder.h"
@@ -1233,7 +1233,8 @@
             chrome::GetChannel(), g_browser_process->local_state(),
             g_browser_process->system_network_context_manager()
                 ->GetSharedURLLoaderFactory(),
-            base::Minutes(base::RandInt(0, 29)));
+            device_activity::DeviceActivityController::DetermineStartUpDelay(
+                first_run::GetFirstRunSentinelCreationTime()));
   }
 #endif
 
diff --git a/chrome/browser/ash/crostini/crostini_terminal.cc b/chrome/browser/ash/crostini/crostini_terminal.cc
index e597b31c..e10d8e3 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.cc
+++ b/chrome/browser/ash/crostini/crostini_terminal.cc
@@ -52,7 +52,6 @@
 #include "ui/color/color_provider_manager.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/native_theme/native_theme.h"
-#include "ui/views/image_model_utils.h"
 
 namespace crostini {
 
@@ -463,10 +462,9 @@
       ui::ColorProviderManager::Get().GetColorProviderFor(
           ui::NativeTheme::GetInstanceForWeb()->GetColorProviderKey(nullptr));
   auto icon = [color_provider](const gfx::VectorIcon& icon) {
-    return views::GetImageSkiaFromImageModel(
-        ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon,
-                                       apps::kAppShortcutIconSizeDip),
-        color_provider);
+    return ui::ImageModel::FromVectorIcon(icon, ui::kColorMenuIcon,
+                                          apps::kAppShortcutIconSizeDip)
+        .Rasterize(color_provider);
   };
   gfx::ImageSkia terminal_ssh_icon = icon(kTerminalSshIcon);
   gfx::ImageSkia crostini_mascot_icon = icon(kCrostiniMascotIcon);
diff --git a/chrome/browser/ash/cryptauth/OWNERS b/chrome/browser/ash/cryptauth/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/ash/cryptauth/OWNERS
+++ b/chrome/browser/ash/cryptauth/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
index 2e0867144..7df8d86 100644
--- a/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
+++ b/chrome/browser/ash/cryptauth/client_app_metadata_provider_service.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/services/device_sync/proto/cryptauth_better_together_feature_metadata.pb.h"
@@ -23,7 +24,6 @@
 #include "chrome/browser/ash/cryptauth/cryptauth_device_id_provider_impl.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
diff --git a/chrome/browser/ash/device_sync/OWNERS b/chrome/browser/ash/device_sync/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/ash/device_sync/OWNERS
+++ b/chrome/browser/ash/device_sync/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/device_sync/device_sync_client_factory.cc b/chrome/browser/ash/device_sync/device_sync_client_factory.cc
index fb339fa..adad199 100644
--- a/chrome/browser/ash/device_sync/device_sync_client_factory.cc
+++ b/chrome/browser/ash/device_sync/device_sync_client_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/device_sync/device_sync_client_factory.h"
 
+#include "ash/components/multidevice/stub_multidevice_util.h"
 #include "ash/services/device_sync/device_sync_impl.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client_impl.h"
@@ -16,7 +17,6 @@
 #include "chrome/browser/gcm/gcm_profile_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chromeos/components/multidevice/stub_multidevice_util.h"
 #include "components/gcm_driver/gcm_profile_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
index 66242d09..e944485 100644
--- a/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
+++ b/chrome/browser/ash/eche_app/eche_app_notification_controller.cc
@@ -4,12 +4,12 @@
 
 #include "chrome/browser/ash/eche_app/eche_app_notification_controller.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/public/cpp/new_window_delegate.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/grit/generated_resources.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/message_center/message_center.h"
diff --git a/chrome/browser/ash/file_manager/external_filesystem_apitest.cc b/chrome/browser/ash/file_manager/external_filesystem_apitest.cc
index 81a5c13..4111f9a 100644
--- a/chrome/browser/ash/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/ash/file_manager/external_filesystem_apitest.cc
@@ -815,7 +815,7 @@
       FLAGS_NONE)) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(DriveFileSystemExtensionApiTest, FileWatch) {
+IN_PROC_BROWSER_TEST_F(DriveFileSystemExtensionApiTest, DISABLED_FileWatch) {
   EXPECT_TRUE(RunFileSystemExtensionApiTest(
       "file_browser/file_watcher_test",
       FILE_PATH_LITERAL("manifest.json"),
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc
index 278621a..30918cc 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.cc
+++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ash/file_manager/extract_io_task.h"
 
+#include "base/files/file_util.h"
+#include "base/strings/strcat.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "components/services/unzip/content/unzip_service.h"
 
@@ -43,6 +45,22 @@
   }
 }
 
+base::FilePath ExtractIOTask::CreateDestinationName(
+    const base::FilePath& parent,
+    const base::FilePath& source_file) {
+  const base::FilePath target = source_file.BaseName().RemoveExtension();
+  base::FilePath destination_directory = parent.Append(target);
+  if (base::PathExists(destination_directory)) {
+    // Create a unique name for the output directory.
+    for (int i = 1; base::PathExists(destination_directory) && i < kMaxRetries;
+         ++i) {
+      destination_directory = parent.Append(
+          base::StrCat({target.value(), " (", base::NumberToString(i), ")"}));
+    }
+  }
+  return destination_directory;
+}
+
 void ExtractIOTask::Execute(IOTask::ProgressCallback progress_callback,
                             IOTask::CompleteCallback complete_callback) {
   progress_callback_ = std::move(progress_callback);
@@ -55,10 +73,15 @@
     const base::FilePath source_file = source.url.path();
     // TODO(crbug.com/953256) Perform this check only once.
     if (chromeos::FileSystemBackend::CanHandleURL(parent_folder_)) {
-      const base::FilePath destination_directory = parent_folder_.path();
-      unzip::Unzip(unzip::LaunchUnzipper(), source_file, destination_directory,
-                   base::BindOnce(&ExtractIOTask::ZipExtractCallback,
-                                  weak_ptr_factory_.GetWeakPtr()));
+      const base::FilePath destination_directory =
+          CreateDestinationName(parent_folder_.path(), source_file);
+      // Create the directory to extract into.
+      if (base::CreateDirectory(destination_directory)) {
+        unzip::Unzip(unzip::LaunchUnzipper(), source_file,
+                     destination_directory,
+                     base::BindOnce(&ExtractIOTask::ZipExtractCallback,
+                                    weak_ptr_factory_.GetWeakPtr()));
+      }  // TODO(crbug.com/953256) Report directory creation error.
     } else {
       progress_.state = State::kError;
       // We won't get a callback so reduce the count and maybe finalise.
diff --git a/chrome/browser/ash/file_manager/extract_io_task.h b/chrome/browser/ash/file_manager/extract_io_task.h
index 5dc4df5..e2633ca 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.h
+++ b/chrome/browser/ash/file_manager/extract_io_task.h
@@ -39,6 +39,11 @@
 
   void ZipExtractCallback(bool success);
 
+  // Creates a (non-existing) destination name for creating a directory inside
+  // 'parent'.
+  base::FilePath CreateDestinationName(const base::FilePath& parent,
+                                       const base::FilePath& source_file);
+
   // URLs of the files that have archives in them for extraction.
   const std::vector<storage::FileSystemURL> source_urls_;
 
@@ -53,6 +58,9 @@
   // Counter of the number of archives needing extraction.
   size_t extractCount_;
 
+  // Maximum directory naming retries when trying to extract.
+  const int kMaxRetries = 1000;
+
   base::WeakPtrFactory<ExtractIOTask> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
index fd4f5a9..f2606d2 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
@@ -1664,6 +1664,10 @@
   explicit MockGuestOsMountProvider(std::string name) : name_(name) {}
 
   std::string DisplayName() override { return name_; }
+  Profile* profile() override { return nullptr; }
+  crostini::ContainerId ContainerId() override {
+    return crostini::ContainerId::GetDefault();
+  }
 
  private:
   std::string name_;
diff --git a/chrome/browser/ash/file_manager/path_util.cc b/chrome/browser/ash/file_manager/path_util.cc
index 0caa571..5823746 100644
--- a/chrome/browser/ash/file_manager/path_util.cc
+++ b/chrome/browser/ash/file_manager/path_util.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ash/drive/file_system_util.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/smb_client/smb_service.h"
 #include "chrome/browser/ash/smb_client/smb_service_factory.h"
@@ -343,10 +344,23 @@
       "_");
 }
 
+std::string GetGuestOsMountPointName(Profile* profile,
+                                     crostini::ContainerId id) {
+  return base::JoinString(
+      {"guestos", ash::ProfileHelper::GetUserIdHashFromProfile(profile),
+       net::EscapeAllExceptUnreserved(id.vm_name),
+       net::EscapeAllExceptUnreserved(id.container_name)},
+      "+");
+}
+
 base::FilePath GetCrostiniMountDirectory(Profile* profile) {
   return base::FilePath("/media/fuse/" + GetCrostiniMountPointName(profile));
 }
 
+base::FilePath GetGuestOsMountDirectory(std::string mountPointName) {
+  return base::FilePath("/media/fuse/" + mountPointName);
+}
+
 std::vector<std::string> GetCrostiniMountOptions(
     const std::string& hostname,
     const std::string& host_private_key,
diff --git a/chrome/browser/ash/file_manager/path_util.h b/chrome/browser/ash/file_manager/path_util.h
index cdb1bec..91e8e77 100644
--- a/chrome/browser/ash/file_manager/path_util.h
+++ b/chrome/browser/ash/file_manager/path_util.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/crostini/crostini_util.h"
 #include "storage/browser/file_system/file_system_url.h"
 
 class GURL;
@@ -100,9 +101,16 @@
 // The canonical mount point name for crostini "Linux files" folder.
 std::string GetCrostiniMountPointName(Profile* profile);
 
+// The canonical mount point name for the Guest OS `id`.
+std::string GetGuestOsMountPointName(Profile* profile,
+                                     crostini::ContainerId id);
+
 // The actual directory the crostini "Linux files" folder is mounted.
 base::FilePath GetCrostiniMountDirectory(Profile* profile);
 
+// The actual directory the Guest OS with `mountPointName` is mounted in.
+base::FilePath GetGuestOsMountDirectory(std::string mountPointName);
+
 // The sshfs mount options for crostini "Linux files" mount.
 std::vector<std::string> GetCrostiniMountOptions(
     const std::string& hostname,
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc
index 5797518a..4adfb567 100644
--- a/chrome/browser/ash/file_manager/volume_manager.cc
+++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -164,6 +164,8 @@
       return "smb";
     case VOLUME_TYPE_SYSTEM_INTERNAL:
       return "system_internal";
+    case VOLUME_TYPE_GUEST_OS:
+      return "guest_os";
     case NUM_VOLUME_TYPE:
       break;
   }
@@ -387,6 +389,25 @@
 }
 
 // static
+std::unique_ptr<Volume> Volume::CreateForSftpGuestOs(
+    const std::string display_name,
+    const base::FilePath& sftp_mount_path,
+    const base::FilePath& remote_mount_path) {
+  std::unique_ptr<Volume> volume(new Volume());
+  volume->type_ = VOLUME_TYPE_GUEST_OS;
+  volume->device_type_ = chromeos::DEVICE_TYPE_UNKNOWN;
+  // Keep source_path empty.
+  volume->source_ = SOURCE_SYSTEM;
+  volume->mount_path_ = sftp_mount_path;
+  volume->remote_mount_path_ = remote_mount_path;
+  volume->mount_condition_ = ash::disks::MOUNT_CONDITION_NONE;
+  volume->volume_id_ = GenerateVolumeId(*volume);
+  volume->volume_label_ = display_name;
+  volume->watchable_ = false;
+  return volume;
+}
+
+// static
 std::unique_ptr<Volume> Volume::CreateForAndroidFiles(
     const base::FilePath& mount_path) {
   std::unique_ptr<Volume> volume(new Volume());
@@ -727,6 +748,19 @@
                          })));
 }
 
+void VolumeManager::AddSftpGuestOsVolume(
+    const std::string display_name,
+    const base::FilePath& sftp_mount_path,
+    const base::FilePath& remote_mount_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  std::unique_ptr<Volume> volume = Volume::CreateForSftpGuestOs(
+      display_name, sftp_mount_path, remote_mount_path);
+  // Ignore if volume already exists.
+  if (mounted_volumes_.find(volume->volume_id()) != mounted_volumes_.end())
+    return;
+  DoMountEvent(chromeos::MOUNT_ERROR_NONE, std::move(volume));
+}
+
 void VolumeManager::RemoveSshfsCrostiniVolume(
     const base::FilePath& sshfs_mount_path,
     RemoveSshfsCrostiniVolumeCallback callback) {
@@ -738,6 +772,17 @@
                      std::move(callback)));
 }
 
+void VolumeManager::RemoveSftpGuestOsVolume(
+    const base::FilePath& sftp_mount_path,
+    RemoveSshfsCrostiniVolumeCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  disk_mount_manager_->UnmountPath(
+      sftp_mount_path.value(),
+      base::BindOnce(&VolumeManager::OnSftpGuestOsUnmountCallback,
+                     base::Unretained(this), sftp_mount_path,
+                     std::move(callback)));
+}
+
 bool VolumeManager::RegisterAndroidFilesDirectoryForTesting(
     const base::FilePath& path) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -1519,4 +1564,28 @@
     std::move(callback).Run(false);
 }
 
+void VolumeManager::OnSftpGuestOsUnmountCallback(
+    const base::FilePath& sftp_mount_path,
+    RemoveSftpGuestOsVolumeCallback callback,
+    chromeos::MountError error_code) {
+  if ((error_code == chromeos::MOUNT_ERROR_NONE) ||
+      (error_code == chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED)) {
+    // Remove metadata associated with the mount. It will be a no-op if it
+    // wasn't mounted or unmounted out of band. We need the VolumeId to be
+    // consistent, which means the mount path needs to be the same. display_name
+    // and remote_mount_path aren't needed and we don't know them at unmount so
+    // leave them blank.
+    DoUnmountEvent(
+        chromeos::MOUNT_ERROR_NONE,
+        *Volume::CreateForSftpGuestOs("", sftp_mount_path, base::FilePath()));
+    if (callback)
+      std::move(callback).Run(true);
+    return;
+  }
+
+  LOG(ERROR) << "Unmounting SFTP path failed with error: " << error_code;
+  if (callback)
+    std::move(callback).Run(false);
+}
+
 }  // namespace file_manager
diff --git a/chrome/browser/ash/file_manager/volume_manager.h b/chrome/browser/ash/file_manager/volume_manager.h
index 33f0a13..df6e99c 100644
--- a/chrome/browser/ash/file_manager/volume_manager.h
+++ b/chrome/browser/ash/file_manager/volume_manager.h
@@ -67,6 +67,7 @@
   VOLUME_TYPE_SMB,
   VOLUME_TYPE_SYSTEM_INTERNAL,  // Internal volume which is never exposed to
                                 // users.
+  VOLUME_TYPE_GUEST_OS,         // Guest OS volumes (Crostini, Bruschetta, etc)
   // The enum values must be kept in sync with FileManagerVolumeType in
   // tools/metrics/histograms/enums.xml. Since enums for histograms are
   // append-only (for keeping the number consistent across versions), new values
@@ -118,6 +119,10 @@
   static std::unique_ptr<Volume> CreateForSshfsCrostini(
       const base::FilePath& crostini_path,
       const base::FilePath& remote_mount_path);
+  static std::unique_ptr<Volume> CreateForSftpGuestOs(
+      const std::string display_name,
+      const base::FilePath& sftp_mount_path,
+      const base::FilePath& remote_mount_path);
   static std::unique_ptr<Volume> CreateForAndroidFiles(
       const base::FilePath& mount_path);
   static std::unique_ptr<Volume> CreateForDocumentsProvider(
@@ -300,9 +305,12 @@
       const std::string&,
       device::mojom::MtpManager::GetStorageInfoCallback)>;
 
-  // Callback for |RemoveSshfsCrostiniVolume|.
+  // Callback for `RemoveSshfsCrostiniVolume`.
   using RemoveSshfsCrostiniVolumeCallback = base::OnceCallback<void(bool)>;
 
+  // Callback for `RemoveSftpGuestOsVolume`.
+  using RemoveSftpGuestOsVolumeCallback = base::OnceCallback<void(bool)>;
+
   VolumeManager(
       Profile* profile,
       drive::DriveIntegrationService* drive_integration_service,
@@ -344,16 +352,29 @@
   // path, is located. Returns nullptr if no volume is found.
   base::WeakPtr<Volume> FindVolumeFromPath(const base::FilePath& path);
 
-  // Add sshfs crostini volume mounted at specified path.
+  // Add sshfs crostini volume mounted at `sshfs_mount_path` path. Will
+  // automatically remove the volume on container shutdown.
   void AddSshfsCrostiniVolume(const base::FilePath& sshfs_mount_path,
                               const base::FilePath& remote_mount_path);
 
-  // Removes specified sshfs crostini mount. Runs |callback| with true if the
+  // Add sftp Guest OS volume mounted at `sftp_mount_path`. Note: volume must be
+  // removed on unmount (including Guest OS shutdown).
+  void AddSftpGuestOsVolume(const std::string display_name,
+                            const base::FilePath& sftp_mount_path,
+                            const base::FilePath& remote_mount_path);
+
+  // Removes specified sshfs crostini mount. Runs `callback` with true if the
   // mount was removed successfully or wasn't mounted to begin with. Runs
-  // |callback| with false in all other cases.
+  // `callback` with false in all other cases.
   void RemoveSshfsCrostiniVolume(const base::FilePath& sshfs_mount_path,
                                  RemoveSshfsCrostiniVolumeCallback callback);
 
+  // Removes specified sftp Guest OS mount. Runs `callback` with true if the
+  // mount was removed successfully or wasn't mounted to begin with. Runs
+  // `callback` with false in all other cases.
+  void RemoveSftpGuestOsVolume(const base::FilePath& sftp_mount_path,
+                               RemoveSftpGuestOsVolumeCallback callback);
+
   // Removes Downloads volume used for testing.
   void RemoveDownloadsDirectoryForTesting();
 
@@ -495,6 +516,10 @@
       RemoveSshfsCrostiniVolumeCallback callback,
       chromeos::MountError error_code);
 
+  void OnSftpGuestOsUnmountCallback(const base::FilePath& sftp_mount_path,
+                                    RemoveSftpGuestOsVolumeCallback callback,
+                                    chromeos::MountError error_code);
+
   Profile* profile_;
   drive::DriveIntegrationService* drive_integration_service_;  // Not owned.
   ash::disks::DiskMountManager* disk_mount_manager_;           // Not owned.
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc
new file mode 100644
index 0000000..5d7befc
--- /dev/null
+++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.cc
@@ -0,0 +1,169 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h"
+#include <memory>
+
+#include "ash/components/disks/disk_mount_manager.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/ash/borealis/infra/expected.h"
+#include "chrome/browser/ash/crostini/crostini_util.h"
+#include "chrome/browser/ash/file_manager/path_util.h"
+#include "chrome/browser/ash/file_manager/volume_manager.h"
+#include "chrome/browser/ash/guest_os/infra/cached_callback.h"
+#include "storage/browser/file_system/external_mount_points.h"
+
+namespace guest_os {
+
+// An RAII-style class controlling the lifetime of the SFTP volume. Will add the
+// volume on creation and remove it on destruction.
+class ScopedVolume {
+ public:
+  explicit ScopedVolume(
+      Profile* profile,
+      std::string display_name,
+      std::string mount_label,
+      base::FilePath homedir,
+      const ash::disks::DiskMountManager::MountPointInfo& mount_info)
+      : profile_(profile), mount_label_(mount_label) {
+    base::FilePath mount_path = base::FilePath(mount_info.mount_path);
+    if (!storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+            mount_label_, storage::kFileSystemTypeLocal,
+            storage::FileSystemMountOption(), mount_path)) {
+      // We don't revoke the filesystem on unmount and this call fails if a
+      // filesystem of the same name already exists, so ignore errors.
+      // TODO(crbug/1293229): This follows the logic of existing code, but we
+      // can probably change it to revoke the filesystem on unmount.
+    }
+
+    auto* vmgr = file_manager::VolumeManager::Get(profile_);
+    if (vmgr) {
+      // vmgr is null in unit tests.
+      vmgr->AddSftpGuestOsVolume(display_name, mount_path, homedir);
+    }
+  }
+
+  ~ScopedVolume() {
+    if (profile_->ShutdownStarted()) {
+      // We're shutting down, but because we're not a keyed service we don't get
+      // two-phase shutdown, we just can't call anything. Either the whole
+      // system is shutting down (in which case everything gets undone anyway)
+      // or it's just the browser (in which case it's basically the same as a
+      // browser crash which we also need to handle).
+      // So do nothing.
+      return;
+    }
+
+    auto* vmgr = file_manager::VolumeManager::Get(profile_);
+    if (vmgr) {
+      // vmgr is null in unit tests. Also, this calls disk_manager to unmount
+      // for us (and we never unregister the filesystem) hence unmount doesn't
+      // seem symmetric with mount.
+      vmgr->RemoveSftpGuestOsVolume(
+          file_manager::util::GetGuestOsMountDirectory(mount_label_),
+          base::DoNothing());
+    }
+  }
+
+  Profile* profile_;
+  std::string mount_label_;
+};
+
+class GuestOsMountProviderInner : public CachedCallback<ScopedVolume, bool> {
+ public:
+  explicit GuestOsMountProviderInner(Profile* profile,
+                                     std::string display_name,
+                                     crostini::ContainerId container_id,
+                                     int cid,
+                                     int port,
+                                     base::FilePath homedir)
+      : profile_(profile),
+        display_name_(display_name),
+        container_id_(container_id),
+        cid_(cid),
+        port_(port),
+        homedir_(homedir) {}
+
+  // Mount.
+  void Build(RealCallback callback) override {
+    mount_label_ =
+        file_manager::util::GetGuestOsMountPointName(profile_, container_id_);
+    auto* dmgr = ash::disks::DiskMountManager::GetInstance();
+
+    // Call to sshfs to mount.
+    std::string source_path = base::StringPrintf("sftp://%d:%d", cid_, port_);
+
+    dmgr->MountPath(
+        source_path, "", mount_label_, {}, chromeos::MOUNT_TYPE_NETWORK_STORAGE,
+        chromeos::MOUNT_ACCESS_MODE_READ_WRITE,
+        base::BindOnce(&GuestOsMountProviderInner::OnMountEvent,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+  void OnMountEvent(
+      RealCallback callback,
+      chromeos::MountError error_code,
+      const ash::disks::DiskMountManager::MountPointInfo& mount_info) {
+    if (error_code != chromeos::MountError::MOUNT_ERROR_NONE) {
+      LOG(ERROR) << "Error mounting Guest OS container: error_code="
+                 << error_code << ", source_path=" << mount_info.source_path
+                 << ", mount_path=" << mount_info.mount_path
+                 << ", mount_type=" << mount_info.mount_type
+                 << ", mount_condition=" << mount_info.mount_condition;
+      std::move(callback).Run(Failure(false));
+      return;
+    }
+    auto scoped_volume = std::make_unique<ScopedVolume>(
+        profile_, display_name_, mount_label_, homedir_, mount_info);
+
+    // CachedCallback magic keeps the scope alive until we're destroyed or it's
+    // invalidated.
+    std::move(callback).Run(RealResult(std::move(scoped_volume)));
+  }
+
+  Profile* profile_;
+  std::string display_name_;
+  crostini::ContainerId container_id_;
+  std::string mount_label_;
+  int cid_;
+  int port_;  // vsock port
+  base::FilePath homedir_;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<GuestOsMountProviderInner> weak_ptr_factory_{this};
+};
+
+void GuestOsMountProvider::Mount(base::OnceCallback<void(bool)> callback) {
+  if (!callback_) {
+    callback_ = std::make_unique<GuestOsMountProviderInner>(
+        profile(), DisplayName(), ContainerId(), cid(), port(), homedir());
+  }
+  callback_->Get(base::BindOnce(
+      [](base::OnceCallback<void(bool)> callback,
+         guest_os::GuestOsMountProviderInner::Result result) {
+        std::move(callback).Run(!!result);
+      },
+      std::move(callback)));
+}
+
+void GuestOsMountProvider::Unmount() {
+  callback_->Invalidate();
+}
+
+GuestOsMountProvider::GuestOsMountProvider() = default;
+GuestOsMountProvider::~GuestOsMountProvider() = default;
+int GuestOsMountProvider::cid() {
+  return 0;
+}
+int GuestOsMountProvider::port() {
+  return 0;
+}
+base::FilePath GuestOsMountProvider::homedir() {
+  return base::FilePath("/home/fake");
+}
+}  // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h
index 9f1e4d9..446a6476 100644
--- a/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h
+++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider.h
@@ -6,15 +6,48 @@
 #define CHROME_BROWSER_ASH_GUEST_OS_PUBLIC_GUEST_OS_MOUNT_PROVIDER_H_
 
 #include <string>
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "chrome/browser/ash/crostini/crostini_util.h"
+
+class Profile;
 
 namespace guest_os {
 
+class GuestOsMountProviderInner;
 class GuestOsMountProvider {
  public:
-  virtual ~GuestOsMountProvider() = default;
+  GuestOsMountProvider();
+  virtual ~GuestOsMountProvider();
+
+  GuestOsMountProvider(const GuestOsMountProvider&) = delete;
+  GuestOsMountProvider& operator=(const GuestOsMountProvider&) = delete;
+
+  // Get the profile for this provider.
+  virtual Profile* profile() = 0;
 
   // The localised name to show in UI elements such as the files app sidebar.
   virtual std::string DisplayName() = 0;
+
+  virtual crostini::ContainerId ContainerId() = 0;
+
+  // TODO(crbug/1293229): How exactly we perform an SFTP mount is TBD, so these
+  // are subject to change. For now we put random fake values in so we don't
+  // need to keep changing subclasses as we figure out the format. Assuming we
+  // keep a similar format to now we get cid from concierge or cicerone, port
+  // from garcon and homedir is either hardcoded or from tremplin or garcon.
+  virtual int cid();
+  virtual int port();
+  virtual base::FilePath homedir();
+
+  // Requests the provider to mount its volume.
+  void Mount(base::OnceCallback<void(bool)> callback);
+
+  // Requests the provider to unmount.
+  void Unmount();
+
+ private:
+  std::unique_ptr<GuestOsMountProviderInner> callback_;
 };
 
 }  // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc
index 450ce7a..34df193 100644
--- a/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc
+++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc
@@ -16,6 +16,10 @@
 
 class MockProvider : public GuestOsMountProvider {
   std::string DisplayName() override { return "Ptery"; }
+  Profile* profile() override { return nullptr; }
+  crostini::ContainerId ContainerId() override {
+    return crostini::ContainerId::GetDefault();
+  }
 };
 
 class MockObserver : public GuestOsMountProviderRegistry::Observer {
diff --git a/chrome/browser/ash/guest_os/public/guest_os_mount_provider_unittest.cc b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_unittest.cc
new file mode 100644
index 0000000..4686d12
--- /dev/null
+++ b/chrome/browser/ash/guest_os/public/guest_os_mount_provider_unittest.cc
@@ -0,0 +1,203 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h"
+
+#include <memory>
+
+#include "ash/components/disks/disk_mount_manager.h"
+#include "ash/components/disks/mock_disk_mount_manager.h"
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h"
+#include "chrome/browser/ash/file_manager/volume_manager.h"
+#include "chrome/browser/ash/file_manager/volume_manager_factory.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/test/browser_task_environment.h"
+#include "storage/browser/file_system/external_mount_points.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::ash::disks::DiskMountManager;
+using testing::_;
+
+namespace {
+
+// Creates a new VolumeManager for tests.
+// By default, VolumeManager KeyedService is null for testing.
+std::unique_ptr<KeyedService> BuildVolumeManager(
+    content::BrowserContext* context) {
+  return std::make_unique<file_manager::VolumeManager>(
+      Profile::FromBrowserContext(context),
+      nullptr /* drive_integration_service */,
+      nullptr /* power_manager_client */, DiskMountManager::GetInstance(),
+      nullptr /* file_system_provider_service */,
+      file_manager::VolumeManager::GetMtpStorageInfoCallback());
+}
+}  // namespace
+
+namespace guest_os {
+
+// TODO(crbug/1293229): This is MockProvider2 because we already have a
+// MockProvider and ODR will ruin our day if we have the same name. Should move
+// this into a separate file and have both test suites use the same object,
+// since both just need a placeholder.
+class MockProvider2 : public GuestOsMountProvider {
+ public:
+  explicit MockProvider2(Profile* profile, crostini::ContainerId container_id)
+      : profile_(profile), container_id_(container_id) {}
+  std::string DisplayName() override { return "Ptery"; }
+  Profile* profile() override { return profile_; }
+  Profile* profile_;
+  crostini::ContainerId ContainerId() override { return container_id_; }
+  // TODO(crbug/1293229): Make ContainerId generic and in guest_os namespace.
+  crostini::ContainerId container_id_;
+};
+
+class GuestOsMountProviderTest : public testing::Test {
+ public:
+  GuestOsMountProviderTest() {
+    profile_ = std::make_unique<TestingProfile>();
+    // DiskMountManager::InitializeForTesting takes ownership and works with
+    // a raw pointer, hence the new with no matching delete.
+    disk_manager_ = new ash::disks::MockDiskMountManager;
+    provider_ = std::make_unique<MockProvider2>(profile_.get(), kContainerId);
+    file_manager::VolumeManagerFactory::GetInstance()->SetTestingFactory(
+        profile_.get(), base::BindRepeating(&BuildVolumeManager));
+
+    DiskMountManager::InitializeForTesting(disk_manager_);
+    volume_manager_ = file_manager::VolumeManagerFactory::Get(profile_.get());
+  }
+
+  GuestOsMountProviderTest(const GuestOsMountProviderTest&) = delete;
+  GuestOsMountProviderTest& operator=(const GuestOsMountProviderTest&) = delete;
+
+  ~GuestOsMountProviderTest() override {
+    storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+        kMountName);
+    // Set an empty factory to shut down our testing factory.
+    file_manager::VolumeManagerFactory::GetInstance()->SetTestingFactory(
+        profile_.get(), BrowserContextKeyedServiceFactory::TestingFactory{});
+    DiskMountManager::Shutdown();
+    provider_.reset();
+    profile_.reset();
+  }
+
+ protected:
+  void NotifyMountEvent(
+      const std::string& source_path,
+      const std::string& source_format,
+      const std::string& mount_label,
+      const std::vector<std::string>& mount_options,
+      chromeos::MountType type,
+      chromeos::MountAccessMode access_mode,
+      ash::disks::DiskMountManager::MountPathCallback callback) {
+    auto event = DiskMountManager::MountEvent::MOUNTING;
+    auto code = chromeos::MountError::MOUNT_ERROR_NONE;
+    auto info = DiskMountManager::MountPointInfo(
+        "sftp://0:0", "/media/fuse/" + kMountName,
+        chromeos::MOUNT_TYPE_NETWORK_STORAGE, ash::disks::MOUNT_CONDITION_NONE);
+    disk_manager_->NotifyMountEvent(event, code, info);
+    std::move(callback).Run(code, info);
+  }
+
+  void ExpectMountCalls(int n) {
+    std::vector<std::string> default_mount_options;
+    EXPECT_CALL(*disk_manager_,
+                MountPath("sftp://0:0", "", kMountName, default_mount_options,
+                          chromeos::MOUNT_TYPE_NETWORK_STORAGE,
+                          chromeos::MOUNT_ACCESS_MODE_READ_WRITE, _))
+        .Times(n)
+        .WillRepeatedly(
+            Invoke(this, &GuestOsMountProviderTest::NotifyMountEvent));
+  }
+
+  // guestos_${UserHash}_${base64(kContainerId.ToString())}. Note that UserHash
+  // is an empty string in these tests.
+  const crostini::ContainerId kContainerId =
+      crostini::ContainerId("cow", "ptery/daccy");
+  const std::string kMountName = std::string{"guestos++cow+ptery%2Fdaccy"};
+
+  content::BrowserTaskEnvironment task_environment_;
+  ash::disks::MockDiskMountManager* disk_manager_;
+  std::unique_ptr<TestingProfile> profile_;
+  file_manager::VolumeManager* volume_manager_;
+  std::unique_ptr<MockProvider2> provider_;
+};
+
+TEST_F(GuestOsMountProviderTest, MountDiskMountsDisk) {
+  ExpectMountCalls(1);
+  bool result = false;
+
+  provider_->Mount(
+      base::BindLambdaForTesting([&result](bool res) { result = res; }));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(result);
+  base::FilePath path;
+  EXPECT_TRUE(
+      storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(
+          kMountName, &path));
+  EXPECT_EQ(base::FilePath("/media/fuse/" + kMountName), path);
+  auto volume = volume_manager_->FindVolumeById("guest_os:" + kMountName);
+  ASSERT_TRUE(volume);
+  EXPECT_EQ(volume->type(), file_manager::VOLUME_TYPE_GUEST_OS);
+  EXPECT_EQ(volume->volume_label(), provider_->DisplayName());
+}
+
+TEST_F(GuestOsMountProviderTest, MultipleCallsAreQueuedAndOnlyMountOnce) {
+  ExpectMountCalls(1);
+  int successes = 0;
+  provider_->Mount(base::BindLambdaForTesting(
+      [&successes](bool result) { successes += result; }));
+  provider_->Mount(base::BindLambdaForTesting(
+      [&successes](bool result) { successes += result; }));
+  task_environment_.RunUntilIdle();
+  provider_->Mount(base::BindLambdaForTesting(
+      [&successes](bool result) { successes += result; }));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_EQ(successes, 3);
+  base::FilePath path;
+  EXPECT_TRUE(
+      storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(
+          kMountName, &path));
+  EXPECT_EQ(base::FilePath("/media/fuse/" + kMountName), path);
+}
+
+TEST_F(GuestOsMountProviderTest, CanRemountAfterUnmount) {
+  ExpectMountCalls(2);
+  EXPECT_CALL(*disk_manager_, UnmountPath)
+      .WillOnce(testing::Invoke(
+          [this](const std::string& mount_path,
+                 DiskMountManager::UnmountPathCallback callback) {
+            EXPECT_EQ(mount_path, "/media/fuse/" + kMountName);
+            std::move(callback).Run(chromeos::MOUNT_ERROR_NONE);
+          }));
+
+  provider_->Mount(
+      base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); }));
+  task_environment_.RunUntilIdle();
+  provider_->Unmount();
+  task_environment_.RunUntilIdle();
+  provider_->Mount(
+      base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); }));
+  task_environment_.RunUntilIdle();
+
+  base::FilePath path;
+  EXPECT_TRUE(
+      storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(
+          kMountName, &path));
+  EXPECT_EQ(base::FilePath("/media/fuse/" + kMountName), path);
+}
+}  // namespace guest_os
diff --git a/chrome/browser/ash/login/easy_unlock/OWNERS b/chrome/browser/ash/login/easy_unlock/OWNERS
index df774b8..2a6e671 100644
--- a/chrome/browser/ash/login/easy_unlock/OWNERS
+++ b/chrome/browser/ash/login/easy_unlock/OWNERS
@@ -1,3 +1,3 @@
 hansberry@chromium.org
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
 
diff --git a/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc b/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc
index 3d3c57a..e3dbfb18 100644
--- a/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc
+++ b/chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "base/logging.h"
 #include "base/system/sys_info.h"
@@ -17,7 +18,6 @@
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_window.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc
index bb4739e..89f07b7 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_challenge_wrapper.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "third_party/securemessage/proto/securemessage.pb.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc
index 744d2c01..e1319ea 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_create_keys_operation.cc
@@ -13,13 +13,13 @@
 #include "ash/components/cryptohome/system_salt_getter.h"
 #include "ash/components/cryptohome/userdataauth_util.h"
 #include "ash/components/login/auth/key.h"
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_types.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/easy_unlock/easy_unlock_client.h"
 #include "chromeos/dbus/userdataauth/userdataauth_client.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc
index f432fb6..83914c20 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_get_keys_operation.cc
@@ -9,10 +9,10 @@
 #include <vector>
 
 #include "ash/components/cryptohome/userdataauth_util.h"
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/userdataauth/userdataauth_client.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc
index 6468bf8..4c76711 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
@@ -14,7 +15,6 @@
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_names.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager_factory.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/account_id/account_id.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
index 72c3566c..1c97433 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "ash/components/login/auth/user_context.h"
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/proximity_auth_local_state_pref_manager.h"
 #include "ash/components/proximity_auth/proximity_auth_profile_pref_manager.h"
 #include "ash/components/proximity_auth/proximity_auth_system.h"
@@ -39,7 +40,6 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h
index 9b96d70..70d0965 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.h
@@ -9,6 +9,7 @@
 #include <set>
 #include <string>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/proximity_auth/smart_lock_metrics_recorder.h"
 #include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
@@ -19,7 +20,6 @@
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_types.h"
 #include "chrome/browser/ash/login/easy_unlock/smartlock_feature_usage_metrics.h"
 #include "chrome/browser/ash/login/easy_unlock/smartlock_state_handler.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 // TODO(https://crbug.com/1164001): move to forward declaration
 #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
 #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
index b5bf1926..79b63c7 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "apps/app_lifetime_monitor_factory.h"
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/proximity_auth/proximity_auth_pref_names.h"
 #include "ash/components/proximity_auth/proximity_auth_profile_pref_manager.h"
 #include "ash/components/proximity_auth/proximity_auth_system.h"
@@ -40,7 +41,6 @@
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/gcm_driver/gcm_profile_service.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h
index 4fa8721c..5b0328ae 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.h
@@ -8,17 +8,17 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
 #include "ash/services/device_sync/proto/cryptauth_api.pb.h"
 #include "ash/services/device_sync/public/cpp/device_sync_client.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
+// TODO(https://crbug.com/1164001): move to forward declaration
+#include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
 #include "base/callback.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-// TODO(https://crbug.com/1164001): move to forward declaration
-#include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
 #include "components/prefs/pref_change_registrar.h"
 
 namespace base {
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
index b84192d..b6125b9 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
@@ -11,6 +11,8 @@
 #include <string>
 #include <utility>
 
+#include "ash/components/multidevice/beacon_seed.h"
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/proximity_auth/fake_lock_handler.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
 #include "ash/constants/ash_features.h"
@@ -37,8 +39,6 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "chromeos/components/multidevice/beacon_seed.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc
index 7f79065..ba86f0f 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.cc
@@ -9,6 +9,11 @@
 #include <memory>
 
 #include "ash/components/login/auth/user_context.h"
+#include "ash/components/multidevice/logging/logging.h"
+#include "ash/components/multidevice/remote_device.h"
+#include "ash/components/multidevice/remote_device_cache.h"
+#include "ash/components/multidevice/remote_device_ref.h"
+#include "ash/components/multidevice/software_feature_state.h"
 #include "ash/components/proximity_auth/proximity_auth_local_state_pref_manager.h"
 #include "ash/components/proximity_auth/smart_lock_metrics_recorder.h"
 #include "ash/constants/ash_features.h"
@@ -31,11 +36,6 @@
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/multidevice/remote_device.h"
-#include "chromeos/components/multidevice/remote_device_cache.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
-#include "chromeos/components/multidevice/software_feature_state.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/tpm/tpm_token_loader.h"
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h
index 1bd1115..83a7891 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_signin.h
@@ -9,6 +9,8 @@
 #include <memory>
 #include <string>
 
+// TODO(https://crbug.com/1164001): move to forward declaration
+#include "ash/components/multidevice/remote_device_cache.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
 // TODO(https://crbug.com/1164001): move to forward declaration
 #include "ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
@@ -17,8 +19,6 @@
 #include "base/values.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_types.h"
-// TODO(https://crbug.com/1164001): move to forward declaration
-#include "chromeos/components/multidevice/remote_device_cache.h"
 
 namespace proximity_auth {
 class ProximityAuthLocalStatePrefManager;
diff --git a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
index e8a8369..174eff1 100644
--- a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
+++ b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
@@ -279,6 +279,8 @@
   std::vector<std::string> expected_input_methods;
   // kPreferredKeyboardLayout is now set to last focused POD.
   expected_input_methods.push_back(user_input_methods[0]);
+  // Owner input method.
+  expected_input_methods.push_back(user_input_methods[2]);
   // Locale default input methods (the first one also is hardware IM).
   Append_en_US_InputMethods(&expected_input_methods);
 
diff --git a/chrome/browser/ash/login/oobe_interactive_ui_test.cc b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
index f798e9d..1a7246d5 100644
--- a/chrome/browser/ash/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
@@ -671,7 +671,7 @@
   }
 
   void PerformStepsBeforeEnrollmentCheck();
-  void PerformSessionSignInSteps();
+  void PerformSessionSignInSteps(bool is_enterprise_enrolled);
 
   void SimpleEndToEnd();
 
@@ -713,7 +713,8 @@
   test::ExitUpdateScreenNoUpdate();
 }
 
-void OobeInteractiveUITest::PerformSessionSignInSteps() {
+void OobeInteractiveUITest::PerformSessionSignInSteps(
+    bool is_enterprise_enrolled) {
   ForceBrandedBuild();
   if (GetFirstSigninScreen() == UserCreationView::kScreenId) {
     test::WaitForUserCreationScreen();
@@ -722,7 +723,9 @@
   WaitForGaiaSignInScreen(test_setup()->arc_state() != ArcState::kNotAvailable);
   LogInAsRegularUser();
 
-  if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
+  if (chromeos::features::IsOobeConsolidatedConsentEnabled() &&
+      (!is_enterprise_enrolled ||
+       test_setup()->arc_state() != ArcState::kNotAvailable)) {
     test::WaitForConsolidatedConsentScreen();
     RunConsolidatedConsentScreenChecks();
     test::TapConsolidatedConsentAccept();
@@ -766,7 +769,7 @@
 
 void OobeInteractiveUITest::SimpleEndToEnd() {
   PerformStepsBeforeEnrollmentCheck();
-  PerformSessionSignInSteps();
+  PerformSessionSignInSteps(false /* is_enterprise_enrolled */);
 
   WaitForLoginDisplayHostShutdown();
 }
@@ -855,7 +858,7 @@
   enrollment_ui_.LeaveSuccessScreen();
   login_screen_waiter->WaitEvenIfShown();
 
-  PerformSessionSignInSteps();
+  PerformSessionSignInSteps(true /* is_enterprise_enrolled */);
 
   WaitForLoginDisplayHostShutdown();
 }
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen.cc
index 4035b03..28407c2 100644
--- a/chrome/browser/ash/login/screens/consolidated_consent_screen.cc
+++ b/chrome/browser/ash/login/screens/consolidated_consent_screen.cc
@@ -18,10 +18,14 @@
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
+#include "chrome/browser/ash/settings/stats_reporting_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
+#include "chrome/browser/metrics/metrics_reporting_state.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -30,6 +34,7 @@
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/consent_auditor/consent_auditor.h"
+#include "components/metrics/metrics_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
@@ -114,18 +119,14 @@
   if (arc::IsArcDemoModeSetupFlow())
     return false;
 
-  // For managed users, admins are required to accept ToS on the server side.
-  // So, if the user is managed and no arc negotiation is needed, skip the
-  // screen. IsManaged() returns true for child users, don't skip consolidated
-  // consent in that case.
-  Profile* profile = ProfileManager::GetActiveUserProfile();
-  CHECK(profile);
-  bool is_child_account =
-      user_manager::UserManager::Get()->IsLoggedInAsChildUser();
-  bool is_enterprise_managed =
-      profile->GetProfilePolicyConnector()->IsManaged() && !is_child_account;
-  if ((is_enterprise_managed &&
-       !arc::IsArcTermsOfServiceOobeNegotiationNeeded()) ||
+  // For managed devices, admins are required to accept ToS on the server side.
+  // So, if the device is managed and no arc negotiation is needed, skip the
+  // screen.
+  policy::BrowserPolicyConnectorAsh* connector =
+      g_browser_process->platform_part()->browser_policy_connector_ash();
+  bool is_device_managed = connector->IsDeviceEnterpriseManaged();
+
+  if ((is_device_managed && !arc::IsArcTermsOfServiceOobeNegotiationNeeded()) ||
       !context->is_branded_build) {
     exit_callback_.Run(Result::NOT_APPLICABLE);
     return true;
@@ -148,24 +149,9 @@
       base::BindOnce(&ConsolidatedConsentScreen::OnOwnershipStatusCheckDone,
                      weak_factory_.GetWeakPtr()));
 
-  bool is_demo = arc::IsArcDemoModeSetupFlow();
-  bool is_arc_enabled = arc::IsArcTermsOfServiceOobeNegotiationNeeded();
-  if (!is_demo && is_arc_enabled) {
-    // Enable ARC to match ArcSessionManager logic. ArcSessionManager expects
-    // that ARC is enabled (prefs::kArcEnabled = true) on showing Terms of
-    // Service. If user accepts ToS then prefs::kArcEnabled is left activated.
-    // If user skips ToS then prefs::kArcEnabled is automatically reset in
-    // ArcSessionManager.
-    arc::SetArcPlayStoreEnabledForProfile(profile, true);
-
-    pref_handler_ = std::make_unique<arc::ArcOptInPreferenceHandler>(
-        this, profile->GetPrefs());
-    pref_handler_->Start();
-  }
-
   ConsolidatedConsentScreenView::ScreenConfig config;
-  config.is_arc_enabled = is_arc_enabled;
-  config.is_demo = is_demo;
+  config.is_arc_enabled = arc::IsArcTermsOfServiceOobeNegotiationNeeded();
+  config.is_demo = arc::IsArcDemoModeSetupFlow();
   config.is_enterprise_managed_account = is_enterprise_managed_account_;
   config.is_child_account = is_child_account_;
   config.country_code = base::CountryCodeForCurrentTimezone();
@@ -195,8 +181,10 @@
 
 void ConsolidatedConsentScreen::OnMetricsModeChanged(bool enabled,
                                                      bool managed) {
+  // When the usage opt-in is not managed, override the enabled value
+  // with `true` to encourage users to consent with it during OptIn flow.
   if (view_)
-    view_->SetUsageMode(enabled, managed);
+    view_->SetUsageMode(/*enabled=*/!managed || enabled, managed);
 }
 
 void ConsolidatedConsentScreen::OnBackupAndRestoreModeChanged(bool enabled,
@@ -215,18 +203,50 @@
 
 void ConsolidatedConsentScreen::OnOwnershipStatusCheckDone(
     DeviceSettingsService::OwnershipStatus status) {
-  bool is_owner = false;
-
   // If no ownership is established yet, then the current user is the first
   // user to sign in. Therefore, the current user would be the owner.
-  if (status == DeviceSettingsService::OWNERSHIP_NONE) {
-    is_owner = true;
-  } else if (status == DeviceSettingsService::OWNERSHIP_TAKEN) {
-    is_owner = user_manager::UserManager::Get()->IsCurrentUserOwner();
+  if (status == DeviceSettingsService::OWNERSHIP_NONE)
+    is_owner_ = true;
+  else if (status == DeviceSettingsService::OWNERSHIP_TAKEN)
+    is_owner_ = user_manager::UserManager::Get()->IsCurrentUserOwner();
+
+  const bool is_negotiation_needed =
+      arc::IsArcTermsOfServiceOobeNegotiationNeeded();
+  // If the user is not the owner and the owner disabled metrics, the user
+  // is not allowed to update the usage opt-in.
+  if (!is_owner_) {
+    const bool is_metrics_enabled =
+        ash::StatsReportingController::Get()->IsEnabled();
+
+    if (!is_negotiation_needed && !is_metrics_enabled) {
+      exit_callback_.Run(Result::NOT_APPLICABLE);
+      return;
+    }
+
+    if (!is_metrics_enabled) {
+      view_->HideUsageOptin();
+    }
+  }
+
+  const bool is_demo = arc::IsArcDemoModeSetupFlow();
+  if (!is_demo && is_negotiation_needed) {
+    // Enable ARC to match ArcSessionManager logic. ArcSessionManager expects
+    // that ARC is enabled (prefs::kArcEnabled = true) on showing Terms of
+    // Service. If user accepts ToS then prefs::kArcEnabled is left activated.
+    // If user skips ToS then prefs::kArcEnabled is automatically reset in
+    // ArcSessionManager.
+    Profile* profile = ProfileManager::GetActiveUserProfile();
+    DCHECK(profile);
+
+    arc::SetArcPlayStoreEnabledForProfile(profile, true);
+
+    pref_handler_ = std::make_unique<arc::ArcOptInPreferenceHandler>(
+        this, profile->GetPrefs());
+    pref_handler_->Start();
   }
 
   if (view_)
-    view_->SetIsDeviceOwner(is_owner);
+    view_->SetIsDeviceOwner(is_owner_.value());
 }
 
 void ConsolidatedConsentScreen::RecordConsents(
@@ -290,12 +310,27 @@
   }
 }
 
+void ConsolidatedConsentScreen::ReportUsageOptIn(bool is_enabled) {
+  DCHECK(is_owner_.has_value());
+  if (is_owner_.value()) {
+    ash::StatsReportingController::Get()->SetEnabled(
+        ProfileManager::GetActiveUserProfile(), is_enabled);
+    return;
+  }
+
+  auto* metrics_service = g_browser_process->metrics_service();
+  DCHECK(metrics_service);
+
+  // If user is not eligible for per-user, this will no-op. See details at
+  // chrome/browser/metrics/per_user_state_manager_chromeos.h.
+  metrics_service->UpdateCurrentUserMetricsConsent(is_enabled);
+}
+
 void ConsolidatedConsentScreen::OnAccept(bool enable_stats_usage,
                                          bool enable_backup_restore,
                                          bool enable_location_services,
                                          const std::string& tos_content) {
-  // Should be called regardless of ARC.
-  pref_handler_->EnableMetrics(enable_stats_usage);
+  ReportUsageOptIn(enable_stats_usage);
 
   if (arc::IsArcDemoModeSetupFlow() ||
       !arc::IsArcTermsOfServiceOobeNegotiationNeeded()) {
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen.h b/chrome/browser/ash/login/screens/consolidated_consent_screen.h
index 61b8102..71f336296 100644
--- a/chrome/browser/ash/login/screens/consolidated_consent_screen.h
+++ b/chrome/browser/ash/login/screens/consolidated_consent_screen.h
@@ -107,11 +107,15 @@
   void OnOwnershipStatusCheckDone(
       DeviceSettingsService::OwnershipStatus status);
 
+  void ReportUsageOptIn(bool is_enabled);
+
   // Exits the screen with `Result::ACCEPTED` in the normal flow, and
   // `Result::ACCEPTED_DEMO_ONLINE` or `Result::ACCEPTED_DEMO_OFFLINE` in the
   // demo setup flow.
   void ExitScreenWithAcceptedResult();
 
+  absl::optional<bool> is_owner_;
+
   bool is_child_account_ = false;
 
   bool is_enterprise_managed_account_ = false;
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
index 78bd441..346114e 100644
--- a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/hash/sha1.h"
 #include "chrome/browser/ash/arc/session/arc_service_launcher.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/fake_arc_tos_mixin.h"
 #include "chrome/browser/ash/login/test/fake_eula_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
@@ -597,14 +598,15 @@
                     ArcManagedOptin::kManagedDisabled);
 }
 
-// When both ARC opt ins are managed, skip the screen.
-// TODO(crbug.com/1273975): this test should be updated once the per user
-// metrics is integrated into consolidated consent.
-IN_PROC_BROWSER_TEST_F(ConsolidatedConsentScreenManagedUserTest, Skip) {
-  SetUpArcEnabledPolicy();
-  SetUpManagedOptIns(ArcManagedOptin::kManagedEnabled,
-                     ArcManagedOptin::kManagedEnabled);
-  LoginManagedUser();
+class ConsolidatedConsentScreenManagedDeviceTest
+    : public ConsolidatedConsentScreenTest {
+ private:
+  DeviceStateMixin device_state_{
+      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
+};
+
+IN_PROC_BROWSER_TEST_F(ConsolidatedConsentScreenManagedDeviceTest, Skip) {
+  LoginAsRegularUser();
   WaitForScreenExit();
   EXPECT_EQ(screen_result_.value(),
             ConsolidatedConsentScreen::Result::NOT_APPLICABLE);
diff --git a/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h b/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h
index 3aabb2e..35d1d24 100644
--- a/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h
+++ b/chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h
@@ -40,6 +40,7 @@
   MOCK_METHOD(void, SetBackupMode, (bool enabled, bool managed));
   MOCK_METHOD(void, SetLocationMode, (bool enabled, bool managed));
   MOCK_METHOD(void, SetIsDeviceOwner, (bool is_owner));
+  MOCK_METHOD(void, HideUsageOptin, ());
 
  private:
   ConsolidatedConsentScreen* screen_ = nullptr;
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index 38076c8..725a4c1f4 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -371,6 +371,15 @@
   multi_profile_user_controller_ =
       std::make_unique<MultiProfileUserController>(this, GetLocalState());
 
+  // |this| is sometimes initialized before owner is ready in CrosSettings for
+  // the consoldiated consent screen flow. Listen for changes to owner setting
+  // to ensure that owner changes are reflected in |this|.
+  // TODO(crbug.com/1307359): Investigate using RetrieveTrustedDevicePolicies
+  // instead of UpdateOwnerId.
+  owner_subscription_ = cros_settings_->AddSettingsObserver(
+      kDeviceOwner, base::BindRepeating(&ChromeUserManagerImpl::UpdateOwnerId,
+                                        weak_factory_.GetWeakPtr()));
+
   policy::DeviceLocalAccountPolicyService* device_local_account_policy_service =
       g_browser_process->platform_part()
           ->browser_policy_connector_ash()
@@ -404,6 +413,16 @@
     enterprise_user_session_metrics::RecordStoredSessionLength();
 }
 
+void ChromeUserManagerImpl::UpdateOwnerId() {
+  std::string owner_email;
+  cros_settings_->GetString(kDeviceOwner, &owner_email);
+
+  user_manager::KnownUser known_user(GetLocalState());
+  const AccountId owner_account_id = known_user.GetAccountId(
+      owner_email, std::string() /* id */, AccountType::UNKNOWN);
+  SetOwnerId(owner_account_id);
+}
+
 ChromeUserManagerImpl::~ChromeUserManagerImpl() {
   if (g_browser_process->profile_manager())
     g_browser_process->profile_manager()->RemoveObserver(this);
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.h b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
index 95bbf3a6..9e69e22 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.h
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
@@ -240,6 +240,8 @@
       const AccountId& account_id,
       const policy::DeviceLocalAccount::Type type) const;
 
+  void UpdateOwnerId();
+
   // Interface to the signed settings store.
   CrosSettings* cros_settings_;
 
@@ -272,6 +274,7 @@
   base::CallbackListSubscription allow_guest_subscription_;
   base::CallbackListSubscription users_subscription_;
   base::CallbackListSubscription family_link_accounts_subscription_;
+  base::CallbackListSubscription owner_subscription_;
 
   base::CallbackListSubscription local_accounts_subscription_;
 
diff --git a/chrome/browser/ash/multidevice_setup/OWNERS b/chrome/browser/ash/multidevice_setup/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/ash/multidevice_setup/OWNERS
+++ b/chrome/browser/ash/multidevice_setup/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc b/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc
index e93de16a..72e3dd5 100644
--- a/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc
+++ b/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/multidevice_setup_service.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h"
@@ -14,7 +15,6 @@
 #include "chrome/browser/ash/device_sync/device_sync_client_factory.h"
 #include "chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc b/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc
index 87e5da9d..fd6f5b4 100644
--- a/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc
+++ b/chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/multidevice_setup/multidevice_setup_service.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
@@ -22,7 +23,6 @@
 #include "chrome/browser/ash/multidevice_setup/oobe_completion_tracker_factory.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/note_taking_helper.cc b/chrome/browser/ash/note_taking_helper.cc
index eb1857f..ca29872 100644
--- a/chrome/browser/ash/note_taking_helper.cc
+++ b/chrome/browser/ash/note_taking_helper.cc
@@ -43,6 +43,7 @@
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/app_update.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
@@ -140,9 +141,9 @@
   return name;
 }
 
-bool IsNoteTakingIntentFilter(const apps::mojom::IntentFilterPtr& filter) {
+bool IsNoteTakingIntentFilter(const apps::IntentFilterPtr& filter) {
   for (const auto& condition : filter->conditions) {
-    if (condition->condition_type != apps::mojom::ConditionType::kAction)
+    if (condition->condition_type != apps::ConditionType::kAction)
       continue;
 
     for (const auto& condition_value : condition->condition_values) {
@@ -154,8 +155,8 @@
 }
 
 bool HasNoteTakingIntentFilter(
-    const std::vector<apps::mojom::IntentFilterPtr>& filters) {
-  for (const apps::mojom::IntentFilterPtr& filter : filters) {
+    const std::vector<apps::IntentFilterPtr>& filters) {
+  for (const apps::IntentFilterPtr& filter : filters) {
     if (IsNoteTakingIntentFilter(filter))
       return true;
   }
diff --git a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc
index f118d9c..4e8595eb 100644
--- a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc
+++ b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/ash/phonehub/browser_tabs_model_provider_impl.h"
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/phonehub/browser_tabs_metadata_fetcher.h"
 #include "ash/components/phonehub/browser_tabs_model.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
diff --git a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc
index 5d07ade..7bf36da4 100644
--- a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc
+++ b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc
@@ -8,11 +8,11 @@
 #include <utility>
 #include <vector>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/phonehub/fake_browser_tabs_metadata_fetcher.h"
 #include "ash/components/phonehub/mutable_phone_model.h"
 #include "ash/components/phonehub/phone_model_test_util.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/sync/driver/mock_sync_service.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
 #include "components/sync_sessions/session_sync_service.h"
diff --git a/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc b/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc
index 17a8e16..8cb3f5e2 100644
--- a/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc
+++ b/chrome/browser/ash/phonehub/camera_roll_download_manager_impl.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/camera_roll_download_manager.h"
 #include "ash/components/phonehub/proto/phonehub_api.pb.h"
 #include "ash/public/cpp/holding_space/holding_space_progress.h"
@@ -29,7 +30,6 @@
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
index 33dcee58..4de41f02 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
@@ -104,6 +104,7 @@
   }
   DlpBooleanHistogram(dlp::kScreenshotBlockedUMA,
                       IsBlocked(info.restriction_info));
+  DlpBooleanHistogram(dlp::kScreenshotWarnedUMA, IsWarn(info.restriction_info));
   CheckScreenCaptureRestriction(info, std::move(callback));
 }
 
@@ -144,10 +145,15 @@
 
 void DlpContentManagerAsh::CheckStoppedVideoCapture(
     ash::OnCaptureModeDlpRestrictionChecked callback) {
+  if (!running_video_capture_info_.has_value()) {
+    std::move(callback).Run(/*proceed=*/true);
+    return;
+  }
   // If some confidential content was shown during the recording, but not
   // before, warn the user before saving the file.
-  if (running_video_capture_info_.has_value() &&
-      !running_video_capture_info_->confidential_contents.IsEmpty()) {
+  DlpBooleanHistogram(dlp::kScreenshotWarnedUMA,
+                      running_video_capture_info_->had_warning_restriction);
+  if (!running_video_capture_info_->confidential_contents.IsEmpty()) {
     const GURL& url =
         running_video_capture_info_->confidential_contents.GetContents()
             .begin()
@@ -170,6 +176,7 @@
             DlpRulesManager::Restriction::kScreenshot, std::move(callback)),
         running_video_capture_info_->confidential_contents);
   } else {
+    DlpBooleanHistogram(dlp::kScreenshotWarnSilentProceededUMA, true);
     std::move(callback).Run(/*proceed=*/true);
   }
 
@@ -206,6 +213,8 @@
 
   DlpBooleanHistogram(dlp::kCaptureModeInitBlockedUMA,
                       IsBlocked(info.restriction_info));
+  DlpBooleanHistogram(dlp::kCaptureModeInitWarnedUMA,
+                      IsWarn(info.restriction_info));
   CheckScreenCaptureRestriction(info, std::move(callback));
 }
 
@@ -623,6 +632,7 @@
                           DlpRulesManager::Restriction::kScreenshot);
     running_video_capture_info_->confidential_contents.UnionWith(
         info.confidential_contents);
+    running_video_capture_info_->had_warning_restriction = true;
     return;
   }
 }
@@ -647,6 +657,7 @@
                           DlpRulesManager::Restriction::kScreenshot);
     if (info.confidential_contents.IsEmpty()) {
       // The user already allowed all the visible content.
+      DlpBooleanHistogram(dlp::kScreenshotWarnSilentProceededUMA, true);
       std::move(callback).Run(true);
       return;
     }
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h
index 33e88356..37576bb 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h
@@ -129,6 +129,10 @@
     // informs if we already sent a warning proceeded event for the warning
     // mode.
     bool was_reported_warning_proceeded = false;
+    // Flag that indicates that there was some content with warn level
+    // restriction captured. Used to indicate that the warn UMA should be
+    // logged, even if no warning is shown.
+    bool had_warning_restriction = false;
   };
 
   DlpContentManagerAsh();
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
index e8b94179..811bf6a 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
@@ -272,63 +272,63 @@
   ScreenshotArea partial_in =
       ScreenshotArea::CreateForPartialWindow(root_window, in_rect);
 
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 0);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 4);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/4,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kBlock, 0u);
 
   helper_->ChangeConfidentiality(web_contents, kScreenshotRestricted);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/false);
-  CheckScreenshotRestriction(window, /*expected=*/false);
-  CheckScreenshotRestriction(partial_in, /*expected=*/false);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 3);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 5);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/3, /*warned_count=*/0,
+                        /*total_count=*/8,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kBlock, 3u);
 
   web_contents->WasHidden();
   helper_->ChangeVisibility(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/false);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 4);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 8);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/4, /*warned_count=*/0,
+                        /*total_count=*/12,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kBlock, 4u);
 
   web_contents->WasShown();
   helper_->ChangeVisibility(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/false);
-  CheckScreenshotRestriction(window, /*expected=*/false);
-  CheckScreenshotRestriction(partial_in, /*expected=*/false);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 7);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 9);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/7, /*warned_count=*/0,
+                        /*total_count=*/16,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kBlock, 7u);
 
   helper_->DestroyWebContents(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 7);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 12);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/7, /*warned_count=*/0,
+                        /*total_count=*/19,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kBlock, 7u);
 }
@@ -357,43 +357,63 @@
   ScreenshotArea partial_in =
       ScreenshotArea::CreateForPartialWindow(root_window, in_rect);
 
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/4,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kWarn, 0u);
 
   helper_->ChangeConfidentiality(web_contents, kScreenshotWarned);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/false);
-  CheckScreenshotRestriction(window, /*expected=*/false);
-  CheckScreenshotRestriction(partial_in, /*expected=*/false);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/3,
+                        /*total_count=*/8,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kWarn, 3u);
 
   web_contents->WasHidden();
   helper_->ChangeVisibility(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/false);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/4,
+                        /*total_count=*/12,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kWarn, 4u);
 
   web_contents->WasShown();
   helper_->ChangeVisibility(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/false);
-  CheckScreenshotRestriction(window, /*expected=*/false);
-  CheckScreenshotRestriction(partial_in, /*expected=*/false);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/false);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/7,
+                        /*total_count=*/16,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kWarn, 7u);
 
   helper_->DestroyWebContents(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/7,
+                        /*total_count=*/19,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kWarn, 7u);
 }
@@ -419,47 +439,47 @@
   ScreenshotArea partial_in =
       ScreenshotArea::CreateForPartialWindow(root_window, in_rect);
 
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kReport, 0u);
 
   helper_->ChangeConfidentiality(web_contents, kScreenshotReported);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kReport, 0u);
 
   web_contents->WasHidden();
   helper_->ChangeVisibility(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kReport, 0u);
 
   web_contents->WasShown();
   helper_->ChangeVisibility(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(window, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(window, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kReport, 0u);
 
   helper_->DestroyWebContents(web_contents);
-  CheckScreenshotRestriction(fullscreen, /*expected=*/true);
-  CheckScreenshotRestriction(partial_in, /*expected=*/true);
-  CheckScreenshotRestriction(partial_out, /*expected=*/true);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 0);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 19);
+  CheckScreenshotRestriction(fullscreen, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_in, /*expected_allowed=*/true);
+  CheckScreenshotRestriction(partial_out, /*expected_allowed=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/19,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   CheckEvents(DlpRulesManager::Restriction::kScreenshot,
               DlpRulesManager::Level::kReport, 0u);
 }
@@ -715,12 +735,16 @@
   EXPECT_FALSE(helper_->HasAnyContentCached());
   capture_mode_delegate->StopObservingRestrictedContent(
       on_dlp_checked_at_video_end_cb.Get());
+  histogram_tester_.ExpectUniqueSample(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnedUMA, true, 1);
   // Check that the warning is now shown.
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1);
   // Hit Enter to "Save anyway".
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
       browser(), ui::VKEY_RETURN, /*control=*/false,
       /*shift=*/false, /*alt=*/false, /*command=*/false));
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 0);
   EXPECT_TRUE(helper_->HasContentCachedForRestriction(
       web_contents1, DlpRulesManager::Restriction::kScreenshot));
@@ -780,12 +804,16 @@
   EXPECT_FALSE(helper_->HasAnyContentCached());
   capture_mode_delegate->StopObservingRestrictedContent(
       on_dlp_checked_at_video_end_cb.Get());
+  histogram_tester_.ExpectUniqueSample(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnedUMA, true, 1);
   // Check that the warning is now shown.
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1);
   // Hit Enter to "Cancel".
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
       browser(), ui::VKEY_ESCAPE, /*control=*/false,
       /*shift=*/false, /*alt=*/false, /*command=*/false));
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 1);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 0);
   EXPECT_FALSE(helper_->HasAnyContentCached());
 
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc
index 92a933f..cc5de3c 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_unittest.cc
@@ -579,8 +579,6 @@
   // The warning should be shown only once.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1);
 
-  SetReportQueueForReportingManager();
-  SetupDlpRulesManager();
   EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern(_, _, _))
       .Times(3)
       .WillRepeatedly(::testing::Return(kSrcPattern));
@@ -668,8 +666,6 @@
   // If the user cancels, the warning can be shown again for the same contents.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2);
 
-  SetReportQueueForReportingManager();
-  SetupDlpRulesManager();
   EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern(_, _, _))
       .Times(2)
       .WillRepeatedly(::testing::Return(kSrcPattern));
@@ -758,10 +754,10 @@
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, true, 0);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, false, 1);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/1,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
 
   // Block restriction is enforced for web_contents: block.
   helper_.ChangeConfidentiality(web_contents.get(), kScreenshotRestricted);
@@ -771,11 +767,10 @@
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(false /*expected*/);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, true, 1);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, false, 1);
-
+  VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0,
+                        /*total_count=*/2,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
   EXPECT_EQ(events_.size(), 1u);
   EXPECT_THAT(events_[0],
               IsDlpPolicyEvent(CreateDlpPolicyEvent(
@@ -789,22 +784,20 @@
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
   VerifyAndResetActionAllowed(true /*expected*/);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, true, 1);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kCaptureModeInitBlockedUMA, false, 2);
+  VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0,
+                        /*total_count=*/3,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
 }
 
 TEST_F(DlpContentManagerAshCheckRestrictionTest,
        CaptureModeInitWarnedContinued) {
   // Set the notifier to "Proceed" on the warning.
   MockDlpWarnNotifier* mock_dlp_warn_notifier =
-      CreateAndSetDlpWarnNotifier(true /*should_proceed*/);
+      CreateAndSetDlpWarnNotifier(/*should_proceed=*/true);
   // The warning should be shown only once.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1);
 
-  SetReportQueueForReportingManager();
-  SetupDlpRulesManager();
   EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
       .Times(1)
       .WillRepeatedly(::testing::Return(kSrcPattern));
@@ -816,7 +809,11 @@
   EXPECT_FALSE(helper_.HasAnyContentCached());
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/1,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
+  VerifyAndResetActionAllowed(/*expected=*/true);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_TRUE(events_.empty());
 
@@ -826,7 +823,14 @@
             kScreenshotWarned);
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1,
+                        /*total_count=*/2,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1);
+
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
   EXPECT_EQ(events_.size(), 1u);
@@ -838,7 +842,16 @@
   // Check again: allow based on cached user's response - no dialog is shown.
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/3,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnSilentProceededUMA, true,
+      1);
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
   EXPECT_EQ(events_.size(), 1u);
@@ -849,7 +862,11 @@
             kEmptyRestrictionSet);
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/4,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
   EXPECT_EQ(events_.size(), 1u);
 }
 
@@ -857,12 +874,10 @@
        CaptureModeInitWarnedCancelled) {
   // Set the notifier to "Proceed" on the warning.
   MockDlpWarnNotifier* mock_dlp_warn_notifier =
-      CreateAndSetDlpWarnNotifier(false /*should_proceed*/);
+      CreateAndSetDlpWarnNotifier(/*should_proceed=*/false);
   // If the user cancels, the warning can be shown again for the same contents.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2);
 
-  SetReportQueueForReportingManager();
-  SetupDlpRulesManager();
   EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
       .Times(2)
       .WillRepeatedly(::testing::Return(kSrcPattern));
@@ -874,7 +889,11 @@
   EXPECT_FALSE(helper_.HasAnyContentCached());
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/1,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
+  VerifyAndResetActionAllowed(/*expected=*/true);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_TRUE(events_.empty());
 
@@ -884,7 +903,13 @@
             kScreenshotWarned);
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(false /*expected*/);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1,
+                        /*total_count=*/2,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 1);
+  VerifyAndResetActionAllowed(/*expected=*/false);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_EQ(events_.size(), 1u);
   EXPECT_THAT(events_[0],
@@ -895,7 +920,13 @@
   // Check again: since the user previously cancelled, dialog is shown again.
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(false /*expected*/);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/3,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 2);
+  VerifyAndResetActionAllowed(/*expected=*/false);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_EQ(events_.size(), 2u);
   EXPECT_THAT(events_[1],
@@ -909,8 +940,11 @@
             kEmptyRestrictionSet);
   GetManager()->CheckCaptureModeInitRestriction(
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/4,
+                        /*blocked_suffix=*/dlp::kCaptureModeInitBlockedUMA,
+                        /*warned_suffix=*/dlp::kCaptureModeInitWarnedUMA);
+  VerifyAndResetActionAllowed(/*expected=*/true);
   EXPECT_EQ(events_.size(), 2u);
 }
 
@@ -930,11 +964,11 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 0);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 1);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/1,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
 
   // Block restriction is enforced for web_contents: block.
   helper_.ChangeConfidentiality(web_contents.get(), kScreenshotRestricted);
@@ -944,11 +978,11 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(false /*expected*/);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 1);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 1);
+  VerifyAndResetActionAllowed(/*expected=*/false);
+  VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0,
+                        /*total_count=*/2,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
 
   EXPECT_EQ(events_.size(), 1u);
   EXPECT_THAT(events_[0],
@@ -963,22 +997,20 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, true, 1);
-  histogram_tester_.ExpectBucketCount(
-      GetDlpHistogramPrefix() + dlp::kScreenshotBlockedUMA, false, 2);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/1, /*warned_count=*/0,
+                        /*total_count=*/3,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
 }
 
 TEST_F(DlpContentManagerAshCheckRestrictionTest, ScreenshotWarnedContinued) {
   // Set the notifier to "Proceed" on the warning.
   MockDlpWarnNotifier* mock_dlp_warn_notifier =
-      CreateAndSetDlpWarnNotifier(true /*should_proceed*/);
+      CreateAndSetDlpWarnNotifier(/*should_proceed=*/true);
   // The warning should be shown only once.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(1);
 
-  SetReportQueueForReportingManager();
-  SetupDlpRulesManager();
   EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
       .Times(1)
       .WillRepeatedly(::testing::Return(kSrcPattern));
@@ -993,7 +1025,11 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/1,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_TRUE(events_.empty());
 
@@ -1004,7 +1040,11 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1,
+                        /*total_count=*/2,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
   EXPECT_EQ(events_.size(), 1u);
@@ -1017,7 +1057,13 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/3,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1);
   EXPECT_TRUE(helper_.HasContentCachedForRestriction(
       web_contents.get(), DlpRulesManager::Restriction::kScreenshot));
   EXPECT_EQ(events_.size(), 1u);
@@ -1029,19 +1075,26 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/4,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, true, 1);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnSilentProceededUMA, true,
+      1);
   EXPECT_EQ(events_.size(), 1u);
 }
 
 TEST_F(DlpContentManagerAshCheckRestrictionTest, ScreenshotWarnedCancelled) {
   // Set the notifier to "Proceed" on the warning.
   MockDlpWarnNotifier* mock_dlp_warn_notifier =
-      CreateAndSetDlpWarnNotifier(false /*should_proceed*/);
+      CreateAndSetDlpWarnNotifier(/*should_proceed=*/false);
   // If the user cancels, the warning can be shown again for the same contents.
   EXPECT_CALL(*mock_dlp_warn_notifier, ShowDlpWarningDialog(_, _)).Times(2);
 
-  SetReportQueueForReportingManager();
-  SetupDlpRulesManager();
   EXPECT_CALL(*mock_rules_manager_, GetSourceUrlPattern)
       .Times(2)
       .WillRepeatedly(::testing::Return(kSrcPattern));
@@ -1056,7 +1109,11 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/0,
+                        /*total_count=*/1,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_TRUE(events_.empty());
 
@@ -1067,7 +1124,13 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(false /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/false);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/1,
+                        /*total_count=*/2,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 1);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_EQ(events_.size(), 1u);
   EXPECT_THAT(events_[0],
@@ -1079,7 +1142,13 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(false /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/false);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/3,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
+  histogram_tester_.ExpectBucketCount(
+      GetDlpHistogramPrefix() + dlp::kScreenshotWarnProceededUMA, false, 2);
   EXPECT_FALSE(helper_.HasAnyContentCached());
   EXPECT_EQ(events_.size(), 2u);
   EXPECT_THAT(events_[1],
@@ -1094,7 +1163,11 @@
   GetManager()->CheckScreenshotRestriction(
       area,
       base::BindOnce(on_dlp_restriction_checked_callback, &is_action_allowed_));
-  VerifyAndResetActionAllowed(true /*expected*/);
+  VerifyAndResetActionAllowed(/*expected=*/true);
+  VerifyHistogramCounts(/*blocked_count=*/0, /*warned_count=*/2,
+                        /*total_count=*/4,
+                        /*blocked_suffix=*/dlp::kScreenshotBlockedUMA,
+                        /*warned_suffix=*/dlp::kScreenshotWarnedUMA);
   EXPECT_EQ(events_.size(), 2u);
 }
 
diff --git a/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc b/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc
index d9f4130..8911629 100644
--- a/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc
+++ b/chrome/browser/ash/policy/invalidation/affiliated_invalidation_service_provider_impl_unittest.cc
@@ -79,7 +79,8 @@
 // the invalidation::InvalidationService that is currently being made available.
 class FakeConsumer : public AffiliatedInvalidationServiceProvider::Consumer {
  public:
-  explicit FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider);
+  FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider,
+               const std::string& invalidation_owner_name);
 
   FakeConsumer(const FakeConsumer&) = delete;
   FakeConsumer& operator=(const FakeConsumer&) = delete;
@@ -158,8 +159,9 @@
   session_manager::SessionManager session_manager_;
 };
 
-FakeConsumer::FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider)
-    : provider_(provider) {
+FakeConsumer::FakeConsumer(AffiliatedInvalidationServiceProviderImpl* provider,
+                           const std::string& invalidation_owner_name)
+    : provider_(provider), invalidation_handler_(invalidation_owner_name) {
   provider_->RegisterConsumer(this);
 }
 
@@ -386,7 +388,7 @@
        NoInvalidationServiceAvailable) {
   // Register a consumer. Verify that the consumer is not called back
   // immediately as no connected invalidation service exists yet.
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
   EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
 }
 
@@ -397,7 +399,7 @@
 // connects, it is made available to the consumer.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        UseDeviceInvalidationService) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Indicate that the device-global invalidation service connected. Verify that
   // that the consumer is informed about this.
@@ -421,7 +423,7 @@
 // affiliated user connects, it is made available to the consumer.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        UseAffiliatedProfileInvalidationService) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Verify that a device-global invalidation service has been created.
   EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
@@ -443,7 +445,7 @@
 // unaffiliated user connects, it is ignored.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        DoNotUseUnaffiliatedProfileInvalidationService) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Verify that a device-global invalidation service has been created.
   EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
@@ -462,7 +464,7 @@
 // consumer instead and the device-global invalidation service is destroyed.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        SwitchToAffiliatedProfileInvalidationService) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Indicate that the device-global invalidation service connected. Verify that
   // that the consumer is informed about this.
@@ -479,7 +481,7 @@
 // |invalidation::INVALIDATIONS_ENABLED| are treated as disconnected.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        FlipInvalidationServiceState) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Create and make |profile_invalidation_service_| enabled.
   LogInAsAffiliatedUserAndConnectInvalidationService();
@@ -509,7 +511,7 @@
 // consumer.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        DoNotSwitchToUnaffiliatedProfileInvalidationService) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Indicate that the device-global invalidation service connected. Verify that
   // that the consumer is informed about this.
@@ -530,7 +532,7 @@
 // service connects, it is made available to the consumer.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        SwitchToDeviceInvalidationService) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Verify that a device-global invalidation service has been created.
   EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
@@ -560,7 +562,7 @@
 // to the second user is made available to the consumer instead.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        SwitchBetweenAffiliatedProfileInvalidationServices) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Verify that a device-global invalidation service has been created.
   EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
@@ -614,7 +616,7 @@
 // consumer. Further verifies that when the second consumer also unregisters,
 // the device-global invalidation service is destroyed.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest, MultipleConsumers) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Indicate that the device-global invalidation service connected. Verify that
   // that the consumer is informed about this.
@@ -623,7 +625,7 @@
   // Register a second consumer. Verify that the consumer is called back
   // immediately as a connected invalidation service is available.
   std::unique_ptr<FakeConsumer> second_consumer(
-      new FakeConsumer(provider_.get()));
+      new FakeConsumer(provider_.get(), "second_consumer"));
   EXPECT_EQ(1, second_consumer->GetAndClearInvalidationServiceSetCount());
   EXPECT_EQ(device_invalidation_service_,
             second_consumer->GetInvalidationService());
@@ -650,7 +652,7 @@
 // service belonging to a second affiliated user that subsequently connects is
 // ignored.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest, NoServiceAfterShutdown) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Verify that a device-global invalidation service has been created.
   EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
@@ -701,7 +703,7 @@
 // This is a regression test for http://crbug.com/455504.
 TEST_F(AffiliatedInvalidationServiceProviderImplTest,
        ConnectedDeviceGlobalInvalidationServiceOnShutdown) {
-  consumer_ = std::make_unique<FakeConsumer>(provider_.get());
+  consumer_ = std::make_unique<FakeConsumer>(provider_.get(), "consumer");
 
   // Verify that a device-global invalidation service has been created.
   EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc b/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc
index 82c934c..ee6ba8d 100644
--- a/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc
+++ b/chrome/browser/ash/policy/scheduled_task_handler/device_scheduled_reboot_handler.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/ash/policy/scheduled_task_handler/scheduled_task_util.h"
 #include "chrome/browser/ash/policy/scheduled_task_handler/scoped_wake_lock.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/user_manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -99,8 +98,7 @@
 
   // If the device is on the sign-in screen, skip reboot only if the grace
   // period is applied.
-  if (!skip_reboot_ &&
-      session_manager::SessionManager::Get()->IsScreenLocked()) {
+  if (!skip_reboot_ && !user_manager::UserManager::Get()->IsUserLoggedIn()) {
     RebootDevice(kRebootDescriptionOnTimerExpired);
     return;
   }
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc
index 0ad0c3b..eb9de50e 100644
--- a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc
+++ b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.cc
@@ -66,8 +66,7 @@
     notification_timer_.Stop();
   if (dialog_timer_.IsRunning())
     dialog_timer_.Stop();
-  notification_controller_.CloseRebootNotification();
-  notification_controller_.CloseRebootDialog();
+  CloseNotifications();
   reboot_callback_.Reset();
 }
 
@@ -109,4 +108,9 @@
   return (reboot_time - GetCurrentTime());
 }
 
+void RebootNotificationsScheduler::CloseNotifications() {
+  notification_controller_.CloseRebootNotification();
+  notification_controller_.CloseRebootDialog();
+}
+
 }  // namespace policy
\ No newline at end of file
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h
index cc41fa6..d3e6a97 100644
--- a/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h
+++ b/chrome/browser/ash/policy/scheduled_task_handler/reboot_notifications_scheduler.h
@@ -57,6 +57,9 @@
   // Returns delay from now until |reboot_time|.
   base::TimeDelta GetRebootDelay(const base::Time& reboot_time) const;
 
+  // Closes the reboot notification and the reboot dialog.
+  virtual void CloseNotifications();
+
   // Timers for scheduling notification or dialog displaying.
   base::WallClockTimer notification_timer_, dialog_timer_;
   // Controller responsible for creating notifications and dialog.
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc b/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc
index ee0243ab0..c082c8a 100644
--- a/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc
+++ b/chrome/browser/ash/policy/scheduled_task_handler/test/device_scheduled_reboot_handler_unittest.cc
@@ -24,7 +24,6 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "services/device/public/cpp/test/test_wake_lock_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -96,7 +95,6 @@
             std::move(notifications_scheduler));
     // Set 0 delay for tests.
     device_scheduled_reboot_handler_->SetRebootDelayForTest(base::TimeDelta());
-    session_manager_.SetSessionState(session_manager::SessionState::ACTIVE);
   }
 
   ~DeviceScheduledRebootHandlerTest() override {
@@ -156,6 +154,18 @@
             task_environment_.GetMockClock()->Now());
   }
 
+  void InitWithFeatureFlag(bool enable_force_scheduled_reboots) {
+    if (enable_force_scheduled_reboots) {
+      scoped_feature_list_.InitWithFeatures(
+          /* enabled_features */ {ash::features::kDeviceForceScheduledReboot},
+          /* disabled_features */ {});
+      return;
+    }
+    scoped_feature_list_.InitWithFeatures(
+        /* enabled_features */ {},
+        /* disabled_features */ {ash::features::kDeviceForceScheduledReboot});
+  }
+
   base::test::TaskEnvironment task_environment_;
   ash::MockUserManager* mock_user_manager_;  // Not owned.
   user_manager::ScopedUserManager user_manager_enabler_;
@@ -166,14 +176,11 @@
   device::TestWakeLockProvider wake_lock_provider_;
   FakeRebootNotificationsScheduler* notifications_scheduler_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  session_manager::SessionManager session_manager_;
 };
 
 TEST_F(DeviceScheduledRebootHandlerTest,
        CheckIfDailyRebootIsScheduledForKiosk) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {},
-      /* disabled_features */ {ash::features::kDeviceForceScheduledReboot});
+  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
   EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp())
       .WillRepeatedly(testing::Return(true));
 
@@ -212,11 +219,9 @@
 
 TEST_F(DeviceScheduledRebootHandlerTest,
        CheckIfDailyRebootIsScheduledForNonKiosk) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {},
-      /* disabled_features */ {ash::features::kDeviceForceScheduledReboot});
-  EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp())
-      .WillRepeatedly(testing::Return(false));
+  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
+  EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
+      .WillRepeatedly(testing::Return(true));
 
   // Calculate time from one hour from now and set the reboot policy to
   // happen daily at that time.
@@ -261,11 +266,11 @@
 
 TEST_F(DeviceScheduledRebootHandlerTest,
        CheckIfWeeklyUpdateCheckIsScheduledForKiosk) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {},
-      /* disabled_features */ {ash::features::kDeviceForceScheduledReboot});
+  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
   EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp())
-      .WillOnce(testing::Return(false));
+      .WillRepeatedly(testing::Return(false));
+  EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
+      .WillRepeatedly(testing::Return(true));
   // Set the first reboot to happen 49 hours from now (i.e. 1 hour from 2
   // days from now) and then weekly after.
   base::TimeDelta delay_from_now = base::Hours(49);
@@ -303,11 +308,12 @@
 
 TEST_F(DeviceScheduledRebootHandlerTest,
        CheckIfMonthlyRebootIsScheduledForKiosk) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {},
-      /* disabled_features */ {ash::features::kDeviceForceScheduledReboot});
+  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
   EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp())
-      .WillOnce(testing::Return(false));
+      .WillRepeatedly(testing::Return(false));
+  EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
+      .WillRepeatedly(testing::Return(true));
+
   // Set the first reboot to happen 1 hour from now.
   base::TimeDelta delay_from_now = base::Hours(1);
   auto policy_and_next_reboot_time = scheduled_task_test_util::CreatePolicy(
@@ -357,6 +363,11 @@
 
 TEST_F(DeviceScheduledRebootHandlerTest,
        CheckIfDailyRebootIsScheduledWithExternalDelay) {
+  // Login user and disable kDeviceScheduledReboot flag. The reboot should not
+  // occur.
+  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
+  EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
+      .WillRepeatedly(testing::Return(true));
   device_scheduled_reboot_handler_->SetRebootDelayForTest(kExternalRebootDelay);
 
   // Calculate time from one hour from now and set the reboot policy to
@@ -396,11 +407,10 @@
 }
 
 TEST_F(DeviceScheduledRebootHandlerTest,
-       CheckIfDailyRebootIsScheduledForLockScreen) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {},
-      /* disabled_features */ {ash::features::kDeviceForceScheduledReboot});
-  session_manager_.SetSessionState(session_manager::SessionState::LOCKED);
+       CheckIfDailyRebootIsScheduledForLoginScreen) {
+  InitWithFeatureFlag(false /* enable_force_scheduled_reboots */);
+  EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
+      .WillRepeatedly(testing::Return(false));
 
   // Set device uptime to 10 minutes and schedule reboot in 30 minutes. Apply
   // grace time - reboot should not occur.
@@ -443,9 +453,7 @@
 }
 
 TEST_F(DeviceScheduledRebootHandlerTest, EnableForceRebootFeatureInKiosk) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {ash::features::kDeviceForceScheduledReboot},
-      /* disabled_features */ {});
+  InitWithFeatureFlag(true /* enable_force_scheduled_reboots */);
 
   // Set device uptime to 10 minutes and enable kiosk mode. We don't apply grace
   // period to kiosks, so reboot should occur.
@@ -482,11 +490,9 @@
 
 TEST_F(DeviceScheduledRebootHandlerTest,
        EnableForceRebootFeatureNonKioskSession) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {ash::features::kDeviceForceScheduledReboot},
-      /* disabled_features */ {});
-  EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp())
-      .WillRepeatedly(testing::Return(false));
+  InitWithFeatureFlag(true /* enable_force_scheduled_reboots */);
+  EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
+      .WillRepeatedly(testing::Return(true));
 
   // Set device uptime to 10 minutes and schedule reboot in 30 minutes. Apply
   // grace time - reboot should not occur.
@@ -532,11 +538,9 @@
 }
 
 TEST_F(DeviceScheduledRebootHandlerTest, SimulateNotificationButtonClick) {
-  scoped_feature_list_.InitWithFeatures(
-      /* enabled_features */ {ash::features::kDeviceForceScheduledReboot},
-      /* disabled_features */ {});
-  EXPECT_CALL(*mock_user_manager_, IsLoggedInAsKioskApp())
-      .WillRepeatedly(testing::Return(false));
+  InitWithFeatureFlag(true /* enable_force_scheduled_reboots */);
+  EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
+      .WillRepeatedly(testing::Return(true));
 
   /// Schedule reboot to happen in 3 hours.
   base::TimeDelta delay_from_now = base::Hours(3);
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc
index 561f33f..7338a578 100644
--- a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc
+++ b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.cc
@@ -48,4 +48,7 @@
     const {
   return uptime_;
 }
+
+void FakeRebootNotificationsScheduler::CloseNotifications() {}
+
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h
index d4ad5a93..15c7718 100644
--- a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h
+++ b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_reboot_notifications_scheduler.h
@@ -36,6 +36,8 @@
 
   const base::TimeDelta GetSystemUptime() const override;
 
+  void CloseNotifications() override;
+
   int show_dialog_calls_ = 0, show_notification_calls_ = 0;
   const base::Clock* clock_;
   // Default uptime for test is 10h.
diff --git a/chrome/browser/ash/printing/oauth2/http_exchange.cc b/chrome/browser/ash/printing/oauth2/http_exchange.cc
index 6cca59d..85ecaff 100644
--- a/chrome/browser/ash/printing/oauth2/http_exchange.cc
+++ b/chrome/browser/ash/printing/oauth2/http_exchange.cc
@@ -29,7 +29,7 @@
 std::string ToString(ContentFormat format) {
   switch (format) {
     case ContentFormat::kJson:
-      return "application/json;charset=UTF-8";
+      return "application/json";
     case ContentFormat::kXWwwFormUrlencoded:
       return "application/x-www-form-urlencoded";
     default:
@@ -115,6 +115,7 @@
   resource_request->method = http_method;
   resource_request->url = url;
   resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  resource_request->headers.SetHeader("Accept", "application/json");
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::CompleteNetworkTrafficAnnotation("printing_oauth2_http_exchange",
                                             partial_traffic_annotation, R"(
diff --git a/chrome/browser/ash/printing/oauth2/status_code.h b/chrome/browser/ash/printing/oauth2/status_code.h
index 1d500cf..9d3ee87 100644
--- a/chrome/browser/ash/printing/oauth2/status_code.h
+++ b/chrome/browser/ash/printing/oauth2/status_code.h
@@ -45,10 +45,11 @@
   kUnexpectedError
 };
 
-// This type of callback is returned to the caller. When `status` equals
-// StatusCode::kOK the access token or authorization URL is returned in
-// `data`. In other case `data` contains an error message or an empty string.
-using AuthServerSessionCallback =
+// This is the standard callback used in oauth2 namespace. When `status` equals
+// StatusCode::kOK, `data` may contain an access token or authorization URL.
+// When `status` is different than StatusCode::kOK, `data` may contain
+// an additional error message.
+using StatusCallback =
     base::OnceCallback<void(StatusCode status, const std::string& data)>;
 
 }  // namespace oauth2
diff --git a/chrome/browser/ash/printing/oauth2/test_authorization_server.cc b/chrome/browser/ash/printing/oauth2/test_authorization_server.cc
new file mode 100644
index 0000000..a5dfa67
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/test_authorization_server.cc
@@ -0,0 +1,215 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/printing/oauth2/test_authorization_server.h"
+
+#include <list>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/containers/flat_map.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/escape.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "chrome/browser/ash/printing/oauth2/status_code.h"
+#include "chromeos/printing/uri.h"
+#include "net/base/escape.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
+#include "url/gurl.h"
+
+namespace ash {
+namespace printing {
+namespace oauth2 {
+
+namespace {
+
+// Checks 'value' == 'expected'. Returns empty string when true, otherwise
+// returns error message.
+std::string ExpectEqual(const std::string& name,
+                        const std::string& value,
+                        const std::string& expected) {
+  if (value == expected) {
+    return "";
+  }
+  return base::StringPrintf("Invalid %s: got \"%s\", expected \"%s\";  ",
+                            name.c_str(), value.c_str(), expected.c_str());
+}
+
+// Returns a list with a single element `value`.
+base::Value OneElementArray(const std::string& value) {
+  base::Value::List arr;
+  arr.Append(value);
+  return base::Value(std::move(arr));
+}
+
+}  // namespace
+
+bool ParseURLParameters(const std::string& params_str,
+                        base::flat_map<std::string, std::string>& results) {
+  results.clear();
+  auto params_vect = base::SplitString(params_str, "&", base::KEEP_WHITESPACE,
+                                       base::SPLIT_WANT_ALL);
+  for (auto& key_val : params_vect) {
+    auto equal_sign = key_val.find('=');
+    std::string key;
+    std::string val;
+    if (equal_sign < key_val.size()) {
+      key = key_val.substr(0, equal_sign);
+      val = key_val.substr(equal_sign + 1);
+    } else {
+      key = key_val;
+    }
+    key = net::UnescapeBinaryURLComponent(key);
+    if (key.empty() || results.contains(key)) {
+      return false;
+    }
+    results[key] = net::UnescapeBinaryURLComponent(val);
+  }
+  return true;
+}
+
+base::OnceCallback<void(StatusCode, const std::string&)> BindResult(
+    CallbackResult& target) {
+  target.status = StatusCode::kUnexpectedError;
+  target.data.clear();
+  auto save_results = [](CallbackResult* target, StatusCode status,
+                         const std::string& data) {
+    target->status = status;
+    target->data = data;
+  };
+  return base::BindOnce(save_results, base::Unretained(&target));
+}
+
+base::flat_map<std::string, base::Value> BuildMetadata(
+    const std::string& authorization_server_uri,
+    const std::string& authorization_uri,
+    const std::string& token_uri,
+    const std::string& registration_uri,
+    const std::string& revocation_uri) {
+  base::flat_map<std::string, base::Value> fields;
+  fields["issuer"] = base::Value(authorization_server_uri);
+  fields["authorization_endpoint"] = base::Value(authorization_uri);
+  fields["token_endpoint"] = base::Value(token_uri);
+  if (!registration_uri.empty()) {
+    fields["registration_endpoint"] = base::Value(registration_uri);
+  }
+  if (!revocation_uri.empty()) {
+    fields["revocation_endpoint"] = base::Value(revocation_uri);
+  }
+  fields["response_types_supported"] = OneElementArray("code");
+  fields["response_modes_supported"] = OneElementArray("query");
+  fields["grant_types_supported"] = OneElementArray("authorization_code");
+  fields["token_endpoint_auth_methods_supported"] = OneElementArray("none");
+  fields["code_challenge_methods_supported"] = OneElementArray("S256");
+  return fields;
+}
+
+FakeAuthorizationServer::FakeAuthorizationServer() {
+  fake_server_.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        upload_data_.push(network::GetUploadData(request));
+      }));
+}
+
+FakeAuthorizationServer::~FakeAuthorizationServer() = default;
+
+std::string FakeAuthorizationServer::ReceiveGET(const std::string& url) {
+  std::string payload;
+  auto msg = GetNextRequest("GET", url, "", payload);
+  if (!payload.empty()) {
+    msg += "Unexpected payload: \"" + payload.substr(0, 256) + "\"";
+  }
+  return msg;
+}
+
+std::string FakeAuthorizationServer::ReceivePOSTWithJSON(
+    const std::string& url,
+    base::flat_map<std::string, base::Value>& out_params) {
+  std::string payload;
+  auto msg = GetNextRequest("POST", url, "application/json", payload);
+  auto content = base::JSONReader::Read(payload);
+  out_params.clear();
+  if (content && content->type() == base::Value::Type::DICTIONARY) {
+    for (auto elem : content->GetDict()) {
+      out_params[elem.first] = std::move(elem.second);
+    }
+  } else {
+    msg += "Cannot parse the payload: \"" + payload.substr(0, 256) + "\"";
+  }
+  return msg;
+}
+
+std::string FakeAuthorizationServer::ReceivePOSTWithURLParams(
+    const std::string& url,
+    base::flat_map<std::string, std::string>& out_params) {
+  std::string payload;
+  auto msg =
+      GetNextRequest("POST", url, "application/x-www-form-urlencoded", payload);
+  if (!ParseURLParameters(payload, out_params)) {
+    msg += "Cannot parse the payload: \"" + payload.substr(0, 256) + "\"";
+  }
+  return msg;
+}
+
+void FakeAuthorizationServer::ResponseWithJSON(
+    net::HttpStatusCode status,
+    const base::flat_map<std::string, base::Value>& params) {
+  CHECK(current_request_);
+  std::string response_content;
+  CHECK(base::JSONWriter::Write(base::Value(params), &response_content));
+  network::mojom::URLResponseHeadPtr response_head(
+      network::CreateURLResponseHead(status));
+  response_head->headers->SetHeader("Content-Type", "application/json");
+  response_head->headers->SetHeader("Cache-Control", "no-store");
+  response_head->headers->SetHeader("Pragma", "no-cache");
+  network::URLLoaderCompletionStatus compl_status;
+  CHECK(fake_server_.SimulateResponseForPendingRequest(
+      current_request_->request.url, compl_status, std::move(response_head),
+      response_content));
+  current_request_ = nullptr;
+  task_environment_.RunUntilIdle();
+}
+
+std::string FakeAuthorizationServer::GetNextRequest(
+    const std::string& method,
+    const std::string& url,
+    const std::string& content_type,
+    std::string& content) {
+  CHECK(!current_request_);
+  task_environment_.RunUntilIdle();
+  current_request_ = fake_server_.GetPendingRequest(0);
+  if (!current_request_) {
+    return "There is no pending requests";
+  }
+  CHECK(!upload_data_.empty());
+  content = std::move(upload_data_.front());
+  upload_data_.pop();
+
+  std::string msg;
+  msg += ExpectEqual("HTTP method", current_request_->request.method, method);
+  msg += ExpectEqual("URL", current_request_->request.url.spec(), url);
+  std::string value;
+  current_request_->request.headers.GetHeader("Content-Type", &value);
+  msg += ExpectEqual("header Content-Type", value, content_type);
+  if (current_request_->request.headers.GetHeader("Accept", &value)) {
+    msg += ExpectEqual("header Accept", value, "application/json");
+  }
+  return msg;
+}
+
+}  // namespace oauth2
+}  // namespace printing
+}  // namespace ash
diff --git a/chrome/browser/ash/printing/oauth2/test_authorization_server.h b/chrome/browser/ash/printing/oauth2/test_authorization_server.h
new file mode 100644
index 0000000..c16627f
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/test_authorization_server.h
@@ -0,0 +1,123 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_PRINTING_OAUTH2_TEST_AUTHORIZATION_SERVER_H_
+#define CHROME_BROWSER_ASH_PRINTING_OAUTH2_TEST_AUTHORIZATION_SERVER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/queue.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "chrome/browser/ash/printing/oauth2/status_code.h"
+#include "chromeos/printing/uri.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace ash {
+namespace printing {
+namespace oauth2 {
+
+// Helper function that parses parameters formatted as URL query. Returns false
+// <=> the parsing failed (at least one of the keys is empty or repeated). The
+// results are saved to `results` that is cleared at the beginning.
+bool ParseURLParameters(const std::string& params_str,
+                        base::flat_map<std::string, std::string>& results);
+
+// Represents results returned by callback void(StatusCode, const string&).
+struct CallbackResult {
+  StatusCode status = StatusCode::kUnexpectedError;
+  std::string data;
+};
+
+// Helper function that returns callback saving its parameters to a given
+// structure.
+StatusCallback BindResult(CallbackResult& target);
+
+// Builds metadata returned in the response for Metadata request.
+base::flat_map<std::string, base::Value> BuildMetadata(
+    const std::string& authorization_server_uri,
+    const std::string& authorization_uri,
+    const std::string& token_uri,
+    const std::string& registration_uri = "",
+    const std::string& revocation_uri = "");
+
+// Simulates Authorization Server. It contains base::test::TaskEnvironment that
+// can be accessed via TaskEnvironment() method.
+class FakeAuthorizationServer {
+ public:
+  FakeAuthorizationServer();
+  ~FakeAuthorizationServer();
+
+  base::test::TaskEnvironment& TaskEnvironment() { return task_environment_; }
+
+  // Returns the URLLoaderFactory that must be used to send HTTP requests and
+  // receive responses.
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() {
+    return fake_server_.GetSafeWeakWrapper();
+  }
+
+  // Processes all pending tasks and get the next request from the fake server.
+  // Checks if it is a GET `url` request without a payload. The method returns
+  // an empty string <=> there is no errors. Otherwise it returns an error
+  // message.
+  std::string ReceiveGET(const std::string& url);
+
+  // Processes all pending tasks and get the next request from the fake server.
+  // Checks if it is a POST `url` request with a JSON payload. The payload is
+  // parsed and saved to `out_params` that is always overwritten. The method
+  // returns an empty string <=> there is no errors. Otherwise it returns
+  // an error message.
+  std::string ReceivePOSTWithJSON(
+      const std::string& url,
+      base::flat_map<std::string, base::Value>& out_params);
+
+  // Processes all pending tasks and get the next request from the fake server.
+  // Checks if it is a POST `url` request with a payload containing URL-encoded
+  // parameters. The payload is parsed and saved to `out_params` that is always
+  // overwritten. The method returns an empty string <=> there is no errors.
+  // Otherwise it returns an error message.
+  std::string ReceivePOSTWithURLParams(
+      const std::string& url,
+      base::flat_map<std::string, std::string>& out_params);
+
+  // Sends a response to the current pending request with the JSON content given
+  // in `params` and processes all pending tasks. The call to this method must
+  // be preceded by a previous call to one of Receive* methods.
+  void ResponseWithJSON(net::HttpStatusCode status,
+                        const base::flat_map<std::string, base::Value>& params);
+
+ private:
+  // Processes all pending tasks and get the next request from the fake server.
+  // Returns an error message if there is no requests or the request doesn't
+  // match `method`, `url` and `content_type. The payload is saved to `content`.
+  // The method returns an empty string <=> there is no errors.
+  std::string GetNextRequest(const std::string& method,
+                             const std::string& url,
+                             const std::string& content_type,
+                             std::string& content);
+
+  // Payloads of the incoming requests are stored here (FIFO).
+  base::queue<std::string> upload_data_;
+
+  // The pending request that is currently being processed.
+  network::TestURLLoaderFactory::PendingRequest* current_request_ = nullptr;
+
+  network::TestURLLoaderFactory fake_server_;
+  base::test::TaskEnvironment task_environment_;
+};
+
+}  // namespace oauth2
+}  // namespace printing
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_PRINTING_OAUTH2_TEST_AUTHORIZATION_SERVER_H_
diff --git a/chrome/browser/ash/printing/oauth2/test_authorization_server_unittest.cc b/chrome/browser/ash/printing/oauth2/test_authorization_server_unittest.cc
new file mode 100644
index 0000000..1b5419e
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/test_authorization_server_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/printing/oauth2/test_authorization_server.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/containers/flat_map.h"
+#include "base/json/json_reader.h"
+#include "base/values.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace ash {
+namespace printing {
+namespace oauth2 {
+namespace {
+
+// Helper function that moves the content of `response_body` to `target`.
+void SavePayload(std::string* target,
+                 std::unique_ptr<std::string> response_body) {
+  CHECK(target);
+  if (response_body) {
+    *target = std::move(*response_body);
+  } else {
+    target->clear();
+  }
+}
+
+// Helper function to retrieve HTTP status from `url_loader`.
+// Returns -1 when it is not possible.
+int GetHttpStatus(network::SimpleURLLoader* url_loader) {
+  if (url_loader && url_loader->ResponseInfo() &&
+      url_loader->ResponseInfo()->headers) {
+    return url_loader->ResponseInfo()->headers->response_code();
+  }
+  return -1;
+}
+
+TEST(PrintingOAuth2TestAuthorizationServerTest, ParseURLParameters) {
+  base::flat_map<std::string, std::string> results;
+  results["trash"] = "something";
+  EXPECT_TRUE(ParseURLParameters("ala=ma&kota", results));
+  EXPECT_EQ(results.size(), 2);
+  EXPECT_TRUE(results.contains("kota"));
+  EXPECT_EQ(results["ala"], "ma");
+  EXPECT_EQ(results["kota"], "");
+}
+
+TEST(PrintingOAuth2TestAuthorizationServerTest, BuildMetadata) {
+  auto data = BuildMetadata("server_uri", "auth", "token", "reg", "rev");
+  EXPECT_EQ(data["issuer"].GetString(), "server_uri");
+  EXPECT_EQ(data["authorization_endpoint"].GetString(), "auth");
+  EXPECT_EQ(data["token_endpoint"].GetString(), "token");
+  EXPECT_EQ(data["registration_endpoint"].GetString(), "reg");
+  EXPECT_EQ(data["revocation_endpoint"].GetString(), "rev");
+  ASSERT_TRUE(data["response_types_supported"].is_list());
+  ASSERT_EQ(data["response_types_supported"].GetList().size(), 1);
+  EXPECT_EQ(data["response_types_supported"].GetList().front().GetString(),
+            "code");
+}
+
+TEST(PrintingOAuth2TestAuthorizationServerTest, ReceiveGETAndResponse) {
+  FakeAuthorizationServer server;
+
+  // Prepare SimpleURLLoader and send the request.
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "GET";
+  resource_request->url = GURL("https://abc/def");
+  auto url_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS);
+  std::string response_payload;
+  url_loader->DownloadToString(server.GetURLLoaderFactory().get(),
+                               base::BindOnce(&SavePayload, &response_payload),
+                               1024);
+
+  // Process and check the request and send the response.
+  ASSERT_EQ(server.ReceiveGET("https://abc/def"), "");
+  base::flat_map<std::string, base::Value> content;
+  server.ResponseWithJSON(net::HttpStatusCode::HTTP_CREATED, content);
+
+  // Check the response.
+  EXPECT_EQ(response_payload, "{}");
+  EXPECT_EQ(GetHttpStatus(url_loader.get()),
+            static_cast<int>(net::HttpStatusCode::HTTP_CREATED));
+}
+
+TEST(PrintingOAuth2TestAuthorizationServerTest,
+     ReceivePOSTWithJSONAndResponse) {
+  FakeAuthorizationServer server;
+
+  // Prepare SimpleURLLoader and send the request.
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "POST";
+  resource_request->url = GURL("https://abc/def");
+  auto url_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS);
+  url_loader->AttachStringForUpload(R"({ "field1": "val1", "field2":"val2"})",
+                                    "application/json");
+  std::string response_payload;
+  url_loader->DownloadToString(server.GetURLLoaderFactory().get(),
+                               base::BindOnce(&SavePayload, &response_payload),
+                               1024);
+
+  // Process and check the request and send the response.
+  base::flat_map<std::string, base::Value> content;
+  ASSERT_EQ(server.ReceivePOSTWithJSON("https://abc/def", content), "");
+  EXPECT_EQ(content.size(), 2);
+  EXPECT_EQ(content["field1"].GetString(), "val1");
+  EXPECT_EQ(content["field2"].GetString(), "val2");
+  content["field3"] = base::Value("val3");
+  server.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, content);
+
+  // Check the response.
+  EXPECT_EQ(GetHttpStatus(url_loader.get()),
+            static_cast<int>(net::HttpStatusCode::HTTP_OK));
+  auto parsed = base::JSONReader::Read(response_payload);
+  ASSERT_TRUE(parsed);
+  ASSERT_TRUE(parsed->is_dict());
+  EXPECT_EQ(*parsed, base::Value(std::move(content)));
+}
+
+TEST(PrintingOAuth2TestAuthorizationServerTest,
+     ReceivePOSTWithURLParamsAndError) {
+  FakeAuthorizationServer server;
+
+  // Prepare SimpleURLLoader and send the request.
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "PUT";  // wrong method
+  resource_request->url = GURL("https://abc/def");
+  auto url_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS);
+  url_loader->AttachStringForUpload("f=v", "application/x-www-form-urlencoded");
+  std::string response_payload;
+  url_loader->DownloadToString(server.GetURLLoaderFactory().get(),
+                               base::BindOnce(&SavePayload, &response_payload),
+                               1024);
+
+  // Process the request and check the error message.
+  base::flat_map<std::string, std::string> content;
+  auto err_msg = server.ReceivePOSTWithURLParams("https://abc/def", content);
+  EXPECT_EQ(err_msg, "Invalid HTTP method: got \"PUT\", expected \"POST\";  ");
+}
+
+}  // namespace
+}  // namespace oauth2
+}  // namespace printing
+}  // namespace ash
diff --git a/chrome/browser/ash/secure_channel/OWNERS b/chrome/browser/ash/secure_channel/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/ash/secure_channel/OWNERS
+++ b/chrome/browser/ash/secure_channel/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc b/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc
index 05037b2..8eedfbe 100644
--- a/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc
+++ b/chrome/browser/ash/secure_channel/nearby_connection_broker_impl.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/secure_channel/public/mojom/secure_channel_types.mojom.h"
 #include "base/bind.h"
@@ -23,7 +24,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/secure_channel/nearby_endpoint_finder.h"
 #include "chrome/browser/ash/secure_channel/util/histogram_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/secure_channel/nearby_connector_impl.cc b/chrome/browser/ash/secure_channel/nearby_connector_impl.cc
index 1cf4499..0d43986 100644
--- a/chrome/browser/ash/secure_channel/nearby_connector_impl.cc
+++ b/chrome/browser/ash/secure_channel/nearby_connector_impl.cc
@@ -4,13 +4,13 @@
 
 #include "chrome/browser/ash/secure_channel/nearby_connector_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/nearby/public/cpp/nearby_process_manager.h"
 #include "ash/services/secure_channel/public/cpp/client/nearby_connector.h"
 #include "base/bind.h"
 #include "chrome/browser/ash/secure_channel/nearby_connection_broker_impl.h"
 #include "chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.h"
 #include "chrome/browser/ash/secure_channel/util/histogram_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc b/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc
index e714575..507ed07 100644
--- a/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc
+++ b/chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.cc
@@ -4,12 +4,12 @@
 
 #include "chrome/browser/ash/secure_channel/nearby_endpoint_finder_impl.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/services/secure_channel/public/mojom/nearby_connector.mojom.h"
 #include "base/base64.h"
 #include "base/memory/ptr_util.h"
 #include "base/rand_util.h"
 #include "chrome/browser/ash/secure_channel/util/histogram_util.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace ash {
 namespace secure_channel {
diff --git a/chrome/browser/ash/tether/tether_service.cc b/chrome/browser/ash/tether/tether_service.cc
index b4a897f..cd142ffc 100644
--- a/chrome/browser/ash/tether/tether_service.cc
+++ b/chrome/browser/ash/tether/tether_service.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/tether/tether_service.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/tether/gms_core_notifications_state_tracker_impl.h"
 #include "ash/components/tether/tether_component.h"
 #include "ash/components/tether/tether_component_impl.h"
@@ -19,7 +20,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/network/tether_notification_presenter.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_connect.h"
 #include "chromeos/network/network_type_pattern.h"
diff --git a/chrome/browser/ash/tether/tether_service_unittest.cc b/chrome/browser/ash/tether/tether_service_unittest.cc
index 1d5cfcaa..814c9ef 100644
--- a/chrome/browser/ash/tether/tether_service_unittest.cc
+++ b/chrome/browser/ash/tether/tether_service_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
+#include "ash/components/multidevice/software_feature.h"
 #include "ash/components/tether/fake_notification_presenter.h"
 #include "ash/components/tether/fake_tether_component.h"
 #include "ash/components/tether/fake_tether_host_fetcher.h"
@@ -39,8 +41,6 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
-#include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/suspend.pb.h"
diff --git a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
index 0bb5d903..23a26cf 100644
--- a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
+++ b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
@@ -7,13 +7,59 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "components/browsing_topics/browsing_topics_service.h"
 #include "content/public/test/browser_test.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
 #include "third_party/blink/public/common/features.h"
 
 namespace browsing_topics {
 
-class BrowsingTopicsDisabledBrowserTest : public InProcessBrowserTest {
+class BrowsingTopicsBrowserTestBase : public InProcessBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+    https_server_.AddDefaultHandlers(GetChromeTestDataDir());
+
+    content::SetupCrossSiteRedirector(&https_server_);
+    ASSERT_TRUE(https_server_.Start());
+
+    content::SetupCrossSiteRedirector(embedded_test_server());
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  std::string InvokeTopicsAPI(const content::ToRenderFrameHost& adapter) {
+    return EvalJs(adapter, R"(
+      if (!(document.browsingTopics instanceof Function)) {
+        'not a function';
+      } else {
+        document.browsingTopics()
+        .then(topics => {
+          let result = "[";
+          for (const topic of topics) {
+            result += JSON.stringify(topic, Object.keys(topic).sort()) + ";"
+          }
+          result += "]";
+          return result;
+        })
+        .catch(error => error.message);
+      }
+    )")
+        .ExtractString();
+  }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+ protected:
+  net::EmbeddedTestServer https_server_{
+      net::test_server::EmbeddedTestServer::TYPE_HTTPS};
+};
+
+class BrowsingTopicsDisabledBrowserTest : public BrowsingTopicsBrowserTestBase {
  public:
   BrowsingTopicsDisabledBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
@@ -31,7 +77,16 @@
       BrowsingTopicsServiceFactory::GetForProfile(browser()->profile()));
 }
 
-class BrowsingTopicsBrowserTest : public InProcessBrowserTest {
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledBrowserTest, NoTopicsAPI) {
+  GURL main_frame_url =
+      https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  EXPECT_EQ("not a function", InvokeTopicsAPI(web_contents()));
+}
+
+class BrowsingTopicsBrowserTest : public BrowsingTopicsBrowserTestBase {
  public:
   BrowsingTopicsBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
@@ -72,4 +127,145 @@
   EXPECT_TRUE(browsing_topics_service()->GetTopTopicsForDisplay().empty());
 }
 
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, EmptyPage_TopicsAPI) {
+  GURL main_frame_url =
+      https_server_.GetURL("a.test", "/browsing_topics/empty_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  EXPECT_EQ("[]", InvokeTopicsAPI(web_contents()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    BrowsingTopicsBrowserTest,
+    EmptyPage_PermissionsPolicyBrowsingTopicsNone_TopicsAPI) {
+  GURL main_frame_url = https_server_.GetURL(
+      "a.test", "/browsing_topics/empty_page_browsing_topics_none.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  EXPECT_EQ(
+      "The \"browsing-topics\" Permissions Policy denied the use of "
+      "document.browsingTopics().",
+      InvokeTopicsAPI(web_contents()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    BrowsingTopicsBrowserTest,
+    EmptyPage_PermissionsPolicyInterestCohortNone_TopicsAPI) {
+  GURL main_frame_url = https_server_.GetURL(
+      "a.test", "/browsing_topics/empty_page_interest_cohort_none.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  EXPECT_EQ(
+      "The \"interest-cohort\" Permissions Policy denied the use of "
+      "document.browsingTopics().",
+      InvokeTopicsAPI(web_contents()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    BrowsingTopicsBrowserTest,
+    OneIframePage_SubframePermissionsPolicyBrowsingTopicsNone_TopicsAPI) {
+  GURL main_frame_url =
+      https_server_.GetURL("a.test", "/browsing_topics/one_iframe_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  GURL subframe_url = https_server_.GetURL(
+      "a.test", "/browsing_topics/empty_page_browsing_topics_none.html");
+
+  ASSERT_TRUE(content::NavigateIframeToURL(web_contents(),
+                                           /*iframe_id=*/"frame",
+                                           subframe_url));
+
+  EXPECT_EQ("[]", InvokeTopicsAPI(web_contents()));
+
+  EXPECT_EQ(
+      "The \"browsing-topics\" Permissions Policy denied the use of "
+      "document.browsingTopics().",
+      InvokeTopicsAPI(
+          content::ChildFrameAt(web_contents()->GetMainFrame(), 0)));
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       PermissionsPolicyAllowCertainOrigin_TopicsAPI) {
+  base::StringPairs port_replacement;
+  port_replacement.push_back(
+      std::make_pair("{{PORT}}", base::NumberToString(https_server_.port())));
+
+  GURL main_frame_url = https_server_.GetURL(
+      "a.test", net::test_server::GetFilePathWithReplacements(
+                    "/browsing_topics/"
+                    "one_iframe_page_browsing_topics_allow_certain_origin.html",
+                    port_replacement));
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  EXPECT_EQ("[]", InvokeTopicsAPI(web_contents()));
+
+  GURL subframe_url =
+      https_server_.GetURL("c.test", "/browsing_topics/empty_page.html");
+
+  ASSERT_TRUE(content::NavigateIframeToURL(web_contents(),
+                                           /*iframe_id=*/"frame",
+                                           subframe_url));
+  EXPECT_EQ("[]", InvokeTopicsAPI(content::ChildFrameAt(
+                      web_contents()->GetMainFrame(), 0)));
+
+  subframe_url =
+      https_server_.GetURL("b.test", "/browsing_topics/empty_page.html");
+
+  ASSERT_TRUE(content::NavigateIframeToURL(web_contents(),
+                                           /*iframe_id=*/"frame",
+                                           subframe_url));
+
+  EXPECT_EQ(
+      "The \"browsing-topics\" Permissions Policy denied the use of "
+      "document.browsingTopics().",
+      InvokeTopicsAPI(
+          content::ChildFrameAt(web_contents()->GetMainFrame(), 0)));
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       TopicsAPINotAllowedInInsecureContext) {
+  GURL main_frame_url = embedded_test_server()->GetURL(
+      "a.test", "/browsing_topics/one_iframe_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  // Navigate the iframe to a https site.
+  GURL subframe_url = https_server_.GetURL("b.test", "/empty_page.html");
+  content::NavigateIframeToURL(web_contents(),
+                               /*iframe_id=*/"frame", subframe_url);
+
+  // Both the main frame and the subframe are insecure context because the main
+  // frame is loaded over HTTP. Expect that the API isn't available in either
+  // frame.
+  EXPECT_EQ("not a function", InvokeTopicsAPI(web_contents()));
+  EXPECT_EQ("not a function", InvokeTopicsAPI(content::ChildFrameAt(
+                                  web_contents()->GetMainFrame(), 0)));
+}
+
+IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
+                       TopicsAPINotAllowedInDetachedDocument) {
+  GURL main_frame_url =
+      https_server_.GetURL("a.test", "/browsing_topics/one_iframe_page.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
+
+  EXPECT_EQ(
+      "Failed to execute 'browsingTopics' on 'Document': A browsing "
+      "context is required when calling document.browsingTopics().",
+      EvalJs(web_contents(), R"(
+      const iframe = document.getElementById('frame');
+      const childDocument = iframe.contentWindow.document;
+      iframe.remove();
+
+      childDocument.browsingTopics()
+        .then(topics => "success")
+        .catch(error => error.message);
+    )"));
+}
+
 }  // namespace browsing_topics
diff --git a/chrome/browser/cart/cart_discount_metric_collector.cc b/chrome/browser/cart/cart_discount_metric_collector.cc
index 60d178d..650bf88 100644
--- a/chrome/browser/cart/cart_discount_metric_collector.cc
+++ b/chrome/browser/cart/cart_discount_metric_collector.cc
@@ -48,58 +48,58 @@
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusAcceptedIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn", variation);
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusRejectedIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn", variation);
 }
 
 void CartDiscountMetricCollector::
     RecordDiscountConsentStatusNoShowAfterDecidedIn(
-        ntp_features::DiscountConsentNtpVariation variation) {
+        commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized",
       variation);
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusDismissedIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn", variation);
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusShowInterestIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn",
       variation);
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusNeverShowIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn", variation);
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusNoShowIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn", variation);
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusIgnoredIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn", variation);
 }
 
 void CartDiscountMetricCollector::RecordDiscountConsentStatusShownIn(
-    ntp_features::DiscountConsentNtpVariation variation) {
+    commerce::DiscountConsentNtpVariation variation) {
   base::UmaHistogramEnumeration(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn", variation);
 }
diff --git a/chrome/browser/cart/cart_discount_metric_collector.h b/chrome/browser/cart/cart_discount_metric_collector.h
index 3c5b43e..7c9e467 100644
--- a/chrome/browser/cart/cart_discount_metric_collector.h
+++ b/chrome/browser/cart/cart_discount_metric_collector.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_CART_CART_DISCOUNT_METRIC_COLLECTOR_H_
 #define CHROME_BROWSER_CART_CART_DISCOUNT_METRIC_COLLECTOR_H_
 
-#include "components/search/ntp_features.h"
+#include "components/commerce/core/commerce_feature_list.h"
 
 // This is used to collect metric related to the Cart Discount.
 // These values are persisted to logs. Entries should not be renumbered and
@@ -47,23 +47,23 @@
   // The following get called when cart module shows to record histogram for
   // detail discount consent status.
   static void RecordDiscountConsentStatusAcceptedIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusRejectedIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusNoShowAfterDecidedIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusDismissedIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusShowInterestIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusNeverShowIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusNoShowIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusIgnoredIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
   static void RecordDiscountConsentStatusShownIn(
-      ntp_features::DiscountConsentNtpVariation variation);
+      commerce::DiscountConsentNtpVariation variation);
 };
 
 #endif  // CHROME_BROWSER_CART_CART_DISCOUNT_METRIC_COLLECTOR_H_
diff --git a/chrome/browser/cart/cart_features.cc b/chrome/browser/cart/cart_features.cc
deleted file mode 100644
index fd40c95..0000000
--- a/chrome/browser/cart/cart_features.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/cart/cart_features.h"
-
-#include "base/no_destructor.h"
-#include "components/commerce/core/commerce_feature_list.h"
-#include "third_party/re2/src/re2/re2.h"
-
-namespace cart_features {
-
-namespace {
-
-constexpr base::FeatureParam<std::string> kPartnerMerchantPattern{
-    &ntp_features::kNtpChromeCartModule, "partner-merchant-pattern",
-    // This regex does not match anything.
-    "\\b\\B"};
-
-const re2::RE2& GetPartnerMerchantPattern() {
-  re2::RE2::Options options;
-  options.set_case_sensitive(false);
-  static base::NoDestructor<re2::RE2> instance(kPartnerMerchantPattern.Get(),
-                                               options);
-  return *instance;
-}
-
-}  // namespace
-
-bool IsRuleDiscountPartnerMerchant(const GURL& url) {
-  const std::string& url_string = url.spec();
-  return RE2::PartialMatch(
-      re2::StringPiece(url_string.data(), url_string.size()),
-      GetPartnerMerchantPattern());
-}
-
-bool IsPartnerMerchant(const GURL& url) {
-  return commerce::IsCouponDiscountPartnerMerchant(url) ||
-         IsRuleDiscountPartnerMerchant(url);
-}
-
-bool IsFakeDataEnabled() {
-  return base::GetFieldTrialParamValueByFeature(
-             ntp_features::kNtpChromeCartModule,
-             ntp_features::kNtpChromeCartModuleDataParam) == "fake";
-}
-
-bool IsCartDiscountFeatureEnabled() {
-  return base::GetFieldTrialParamValueByFeature(
-             ntp_features::kNtpChromeCartModule,
-             ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam) ==
-         "true";
-}
-
-}  // namespace cart_features
diff --git a/chrome/browser/cart/cart_features.h b/chrome/browser/cart/cart_features.h
deleted file mode 100644
index e54d57ba..0000000
--- a/chrome/browser/cart/cart_features.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CART_CART_FEATURES_H_
-#define CHROME_BROWSER_CART_CART_FEATURES_H_
-
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "components/search/ntp_features.h"
-#include "url/gurl.h"
-
-namespace cart_features {
-// Default value is 6 hours.
-constexpr base::FeatureParam<base::TimeDelta> kDiscountFetchDelayParam(
-    &ntp_features::kNtpChromeCartModule,
-    "discount-fetch-delay",
-    base::Hours(6));
-
-// Check if a URL belongs to a partner merchant of rule discount.
-bool IsRuleDiscountPartnerMerchant(const GURL& url);
-
-// Check if a URL belongs to a partner merchant of any discount types.
-// TODO(crbug.com/1253633): Move this method to commerce_feature_list after
-// modularizing CartService-related components.
-bool IsPartnerMerchant(const GURL& url);
-
-// Check if the variation with fake data is enabled.
-bool IsFakeDataEnabled();
-
-// Check if cart discount feature is enabled.
-bool IsCartDiscountFeatureEnabled();
-}  // namespace cart_features
-
-#endif  // CHROME_BROWSER_CART_CART_FEATURES_H_
diff --git a/chrome/browser/cart/cart_handler.cc b/chrome/browser/cart/cart_handler.cc
index b67cbd3..12b4c047 100644
--- a/chrome/browser/cart/cart_handler.cc
+++ b/chrome/browser/cart/cart_handler.cc
@@ -7,9 +7,9 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/cart/cart_db_content.pb.h"
-#include "chrome/browser/cart/cart_features.h"
 #include "chrome/browser/cart/cart_service.h"
 #include "chrome/browser/cart/cart_service_factory.h"
+#include "components/commerce/core/commerce_feature_list.h"
 #include "components/search/ntp_features.h"
 
 CartHandler::CartHandler(
@@ -71,7 +71,7 @@
     auto cart = chrome_cart::mojom::MerchantCart::New();
     cart->merchant = std::move(proto_pair.second.merchant());
 
-    if (cart_features::IsRuleDiscountPartnerMerchant(
+    if (commerce::IsRuleDiscountPartnerMerchant(
             GURL(proto_pair.second.merchant_cart_url()))) {
       cart->cart_url = CartService::AppendUTM(
           GURL(std::move(proto_pair.second.merchant_cart_url())),
diff --git a/chrome/browser/cart/cart_service.cc b/chrome/browser/cart/cart_service.cc
index 261c46b..1545467 100644
--- a/chrome/browser/cart/cart_service.cc
+++ b/chrome/browser/cart/cart_service.cc
@@ -11,7 +11,6 @@
 #include "base/task/thread_pool.h"
 #include "chrome/browser/cart/cart_db_content.pb.h"
 #include "chrome/browser/cart/cart_discount_metric_collector.h"
-#include "chrome/browser/cart/cart_features.h"
 #include "chrome/browser/commerce/coupons/coupon_service_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
@@ -63,9 +62,8 @@
 
 std::string GetKeyForURL(const GURL& url) {
   std::string domain = eTLDPlusOne(url);
-  return cart_features::IsFakeDataEnabled()
-             ? std::string(kFakeDataPrefix) + domain
-             : domain;
+  return commerce::IsFakeDataEnabled() ? std::string(kFakeDataPrefix) + domain
+                                       : domain;
 }
 
 bool CompareTimeStampForProtoPair(const CartDB::KeyAndValue pair1,
@@ -129,7 +127,7 @@
   history_service_observation_.Observe(HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS));
   coupon_service_->MaybeFeatureStatusChanged(IsCartAndDiscountEnabled());
-  if (cart_features::IsFakeDataEnabled()) {
+  if (commerce::IsFakeDataEnabled()) {
     AddCartsWithFakeData();
   } else {
     // In case last deconstruction is interrupted and fake data is not deleted.
@@ -176,7 +174,7 @@
 
 GURL CartService::AppendUTM(const GURL& base_url, bool is_discount_enabled) {
   DCHECK(base_url.is_valid() &&
-         cart_features::IsRuleDiscountPartnerMerchant(base_url));
+         commerce::IsRuleDiscountPartnerMerchant(base_url));
 
   if (kRbdUtmParam.Get()) {
     return net::AppendOrReplaceQueryParameter(
@@ -280,22 +278,22 @@
 }
 
 void CartService::AcknowledgeDiscountConsent(bool should_enable) {
-  if (cart_features::IsFakeDataEnabled()) {
+  if (commerce::IsFakeDataEnabled()) {
     return;
   }
   profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountAcknowledged, true);
   profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, should_enable);
   profile_->GetPrefs()->SetInteger(
       prefs::kDiscountConsentDecisionMadeIn,
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
+      commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
 
-  if (should_enable && cart_features::IsCartDiscountFeatureEnabled()) {
+  if (should_enable && commerce::IsCartDiscountFeatureEnabled()) {
     StartGettingDiscount();
   }
 }
 
 void CartService::DismissedDiscountConsent() {
-  if (cart_features::IsFakeDataEnabled()) {
+  if (commerce::IsFakeDataEnabled()) {
     return;
   }
   profile_->GetPrefs()->SetTime(prefs::kDiscountConsentLastDimissedTime,
@@ -306,23 +304,23 @@
                                    past_dimissed_count + 1);
   profile_->GetPrefs()->SetInteger(
       prefs::kDiscountConsentDismissedIn,
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
+      commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
 }
 
 void CartService::InterestedInDiscountConsent() {
-  if (cart_features::IsFakeDataEnabled()) {
+  if (commerce::IsFakeDataEnabled()) {
     return;
   }
   profile_->GetPrefs()->SetBoolean(prefs::kDiscountConsentShowInterest, true);
   profile_->GetPrefs()->SetInteger(
       prefs::kDiscountConsentShowInterestIn,
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
+      commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
 }
 
 void CartService::ShouldShowDiscountConsent(
     base::OnceCallback<void(bool)> callback) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  if (cart_features::IsFakeDataEnabled()) {
+  if (commerce::IsFakeDataEnabled()) {
     content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
         ->PostTask(FROM_HERE, base::BindOnce(
                                   [](base::OnceCallback<void(bool)> callback) {
@@ -343,8 +341,8 @@
             ? CartDiscountMetricCollector::DiscountConsentStatus::ACCEPTED
             : CartDiscountMetricCollector::DiscountConsentStatus::DECLINED);
 
-    ntp_features::DiscountConsentNtpVariation decision_made_in_variation =
-        static_cast<ntp_features::DiscountConsentNtpVariation>(
+    commerce::DiscountConsentNtpVariation decision_made_in_variation =
+        static_cast<commerce::DiscountConsentNtpVariation>(
             profile_->GetPrefs()->GetInteger(
                 prefs::kDiscountConsentDecisionMadeIn));
     if (profile_->GetPrefs()->GetBoolean(prefs::kCartDiscountEnabled)) {
@@ -361,26 +359,25 @@
     if (profile_->GetPrefs()->GetInteger(
             prefs::kDiscountConsentPastDismissedCount) > 0) {
       CartDiscountMetricCollector::RecordDiscountConsentStatusDismissedIn(
-          static_cast<ntp_features::DiscountConsentNtpVariation>(
+          static_cast<commerce::DiscountConsentNtpVariation>(
               profile_->GetPrefs()->GetInteger(
                   prefs::kDiscountConsentDismissedIn)));
     }
 
     if (profile_->GetPrefs()->GetBoolean(prefs::kDiscountConsentShowInterest)) {
       CartDiscountMetricCollector::RecordDiscountConsentStatusShowInterestIn(
-          static_cast<ntp_features::DiscountConsentNtpVariation>(
+          static_cast<commerce::DiscountConsentNtpVariation>(
               profile_->GetPrefs()->GetInteger(
                   prefs::kDiscountConsentShowInterestIn)));
     }
 
-    ntp_features::DiscountConsentNtpVariation last_shown_in_variation =
-        static_cast<ntp_features::DiscountConsentNtpVariation>(
+    commerce::DiscountConsentNtpVariation last_shown_in_variation =
+        static_cast<commerce::DiscountConsentNtpVariation>(
             profile_->GetPrefs()->GetInteger(
                 prefs::kDiscountConsentLastShownInVariation));
-    ntp_features::DiscountConsentNtpVariation current_variation =
-        static_cast<ntp_features::DiscountConsentNtpVariation>(
-            ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation
-                .Get());
+    commerce::DiscountConsentNtpVariation current_variation =
+        static_cast<commerce::DiscountConsentNtpVariation>(
+            commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
     if (!should_show_consent) {
       CartDiscountMetricCollector::RecordDiscountConsentStatus(
           profile_->GetPrefs()->GetBoolean(prefs::kCartDiscountConsentShown)
@@ -418,7 +415,7 @@
     bool success,
     std::vector<CartDB::KeyAndValue> proto_pairs) {
   if (proto_pairs.size() == 0 || ShouldShowWelcomeSurface() ||
-      !cart_features::IsCartDiscountFeatureEnabled()) {
+      !commerce::IsCartDiscountFeatureEnabled()) {
     std::move(callback).Run(false);
     return;
   }
@@ -428,7 +425,7 @@
     // Only show discount consent when there is abandoned cart(s) from partner
     // merchants.
     for (auto proto_pair : proto_pairs) {
-      should_show |= cart_features::IsPartnerMerchant(
+      should_show |= commerce::IsPartnerMerchant(
           GURL(proto_pair.second.merchant_cart_url()));
     }
 
@@ -436,7 +433,7 @@
       base::Time last_dismissed_time = profile_->GetPrefs()->GetTime(
           prefs::kDiscountConsentLastDimissedTime);
       base::TimeDelta reshow_time_delta =
-          ntp_features::kNtpChromeCartModuleDiscountConsentReshowTime.Get() -
+          commerce::kNtpChromeCartModuleDiscountConsentReshowTime.Get() -
           (base::Time::Now() - last_dismissed_time);
       int last_dismissed_count = profile_->GetPrefs()->GetInteger(
           prefs::kDiscountConsentPastDismissedCount);
@@ -444,7 +441,7 @@
           (last_dismissed_time == base::Time() ||
            reshow_time_delta.is_negative()) &&
           last_dismissed_count <
-              ntp_features::kNtpChromeCartModuleDiscountConsentMaxDismissalCount
+              commerce::kNtpChromeCartModuleDiscountConsentMaxDismissalCount
                   .Get();
     }
   }
@@ -455,14 +452,14 @@
 }
 
 bool CartService::IsCartDiscountEnabled() {
-  if (!cart_features::IsCartDiscountFeatureEnabled()) {
+  if (!commerce::IsCartDiscountFeatureEnabled()) {
     return false;
   }
   return profile_->GetPrefs()->GetBoolean(prefs::kCartDiscountEnabled);
 }
 
 void CartService::SetCartDiscountEnabled(bool enabled) {
-  DCHECK(cart_features::IsCartDiscountFeatureEnabled());
+  DCHECK(commerce::IsCartDiscountFeatureEnabled());
   profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, enabled);
 
   if (enabled) {
@@ -478,11 +475,11 @@
     const GURL& cart_url,
     base::OnceCallback<void(const ::GURL&)> callback) {
   auto url = cart_url;
-  if (cart_features::IsRuleDiscountPartnerMerchant(cart_url)) {
+  if (commerce::IsRuleDiscountPartnerMerchant(cart_url)) {
     url = AppendUTM(cart_url, IsCartDiscountEnabled());
   }
 
-  if (!cart_features::IsRuleDiscountPartnerMerchant(cart_url) ||
+  if (!commerce::IsRuleDiscountPartnerMerchant(cart_url) ||
       !IsCartDiscountEnabled()) {
     std::move(callback).Run(url);
     CartDiscountMetricCollector::RecordClickedOnDiscount(false);
@@ -542,8 +539,7 @@
 void CartService::PrepareForNavigation(const GURL& cart_url,
                                        bool is_navigating) {
   metrics_tracker_->PrepareToRecordUKM(cart_url);
-  if (is_navigating ||
-      !cart_features::IsRuleDiscountPartnerMerchant(cart_url) ||
+  if (is_navigating || !commerce::IsRuleDiscountPartnerMerchant(cart_url) ||
       !IsCartDiscountEnabled()) {
     return;
   }
@@ -574,7 +570,7 @@
 
 void CartService::Shutdown() {
   history_service_observation_.Reset();
-  if (cart_features::IsFakeDataEnabled()) {
+  if (commerce::IsFakeDataEnabled()) {
     DeleteCartsWithFakeData();
   }
   // Delete content of all carts that are removed.
@@ -796,7 +792,7 @@
 void CartService::OnLoadCarts(CartDB::LoadCallback callback,
                               bool success,
                               std::vector<CartDB::KeyAndValue> proto_pairs) {
-  if (cart_features::IsFakeDataEnabled()) {
+  if (commerce::IsFakeDataEnabled()) {
     std::sort(proto_pairs.begin(), proto_pairs.end(),
               CompareTimeStampForProtoPair);
     std::move(callback).Run(success, std::move(proto_pairs));
@@ -1034,7 +1030,7 @@
 
   base::Time last_fetched_time =
       profile_->GetPrefs()->GetTime(prefs::kCartDiscountLastFetchedTime);
-  base::TimeDelta fetch_delay = cart_features::kDiscountFetchDelayParam.Get() -
+  base::TimeDelta fetch_delay = commerce::kDiscountFetchDelayParam.Get() -
                                 (base::Time::Now() - last_fetched_time);
   if (last_fetched_time == base::Time() || fetch_delay.is_negative() ||
       kBypassDisocuntFetchingThreshold.Get()) {
diff --git a/chrome/browser/cart/cart_service_unittest.cc b/chrome/browser/cart/cart_service_unittest.cc
index dc484ca..afcdd745 100644
--- a/chrome/browser/cart/cart_service_unittest.cc
+++ b/chrome/browser/cart/cart_service_unittest.cc
@@ -2282,23 +2282,23 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn",
-      ntp_features::DiscountConsentNtpVariation::kDefault, 0);
+      commerce::DiscountConsentNtpVariation::kDefault, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn",
-      ntp_features::DiscountConsentNtpVariation::kDefault, 1);
+      commerce::DiscountConsentNtpVariation::kDefault, 1);
 
   // Simulate consent has been accepted in the Inline variation.
   profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentDecisionMadeIn, 2);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.AcceptedIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2311,23 +2311,23 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn",
-      ntp_features::DiscountConsentNtpVariation::kDefault, 0);
+      commerce::DiscountConsentNtpVariation::kDefault, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn",
-      ntp_features::DiscountConsentNtpVariation::kDefault, 1);
+      commerce::DiscountConsentNtpVariation::kDefault, 1);
 
   // Simulate consent has been rejected in the Inline variation.
   profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentDecisionMadeIn, 2);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.RejectedIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2340,12 +2340,12 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized",
-      ntp_features::DiscountConsentNtpVariation::kDefault, 0);
+      commerce::DiscountConsentNtpVariation::kDefault, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized",
-      ntp_features::DiscountConsentNtpVariation::kDefault, 1);
+      commerce::DiscountConsentNtpVariation::kDefault, 1);
 
   // Simulate consent has been accepted in the Default variation.
   profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, true);
@@ -2353,7 +2353,7 @@
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized",
-      ntp_features::DiscountConsentNtpVariation::kDefault, 2);
+      commerce::DiscountConsentNtpVariation::kDefault, 2);
 
   // Simulate consent has been accepted in the Inline variation.
   profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, true);
@@ -2361,12 +2361,12 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowHasFinalized",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2380,24 +2380,24 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 
   // Simulate consent has been dismissed in the Dialog variation.
   profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentDismissedIn, 3);
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn",
-      ntp_features::DiscountConsentNtpVariation::kDialog, 0);
+      commerce::DiscountConsentNtpVariation::kDialog, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.DismissedIn",
-      ntp_features::DiscountConsentNtpVariation::kDialog, 1);
+      commerce::DiscountConsentNtpVariation::kDialog, 1);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2410,23 +2410,23 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 
   // Simulate 'continue' button is clicked in the Dialog variation.
   profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentShowInterestIn, 3);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn",
-      ntp_features::DiscountConsentNtpVariation::kDialog, 0);
+      commerce::DiscountConsentNtpVariation::kDialog, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.InterestedButNoActionIn",
-      ntp_features::DiscountConsentNtpVariation::kDialog, 1);
+      commerce::DiscountConsentNtpVariation::kDialog, 1);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2440,12 +2440,12 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 
   // Simulate consent is shown in the Inline variation before.
   profile_->GetPrefs()->SetInteger(prefs::kDiscountConsentLastShownInVariation,
@@ -2454,7 +2454,7 @@
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NeverShownIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2469,17 +2469,17 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.NoShowIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 2);
+      commerce::DiscountConsentNtpVariation::kInline, 2);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2493,17 +2493,17 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.IgnoredIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 2);
+      commerce::DiscountConsentNtpVariation::kInline, 2);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test,
@@ -2514,17 +2514,17 @@
 
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 0);
+      commerce::DiscountConsentNtpVariation::kInline, 0);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 1);
+      commerce::DiscountConsentNtpVariation::kInline, 1);
 
   RecordDiscountConsentStatusAtLoad(should_show);
   histogram_tester_.ExpectBucketCount(
       "NewTabPage.Carts.DiscountConsentStatusAtLoad.ShownIn",
-      ntp_features::DiscountConsentNtpVariation::kInline, 2);
+      commerce::DiscountConsentNtpVariation::kInline, 2);
 }
 
 TEST_F(CartServiceDiscountConsentV2Test, TestLastShownInVariationUpdated) {
diff --git a/chrome/browser/cart/commerce_hint_service.cc b/chrome/browser/cart/commerce_hint_service.cc
index aa0d368..49266fd9 100644
--- a/chrome/browser/cart/commerce_hint_service.cc
+++ b/chrome/browser/cart/commerce_hint_service.cc
@@ -10,12 +10,12 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "chrome/browser/cart/cart_db_content.pb.h"
-#include "chrome/browser/cart/cart_features.h"
 #include "chrome/browser/cart/cart_service.h"
 #include "chrome/browser/cart/cart_service_factory.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/commerce/core/commerce_feature_list.h"
 #include "components/search/ntp_features.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "content/public/browser/document_service.h"
@@ -196,7 +196,7 @@
   // When rule-based discount is enabled, do not accept cart page URLs from
   // partner merchants as there could be things like discount tokens in them.
   if (service_->IsCartDiscountEnabled() &&
-      cart_features::IsRuleDiscountPartnerMerchant(navigation_url) &&
+      commerce::IsRuleDiscountPartnerMerchant(navigation_url) &&
       product_id.empty()) {
     validated_cart = absl::nullopt;
   }
@@ -225,7 +225,7 @@
   // When rule-based discount is enabled, do not accept cart page URLs from
   // partner merchants as there could be things like discount tokens in them.
   if (service_->IsCartDiscountEnabled() &&
-      cart_features::IsRuleDiscountPartnerMerchant(cart_url)) {
+      commerce::IsRuleDiscountPartnerMerchant(cart_url)) {
     validated_cart = absl::nullopt;
   }
   cart_db::ChromeCartContentProto proto;
diff --git a/chrome/browser/cart/fetch_discount_worker.cc b/chrome/browser/cart/fetch_discount_worker.cc
index e5f6bc89..e8d239e5 100644
--- a/chrome/browser/cart/fetch_discount_worker.cc
+++ b/chrome/browser/cart/fetch_discount_worker.cc
@@ -9,7 +9,6 @@
 #include "base/task/thread_pool.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/cart/cart_discount_fetcher.h"
-#include "chrome/browser/cart/cart_features.h"
 #include "chrome/browser/commerce/coupons/coupon_db_content.pb.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/search/ntp_features.h"
@@ -149,14 +148,13 @@
   // post another delayed fetch.
   bool has_partner_merchant = false;
   for (auto pair : proto_pairs) {
-    if (cart_features::IsPartnerMerchant(
-            GURL(pair.second.merchant_cart_url()))) {
+    if (commerce::IsPartnerMerchant(GURL(pair.second.merchant_cart_url()))) {
       has_partner_merchant = true;
       break;
     }
   }
   if (!has_partner_merchant) {
-    Start(cart_features::kDiscountFetchDelayParam.Get());
+    Start(commerce::kDiscountFetchDelayParam.Get());
     return;
   }
   backend_task_runner_->PostTask(
@@ -306,6 +304,6 @@
           ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam,
           false)) {
     // Continue to work.
-    Start(cart_features::kDiscountFetchDelayParam.Get());
+    Start(commerce::kDiscountFetchDelayParam.Get());
   }
 }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 019fd680..372f1e1 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4959,6 +4959,8 @@
     // are allowed to use chrome://resources/ and chrome://theme/ URLs.
     allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost);
     allowed_webui_hosts.emplace_back(chrome::kChromeUIThemeHost);
+    // For testing purposes chrome://webui-test/ is also allowed.
+    allowed_webui_hosts.emplace_back(chrome::kChromeUIWebUITestHost);
   }
   if (extension->is_extension() || extension->is_legacy_packaged_app() ||
       (extension->is_platform_app() &&
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index d92fc91..df4b620 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -100,6 +100,9 @@
     "//ash/components/geolocation",
     "//ash/components/login/auth",
     "//ash/components/login/session",
+    "//ash/components/multidevice",
+    "//ash/components/multidevice:stub_multidevice_util",
+    "//ash/components/multidevice/logging",
     "//ash/components/peripheral_notification",
     "//ash/components/phonehub",
     "//ash/components/phonehub/proto",
@@ -234,9 +237,6 @@
     "//chromeos/components/hibernate:buildflags",
     "//chromeos/components/local_search_service/public/cpp:cpp",
     "//chromeos/components/mojo_bootstrap",
-    "//chromeos/components/multidevice",
-    "//chromeos/components/multidevice:stub_multidevice_util",
-    "//chromeos/components/multidevice/logging",
     "//chromeos/components/onc",
     "//chromeos/components/quick_answers",
     "//chromeos/components/quick_answers/public/cpp:cpp",
@@ -904,6 +904,8 @@
     "../ash/arc/input_overlay/ui/action_edit_menu.h",
     "../ash/arc/input_overlay/ui/action_label.cc",
     "../ash/arc/input_overlay/ui/action_label.h",
+    "../ash/arc/input_overlay/ui/action_tag.cc",
+    "../ash/arc/input_overlay/ui/action_tag.h",
     "../ash/arc/input_overlay/ui/action_view.cc",
     "../ash/arc/input_overlay/ui/action_view.h",
     "../ash/arc/input_overlay/ui/input_mapping_view.cc",
@@ -1677,6 +1679,7 @@
     "../ash/guest_os/guest_os_stability_monitor.cc",
     "../ash/guest_os/guest_os_stability_monitor.h",
     "../ash/guest_os/infra/cached_callback.h",
+    "../ash/guest_os/public/guest_os_mount_provider.cc",
     "../ash/guest_os/public/guest_os_mount_provider.h",
     "../ash/guest_os/public/guest_os_mount_provider_registry.cc",
     "../ash/guest_os/public/guest_os_mount_provider_registry.h",
@@ -4309,6 +4312,7 @@
     "../ash/guest_os/guest_os_stability_monitor_unittest.cc",
     "../ash/guest_os/infra/cached_callback_unittest.cc",
     "../ash/guest_os/public/guest_os_mount_provider_registry_unittest.cc",
+    "../ash/guest_os/public/guest_os_mount_provider_unittest.cc",
     "../ash/guest_os/vm_sk_forwarding_native_message_host_unittest.cc",
     "../ash/hats/hats_dialog_unittest.cc",
     "../ash/hats/hats_finch_helper_unittest.cc",
@@ -4640,6 +4644,9 @@
     "../ash/printing/history/test_print_job_history_service_observer.cc",
     "../ash/printing/history/test_print_job_history_service_observer.h",
     "../ash/printing/oauth2/http_exchange_unittest.cc",
+    "../ash/printing/oauth2/test_authorization_server.cc",
+    "../ash/printing/oauth2/test_authorization_server.h",
+    "../ash/printing/oauth2/test_authorization_server_unittest.cc",
     "../ash/printing/ppd_resolution_state_unittest.cc",
     "../ash/printing/ppd_resolution_tracker_unittest.cc",
     "../ash/printing/print_management/print_job_info_mojom_conversions_unittest.cc",
@@ -4864,6 +4871,7 @@
     "//ash/components/audio",
     "//ash/components/disks:test_support",
     "//ash/components/login/auth",
+    "//ash/components/multidevice:test_support",
     "//ash/components/phonehub:test_support",
     "//ash/components/proximity_auth",
     "//ash/components/proximity_auth:test_support",
@@ -4898,7 +4906,6 @@
     "//chrome/test:test_support_ui",
     "//chrome/test:test_support_unit",
     "//chromeos",
-    "//chromeos/components/multidevice:test_support",
     "//chromeos/components/sensors:test_support",
     "//chromeos/dbus:test_support",
     "//chromeos/dbus/anomaly_detector",
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
index c783891..95b93f8 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -504,6 +504,9 @@
       volume_metadata->volume_type =
           file_manager_private::VOLUME_TYPE_SYSTEM_INTERNAL;
       break;
+    case VOLUME_TYPE_GUEST_OS:
+      volume_metadata->volume_type = file_manager_private::VOLUME_TYPE_GUEST_OS;
+      break;
     case NUM_VOLUME_TYPE:
       NOTREACHED();
       break;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
index 073f068..d7ea3de 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -84,8 +84,7 @@
     case DlpRulesManager::Restriction::kPrinting:
       return absl::make_optional(dlp::kPrintingWarnProceededUMA);
     case DlpRulesManager::Restriction::kScreenshot:
-      // TODO(crbug.com/1304750): Add UMA stats for warning for screenshots.
-      return absl::nullopt;
+      return absl::make_optional(dlp::kScreenshotWarnProceededUMA);
     case DlpRulesManager::Restriction::kUnknownRestriction:
     case DlpRulesManager::Restriction::kClipboard:
     case DlpRulesManager::Restriction::kPrivacyScreen:
@@ -173,6 +172,7 @@
     ReportWarningEvent(restriction_info.url,
                        DlpRulesManager::Restriction::kScreenshot);
   DlpBooleanHistogram(dlp::kScreenshotBlockedUMA, IsBlocked(restriction_info));
+  DlpBooleanHistogram(dlp::kScreenshotWarnedUMA, IsWarn(restriction_info));
   // TODO(crbug.com/1252736): Properly handle WARN for screenshots API.
   return IsBlocked(restriction_info) || IsWarn(restriction_info);
 }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h b/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h
index a844ada..b14bcf0 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h
@@ -15,6 +15,7 @@
 
 // Constants with UMA histogram name suffixes.
 constexpr char kCaptureModeInitBlockedUMA[] = "CaptureModeInitBlocked";
+constexpr char kCaptureModeInitWarnedUMA[] = "CaptureModeInitWarned";
 constexpr char kClipboardReadBlockedUMA[] = "ClipboardReadBlocked";
 constexpr char kDataTransferReportingTimeDiffUMA[] =
     "DataTransferReportingTimeDiff";
@@ -36,6 +37,10 @@
     "ScreenShareWarnSilentProceeded";
 constexpr char kScreenSharePausedOrResumedUMA[] = "ScreenSharePausedOrResumed";
 constexpr char kScreenshotBlockedUMA[] = "ScreenshotBlocked";
+constexpr char kScreenshotWarnedUMA[] = "ScreenshotWarned";
+constexpr char kScreenshotWarnProceededUMA[] = "ScreenshotWarnProceeded";
+constexpr char kScreenshotWarnSilentProceededUMA[] =
+    "ScreenshotWarnSilentProceeded";
 constexpr char kVideoCaptureInterruptedUMA[] = "VideoCaptureInterrupted";
 constexpr char kReportedBlockLevelRestriction[] =
     "ReportedBlockLevelRestriction";
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index e6bac3a..d60c68f 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -263,13 +263,13 @@
     status_code_ = status_code;
   }
 
-  static constexpr char kCriticaCH[] = "/critical-ch";
+  static constexpr char kCriticalCH[] = "/critical-ch";
 
  private:
   // A response that flips between two critical-ch headers
   std::unique_ptr<net::test_server::HttpResponse> DifferentCriticalCH(
       const net::test_server::HttpRequest& request) {
-    if (!base::StartsWith(request.relative_url, kCriticaCH))
+    if (!base::StartsWith(request.relative_url, kCriticalCH))
       return nullptr;
 
     request_count_++;
@@ -1694,7 +1694,9 @@
   EXPECT_EQ(0u, count_client_hints_headers_seen());
 }
 
-IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, DelegateToFoo_HttpEquiv) {
+// Flaky on all platforms. https://crbug.com/1285479.
+IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest,
+                       DISABLED_DelegateToFoo_HttpEquiv) {
   // Go to a page which delegates hints to `foo.com`.
   GURL gurl = http_equiv_accept_ch_delegation_foo();
   SetClientHintExpectationsOnMainFrame(false);
@@ -1707,7 +1709,8 @@
 }
 
 // Flaky on all platforms. https://crbug.com/1285479.
-IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, DelegateToFoo_MetaName) {
+IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest,
+                       DISABLED_DelegateToFoo_MetaName) {
   // Go to a page which delegates hints to `foo.com`.
   GURL gurl = meta_name_accept_ch_delegation_foo();
   SetClientHintExpectationsOnMainFrame(false);
@@ -3216,7 +3219,7 @@
   base::HistogramTester histogram;
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(),
-      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH)));
+      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH)));
   histogram.ExpectBucketCount("ClientHints.CriticalCHRestart",
                               2 /*=kNavigationRestarted*/, 1);
   EXPECT_EQ(2, handler.request_count());
@@ -3237,10 +3240,10 @@
   base::HistogramTester histogram;
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(),
-      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH)));
+      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH)));
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(),
-      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH)));
+      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH)));
   histogram.ExpectBucketCount("ClientHints.CriticalCHRestart",
                               2 /*=kNavigationRestarted*/, 2);
   EXPECT_EQ(4, handler.request_count());
@@ -3382,7 +3385,7 @@
   base::HistogramTester histogram;
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(),
-      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH)));
+      https_server.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH)));
   histogram.ExpectBucketCount("ClientHints.CriticalCHRestart",
                               2 /*=kNavigationRestarted*/, 1);
   EXPECT_EQ(2, handler.request_count());
@@ -3415,9 +3418,9 @@
   // This will send the two servers redirecting to each other in a loop until
   // the navigation redirect break is tripped.
   handler_1.SetRedirectLocation(
-      https_server_2.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH));
+      https_server_2.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH));
   handler_2.SetRedirectLocation(
-      https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH));
+      https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH));
 
   handler_1.SetStatusCode(GetParam());
   handler_2.SetStatusCode(GetParam());
@@ -3425,7 +3428,7 @@
   base::HistogramTester histogram;
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(),
-      https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticaCH)));
+      https_server_1.GetURL(AlternatingCriticalCHRequestHandler::kCriticalCH)));
   histogram.ExpectBucketCount("ClientHints.CriticalCHRestart",
                               2 /*=kNavigationRestarted*/, 2);
   EXPECT_EQ(net::URLRequest::kMaxRedirects,
@@ -5133,14 +5136,9 @@
   }
 
  protected:
-  virtual std::string BuildOriginTrialHeader() const = 0;
+  virtual std::string BuildOriginTrialHeader() const { return ""; }
 
-  OriginTrialTestOptions test_options_;
-  std::set<GURL> expected_request_urls_;
-  std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_;
-
- private:
-  std::unique_ptr<base::FeatureList> EnabledFeatures() {
+  virtual std::unique_ptr<base::FeatureList> EnabledFeatures() {
     std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
     feature_list->InitializeFromCommandLine(
         "UserAgentClientHint,CriticalClientHint,AcceptCHFrame,"
@@ -5149,6 +5147,9 @@
     return feature_list;
   }
 
+  OriginTrialTestOptions test_options_;
+  std::set<GURL> expected_request_urls_;
+  std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
@@ -5641,6 +5642,82 @@
   EXPECT_FALSE(cookies[0].IsPartitioned());
 }
 
+class PartitionedCookiesBypassOriginTrialBrowserTest
+    : public PartitionedCookiesOriginTrialBrowserTest {
+ public:
+  PartitionedCookiesBypassOriginTrialBrowserTest()
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    https_server_.ServeFilesFromSourceDirectory(
+        "chrome/test/data/client_hints");
+    https_server_.RegisterRequestMonitor(base::BindRepeating(
+        &PartitionedCookiesBypassOriginTrialBrowserTest::MonitorResourceRequest,
+        base::Unretained(this)));
+    EXPECT_TRUE(https_server_.Start());
+  }
+
+  // The URL of the site receiving cookies.
+  // Requests to this origin should be handled by the test server.
+  static constexpr char kCookieOriginUrlNoPort[] = "https://127.0.0.1:";
+
+  GURL partitioned_cookies_url() const {
+    return GURL(base::StrCat({kCookieOriginUrlNoPort,
+                              base::NumberToString(https_server_.port()),
+                              "/partitioned_cookies_embeddee.html"}));
+  }
+
+  absl::optional<std::string> last_sec_ch_partitioned_cookies_value() {
+    base::AutoLock lock(last_request_lock_);
+    return last_sec_ch_partitioned_cookies_value_;
+  }
+
+  void MonitorResourceRequest(const net::test_server::HttpRequest& request) {
+    base::AutoLock lock(last_request_lock_);
+    const auto& it = request.headers.find("sec-ch-partitioned-cookies");
+    last_sec_ch_partitioned_cookies_value_ =
+        it != request.headers.end() ? absl::make_optional(it->second)
+                                    : absl::nullopt;
+  }
+
+ protected:
+  std::unique_ptr<base::FeatureList> EnabledFeatures() override {
+    std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
+    feature_list->InitializeFromCommandLine(
+        "UserAgentClientHint,CriticalClientHint,AcceptCHFrame,"
+        "PartitionedCookies,PartitionedCookiesBypassOriginTrial",
+        "");
+    return feature_list;
+  }
+
+  net::EmbeddedTestServer https_server_;
+  absl::optional<std::string> last_sec_ch_partitioned_cookies_value_;
+  base::Lock last_request_lock_;
+};
+
+IN_PROC_BROWSER_TEST_F(PartitionedCookiesBypassOriginTrialBrowserTest,
+                       ShouldAllowCookiesWithoutToken) {
+  SetCookie(
+      "__Host-A", "0", partitioned_cookies_url(),
+      net::CookiePartitionKey::FromURLForTesting(partitioned_cookies_url()));
+
+  auto cookies = GetCookies(partitioned_cookies_url());
+  EXPECT_EQ(cookies.size(), 1u);
+  EXPECT_EQ(cookies[0].Name(), "__Host-A");
+  EXPECT_TRUE(cookies[0].IsPartitioned());
+
+  NavigateTo(partitioned_cookies_url());
+
+  // Check that the partitioned cookie did not get converted to unpartitioned
+  // even though the site never opted into the origin trial.
+  cookies = GetCookies(partitioned_cookies_url());
+  EXPECT_EQ(cookies.size(), 1u);
+  EXPECT_EQ(cookies[0].Name(), "__Host-A");
+  EXPECT_TRUE(cookies[0].IsPartitioned());
+
+  // We will still send the client hint in the false state if there are
+  // partitioned cookies on the machine.
+  EXPECT_EQ(last_sec_ch_partitioned_cookies_value(), "?0");
+}
+
 // CrOS multi-profiles implementation is too different for these tests.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 
diff --git a/chrome/browser/commerce/coupons/coupon_service.cc b/chrome/browser/commerce/coupons/coupon_service.cc
index 3c70f5b..0fb4523 100644
--- a/chrome/browser/commerce/coupons/coupon_service.cc
+++ b/chrome/browser/commerce/coupons/coupon_service.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/commerce/coupons/coupon_service.h"
 #include "base/observer_list.h"
-#include "chrome/browser/cart/cart_features.h"
 #include "chrome/browser/commerce/coupons/coupon_db_content.pb.h"
 #include "components/commerce/core/commerce_feature_list.h"
 
@@ -139,8 +138,8 @@
 }
 
 void CouponService::MaybeFeatureStatusChanged(bool enabled) {
-  enabled &= (commerce::IsCouponWithCodeEnabled() ||
-              cart_features::IsFakeDataEnabled());
+  enabled &=
+      (commerce::IsCouponWithCodeEnabled() || commerce::IsFakeDataEnabled());
   if (enabled == features_enabled_)
     return;
   features_enabled_ = enabled;
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index c371379..02e56c1 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -281,6 +281,9 @@
 }
 
 bool CrOSComponentInstaller::Unload(const std::string& name) {
+  DispatchFailedLoads(std::move(load_cache_[name].callbacks));
+  load_cache_.erase(name);
+
   const ComponentConfig* config = FindConfig(name);
   if (!config) {
     // Component |name| does not exist.
@@ -306,6 +309,8 @@
 }
 
 void CrOSComponentInstaller::UnregisterCompatiblePath(const std::string& name) {
+  DispatchFailedLoads(std::move(load_cache_[name].callbacks));
+  load_cache_.erase(name);
   compatible_components_.erase(name);
 }
 
@@ -320,6 +325,13 @@
     delegate_->EmitInstalledSignal(component);
 }
 
+CrOSComponentInstaller::LoadInfo::LoadInfo() = default;
+CrOSComponentInstaller::LoadInfo::~LoadInfo() = default;
+std::map<std::string, CrOSComponentInstaller::LoadInfo>&
+CrOSComponentInstaller::GetLoadCacheForTesting() {
+  return load_cache_;
+}
+
 bool CrOSComponentInstaller::IsRegisteredMayBlock(const std::string& name) {
   base::FilePath root;
   if (!base::PathService::Get(DIR_COMPONENT_USER, &root))
@@ -420,6 +432,23 @@
 
 void CrOSComponentInstaller::LoadInternal(const std::string& name,
                                           LoadCallback load_callback) {
+  // Use the cached value if it exists.
+  auto it = load_cache_.find(name);
+  if (it != load_cache_.end()) {
+    // If the request is ongoing, queue up a callback.
+    if (!it->second.success.has_value()) {
+      it->second.callbacks.push_back(std::move(load_callback));
+      return;
+    }
+    // Otherwise immediately dispatch.
+    DispatchLoadCallback(std::move(load_callback), it->second.path,
+                         it->second.success.value());
+    return;
+  }
+
+  // Update the cache to indicate the request is being queued.
+  load_cache_[name].success = absl::nullopt;
+
   const base::FilePath path = GetCompatiblePath(name);
   DCHECK(!path.empty());
   chromeos::DBusThreadManager::Get()
@@ -438,16 +467,25 @@
   // Report component image mount time.
   UMA_HISTOGRAM_LONG_TIMES("ComponentUpdater.ChromeOS.MountTime",
                            base::TimeTicks::Now() - start_time);
-  if (!result.has_value()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(load_callback),
-                       ReportError(Error::MOUNT_FAILURE), base::FilePath()));
-  } else {
-    metadata_table_->AddComponentForCurrentUser(name);
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(load_callback),
-                                  ReportError(Error::NONE), result.value()));
+
+  bool success = result.has_value();
+  base::FilePath path;
+  if (success)
+    path = result.value();
+
+  DispatchLoadCallback(std::move(load_callback), path, success);
+
+  // Update the cache.
+  auto it = load_cache_.find(name);
+  if (it != load_cache_.end()) {
+    it->second.success = success;
+    it->second.path = path;
+
+    // Dispatch queued up callbacks.
+    for (LoadCallback& queued_callback : it->second.callbacks) {
+      DispatchLoadCallback(std::move(queued_callback), path, success);
+    }
+    it->second.callbacks.clear();
   }
 }
 
@@ -463,4 +501,21 @@
   return compatible_components_.count(name) > 0;
 }
 
+void CrOSComponentInstaller::DispatchLoadCallback(LoadCallback callback,
+                                                  base::FilePath path,
+                                                  bool success) {
+  Error error = success ? Error::NONE : Error::MOUNT_FAILURE;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), ReportError(error), std::move(path)));
+}
+
+void CrOSComponentInstaller::DispatchFailedLoads(
+    std::vector<LoadCallback> callbacks) {
+  for (LoadCallback& callback : callbacks) {
+    DispatchLoadCallback(std::move(callback), base::FilePath(),
+                         /*success=*/false);
+  }
+}
+
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.h b/chrome/browser/component_updater/cros_component_installer_chromeos.h
index 6c2381f..5e971ecf 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.h
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.h
@@ -157,6 +157,28 @@
   // Broadcasts a D-Bus signal for a successful component installation.
   void EmitInstalledSignal(const std::string& component);
 
+  // The load cache contains three pieces of information:
+  //   (1) For a given component, whether the load request was successful, a
+  //   failure, or in-progress.
+  //   (2) If the load request was successful, the file path to the loaded
+  //   image.
+  //   (3) If the load request is in progress, the callbacks to invoke after the
+  //   load request finishes.
+  struct LoadInfo {
+    LoadInfo();
+    ~LoadInfo();
+    // If null, then the request is pending.
+    absl::optional<bool> success;
+    // Only populated on success.
+    base::FilePath path;
+    // Only populated if request is pending. Includes all subsequent callbacks
+    // after the first.
+    std::vector<LoadCallback> callbacks;
+  };
+
+  // Test-only method for introspection.
+  std::map<std::string, LoadInfo>& GetLoadCacheForTesting();
+
  protected:
   ~CrOSComponentInstaller() override;
 
@@ -211,6 +233,13 @@
   // |name|.
   bool IsCompatible(const std::string& name) const;
 
+  // Posts a task with the response information for |callback|.
+  void DispatchLoadCallback(LoadCallback callback,
+                            base::FilePath path,
+                            bool success);
+  // Repeatedly calls DispatchLoadCallback with failure parameters.
+  void DispatchFailedLoads(std::vector<LoadCallback> callbacks);
+
   // Maps from a compatible component name to its installed path.
   base::flat_map<std::string, base::FilePath> compatible_components_;
 
@@ -220,6 +249,10 @@
   // Table storing metadata (installs, usage, etc.).
   std::unique_ptr<MetadataTable> metadata_table_;
 
+  // The load cache stores ongoing load requests, as well as the finished
+  // results.
+  std::map<std::string, LoadInfo> load_cache_;
+
   ComponentUpdateService* const component_updater_;
 };
 
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc b/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc
index d7ffa9fb..524fa49 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos_unittest.cc
@@ -924,4 +924,54 @@
   EXPECT_EQ(base::FilePath(kTestComponentMountPath), mount_path);
 }
 
+TEST_F(CrOSComponentInstallerTest, LoadCache) {
+  absl::optional<base::FilePath> install_path = CreateInstalledComponent(
+      kTestComponentName, "1.0", kTestComponentValidMinEnvVersion);
+  ASSERT_TRUE(install_path.has_value());
+
+  image_loader_client()->SetMountPathForComponent(
+      kTestComponentName, base::FilePath(kTestComponentMountPath));
+
+  TestUpdater updater;
+  std::unique_ptr<MockComponentUpdateService> update_service =
+      CreateUpdateServiceForSingleRegistration(kTestComponentName, &updater);
+  scoped_refptr<CrOSComponentInstaller> cros_component_manager =
+      base::MakeRefCounted<CrOSComponentInstaller>(nullptr,
+                                                   update_service.get());
+
+  cros_component_manager->RegisterInstalled();
+  RunUntilIdle();
+  EXPECT_FALSE(updater.HasPendingUpdate(kTestComponentName));
+  EXPECT_EQ(install_path.value(),
+            cros_component_manager->GetCompatiblePath(kTestComponentName));
+
+  absl::optional<CrOSComponentManager::Error> load_result1;
+  base::FilePath mount_path1;
+  absl::optional<CrOSComponentManager::Error> load_result2;
+  base::FilePath mount_path2;
+  cros_component_manager->Load(
+      kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
+      CrOSComponentManager::UpdatePolicy::kDontForce,
+      base::BindOnce(&RecordLoadResult, &load_result1, &mount_path1));
+  cros_component_manager->Load(
+      kTestComponentName, CrOSComponentManager::MountPolicy::kMount,
+      CrOSComponentManager::UpdatePolicy::kDontForce,
+      base::BindOnce(&RecordLoadResult, &load_result2, &mount_path2));
+
+  auto& load_cache = cros_component_manager->GetLoadCacheForTesting();
+  ASSERT_EQ(load_cache.size(), 1u);
+  ASSERT_EQ(load_cache.begin()->second.callbacks.size(), 1u);
+  RunUntilIdle();
+
+  ASSERT_TRUE(load_result1.has_value());
+  ASSERT_TRUE(load_result2.has_value());
+  ASSERT_EQ(load_result1.value(), load_result2.value());
+  ASSERT_EQ(mount_path1, mount_path2);
+  ASSERT_EQ(load_cache.size(), 1u);
+  ASSERT_EQ(load_cache.begin()->second.callbacks.size(), 0u);
+  ASSERT_TRUE(load_cache.begin()->second.success.has_value());
+  ASSERT_TRUE(load_cache.begin()->second.success.value());
+  ASSERT_EQ(mount_path1, load_cache.begin()->second.path);
+}
+
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.cc b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.cc
new file mode 100644
index 0000000..b5b855b
--- /dev/null
+++ b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.cc
@@ -0,0 +1,119 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/task/post_task.h"
+#include "base/task/thread_pool.h"
+#include "base/version.h"
+#include "chrome/browser/share/share_features.h"
+#include "components/component_updater/component_updater_paths.h"
+
+namespace {
+
+constexpr base::FilePath::CharType kDesktopScreenshotEditorBinaryPbFileName[] =
+    FILE_PATH_LITERAL("image_editor_app");
+
+// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
+// The extension id is: kdbdaidmledpgkihpopchgmjikgkjclh
+constexpr uint8_t kDesktopScreenshotEditorPublicKeySHA256[32] = {
+    0x82, 0x30, 0x22, 0x02, 0x0d, 0x30, 0x09, 0x06, 0x86, 0x2a, 0x86,
+    0x48, 0x0d, 0xf7, 0x01, 0x01, 0x05, 0x01, 0x03, 0x00, 0x02, 0x82,
+    0x00, 0x0f, 0x82, 0x30, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x02};
+
+const char kDesktopScreenshotEditorManifestName[] =
+    "Desktop Screenshot Editor App";
+
+}  // namespace
+
+namespace component_updater {
+
+bool DesktopScreenshotEditorComponentInstallerPolicy::
+    SupportsGroupPolicyEnabledComponentUpdates() const {
+  return true;
+}
+
+bool DesktopScreenshotEditorComponentInstallerPolicy::
+    RequiresNetworkEncryption() const {
+  return false;
+}
+
+update_client::CrxInstaller::Result
+DesktopScreenshotEditorComponentInstallerPolicy::OnCustomInstall(
+    const base::Value& manifest,
+    const base::FilePath& install_dir) {
+  return update_client::CrxInstaller::Result(0);  // Nothing custom added.
+}
+
+void DesktopScreenshotEditorComponentInstallerPolicy::OnCustomUninstall() {}
+
+base::FilePath
+DesktopScreenshotEditorComponentInstallerPolicy::GetInstalledPath(
+    const base::FilePath& base) {
+  return base.Append(kDesktopScreenshotEditorBinaryPbFileName);
+}
+
+void DesktopScreenshotEditorComponentInstallerPolicy::ComponentReady(
+    const base::Version& version,
+    const base::FilePath& install_dir,
+    base::Value manifest) {
+  VLOG(1) << "Component ready, version " << version.GetString() << " in "
+          << install_dir.value();
+}
+
+// Called during startup and installation before ComponentReady().
+bool DesktopScreenshotEditorComponentInstallerPolicy::VerifyInstallation(
+    const base::Value& manifest,
+    const base::FilePath& install_dir) const {
+  // TODO(skare): We could enforce some sanity checks that files are present.
+  // Otherwise, this is a bundle of files that web contents load.
+  return base::PathExists(GetInstalledPath(install_dir));
+}
+
+base::FilePath
+DesktopScreenshotEditorComponentInstallerPolicy::GetRelativeInstallDir() const {
+  return base::FilePath(FILE_PATH_LITERAL("DesktopScreenshotEditor"));
+}
+
+void DesktopScreenshotEditorComponentInstallerPolicy::GetHash(
+    std::vector<uint8_t>* hash) const {
+  hash->assign(std::begin(kDesktopScreenshotEditorPublicKeySHA256),
+               std::end(kDesktopScreenshotEditorPublicKeySHA256));
+}
+
+std::string DesktopScreenshotEditorComponentInstallerPolicy::GetName() const {
+  return kDesktopScreenshotEditorManifestName;
+}
+
+update_client::InstallerAttributes
+DesktopScreenshotEditorComponentInstallerPolicy::GetInstallerAttributes()
+    const {
+  return update_client::InstallerAttributes();
+}
+
+void RegisterDesktopScreenshotEditorComponent(
+    component_updater::ComponentUpdateService* cus) {
+  // Require either Upcoming Sharing Features or DesktopScreenshotsEdit.
+  if (!share::AreUpcomingSharingFeaturesEnabled() &&
+      !base::FeatureList::IsEnabled(share::kSharingDesktopScreenshotsEdit)) {
+    return;
+  }
+  VLOG(1) << "Registering Screenshot Editor component.";
+  auto installer = base::MakeRefCounted<ComponentInstaller>(
+      std::make_unique<DesktopScreenshotEditorComponentInstallerPolicy>());
+  installer->Register(cus, base::OnceClosure());
+}
+
+}  // namespace component_updater
diff --git a/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h
new file mode 100644
index 0000000..d87f505
--- /dev/null
+++ b/chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h
@@ -0,0 +1,63 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_DESKTOP_SCREENSHOT_EDITOR_COMPONENT_INSTALLER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_DESKTOP_SCREENSHOT_EDITOR_COMPONENT_INSTALLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/values.h"
+#include "components/component_updater/component_installer.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace component_updater {
+
+class ComponentUpdateService;
+
+class DesktopScreenshotEditorComponentInstallerPolicy
+    : public ComponentInstallerPolicy {
+ public:
+  DesktopScreenshotEditorComponentInstallerPolicy() = default;
+  DesktopScreenshotEditorComponentInstallerPolicy(
+      const DesktopScreenshotEditorComponentInstallerPolicy&) = delete;
+  DesktopScreenshotEditorComponentInstallerPolicy& operator=(
+      const DesktopScreenshotEditorComponentInstallerPolicy&) = delete;
+  ~DesktopScreenshotEditorComponentInstallerPolicy() override = default;
+
+ private:
+  // The following methods override ComponentInstallerPolicy.
+  bool SupportsGroupPolicyEnabledComponentUpdates() const override;
+  bool RequiresNetworkEncryption() const override;
+  update_client::CrxInstaller::Result OnCustomInstall(
+      const base::Value& manifest,
+      const base::FilePath& install_dir) override;
+  void OnCustomUninstall() override;
+  bool VerifyInstallation(const base::Value& manifest,
+                          const base::FilePath& install_dir) const override;
+  void ComponentReady(const base::Version& version,
+                      const base::FilePath& install_dir,
+                      base::Value manifest) override;
+  base::FilePath GetRelativeInstallDir() const override;
+  void GetHash(std::vector<uint8_t>* hash) const override;
+  std::string GetName() const override;
+  update_client::InstallerAttributes GetInstallerAttributes() const override;
+
+  static base::FilePath GetInstalledPath(const base::FilePath& base);
+};
+
+// Call once during startup to make the component update service aware of
+// the Desktop Screenshot Editor component.
+void RegisterDesktopScreenshotEditorComponent(ComponentUpdateService* cus);
+
+}  // namespace component_updater
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_DESKTOP_SCREENSHOT_EDITOR_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h b/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h
index 4ac49a7..ccacbd0c 100644
--- a/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h
+++ b/chrome/browser/component_updater/desktop_sharing_hub_component_installer.h
@@ -55,7 +55,7 @@
 };
 
 // Call once during startup to make the component update service aware of
-// the File Type Policies component.
+// the Desktop Sharing Hub component.
 void RegisterDesktopSharingHubComponent(ComponentUpdateService* cus);
 
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.cc b/chrome/browser/component_updater/first_party_sets_component_installer.cc
index 3d2734d..00c39e0 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer.cc
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/feature_list.h"
 #include "base/files/file.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
@@ -24,6 +25,7 @@
 #include "content/public/browser/first_party_sets_handler.h"
 #include "content/public/common/content_features.h"
 #include "net/cookies/cookie_util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 using component_updater::ComponentUpdateService;
 
@@ -51,8 +53,11 @@
   return base::File(pb_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
 }
 
-base::FilePath& GetConfigPathInstance() {
-  static base::NoDestructor<base::FilePath> instance;
+absl::optional<base::FilePath>& GetConfigPathInstance() {
+  // Contains nullopt until registration is complete. Afterward, contains the
+  // FilePath for the component file, or an empty FilePath if no component was
+  // installed at startup.
+  static base::NoDestructor<absl::optional<base::FilePath>> instance;
   return *instance;
 }
 
@@ -74,8 +79,15 @@
     return;
   }
 
-  const base::FilePath instance_path = GetConfigPathInstance();
-  if (instance_path.empty()) {
+  const absl::optional<base::FilePath>& instance_path = GetConfigPathInstance();
+  if (!instance_path.has_value()) {
+    // Registration not is complete yet. The policy's `on_sets_ready_` callback
+    // will still be invoked once registration is done, so we don't bother to
+    // save or invoke `on_sets_ready`.
+    return;
+  }
+
+  if (instance_path->empty()) {
     // Registration is complete, but no component version exists on disk.
     std::move(on_sets_ready).Run(base::File());
     return;
@@ -85,7 +97,7 @@
   // network navigations at startup.
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock(), GetTaskPriority()},
-      base::BindOnce(&OpenFile, instance_path), std::move(on_sets_ready));
+      base::BindOnce(&OpenFile, *instance_path), std::move(on_sets_ready));
 }
 
 std::string BoolToString(bool b) {
@@ -103,6 +115,8 @@
 }
 
 void FirstPartySetsComponentInstallerPolicy::OnRegistrationComplete() {
+  if (!GetConfigPathInstance().has_value())
+    GetConfigPathInstance() = base::FilePath();
   SetFirstPartySetsConfig(std::move(on_sets_ready_));
 }
 
@@ -149,7 +163,7 @@
     const base::Version& version,
     const base::FilePath& install_dir,
     base::Value manifest) {
-  if (install_dir.empty() || !GetConfigPathInstance().empty())
+  if (install_dir.empty() || GetConfigPathInstance().has_value())
     return;
 
   VLOG(1) << "First-Party Sets Component ready, version " << version.GetString()
@@ -202,7 +216,7 @@
 
 // static
 void FirstPartySetsComponentInstallerPolicy::ResetForTesting() {
-  GetConfigPathInstance().clear();
+  GetConfigPathInstance().reset();
 }
 
 void RegisterFirstPartySetsComponent(ComponentUpdateService* cus) {
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.h b/chrome/browser/component_updater/first_party_sets_component_installer.h
index 72f4807f..4a352455 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer.h
+++ b/chrome/browser/component_updater/first_party_sets_component_installer.h
@@ -46,6 +46,7 @@
   static void ReconfigureAfterNetworkRestart(
       SetsReadyOnceCallback on_sets_ready);
 
+  // To be called once registration is complete.
   void OnRegistrationComplete();
 
   // Resets static state. Should only be used to clear state during testing.
@@ -69,6 +70,10 @@
   FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            LoadsSets_OnNetworkRestart);
   FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
+                           ReconfigureNetworkBeforeComponentReady);
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
+                           ReconfigureNetworkBeforeRegistrationComplete);
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            IgnoreNewSets_NoInitialComponent);
   FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            IgnoreNewSets_OnComponentReady);
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
index b19a6056..7053917a 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
@@ -292,6 +292,60 @@
   }
 }
 
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       ReconfigureNetworkBeforeComponentReady) {
+  SEQUENCE_CHECKER(sequence_checker);
+  const std::string expectation = "some first party sets";
+
+  base::RunLoop run_loop;
+  FirstPartySetsComponentInstallerPolicy policy(
+      base::BindLambdaForTesting([&](base::File file) {
+        DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+        EXPECT_TRUE(file.IsValid());
+        EXPECT_EQ(ReadToString(std::move(file)), expectation);
+        run_loop.Quit();
+      }));
+
+  ASSERT_TRUE(
+      base::WriteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
+                          component_install_dir_.GetPath()),
+                      expectation));
+
+  policy.ReconfigureAfterNetworkRestart(
+      base::BindLambdaForTesting([](base::File file) {
+        // Should not be called.
+        EXPECT_TRUE(false);
+      }));
+
+  policy.ComponentReady(base::Version(), component_install_dir_.GetPath(),
+                        base::Value(base::Value::Type::DICTIONARY));
+
+  run_loop.Run();
+}
+
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       ReconfigureNetworkBeforeRegistrationComplete) {
+  SEQUENCE_CHECKER(sequence_checker);
+
+  base::RunLoop run_loop;
+  FirstPartySetsComponentInstallerPolicy policy(
+      base::BindLambdaForTesting([&](base::File file) {
+        DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker);
+        EXPECT_FALSE(file.IsValid());
+        run_loop.Quit();
+      }));
+
+  policy.ReconfigureAfterNetworkRestart(
+      base::BindLambdaForTesting([](base::File file) {
+        // Should not be called.
+        EXPECT_TRUE(false);
+      }));
+
+  policy.OnRegistrationComplete();
+
+  run_loop.Run();
+}
+
 // Test ReconfigureAfterNetworkRestart calls the callback with the correct
 // version, i.e. the first installed component, even if there are newer versions
 // installed after browser startup.
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc
index 52efafe..839de739 100644
--- a/chrome/browser/component_updater/recovery_component_installer.cc
+++ b/chrome/browser/component_updater/recovery_component_installer.cc
@@ -201,6 +201,7 @@
 
   base::LaunchOptions options;
   options.start_hidden = true;
+  options.elevated = true;
   base::Process process = base::LaunchElevatedProcess(cmdline, options);
 #elif BUILDFLAG(IS_MAC)
   base::mac::ScopedAuthorizationRef authRef(
diff --git a/chrome/browser/component_updater/registration.cc b/chrome/browser/component_updater/registration.cc
index 754679bb..d33dbde 100644
--- a/chrome/browser/component_updater/registration.cc
+++ b/chrome/browser/component_updater/registration.cc
@@ -60,6 +60,7 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/component_updater/desktop_screenshot_editor_component_installer.h"
 #include "chrome/browser/component_updater/desktop_sharing_hub_component_installer.h"
 #include "chrome/browser/component_updater/soda_component_installer.h"
 #include "chrome/browser/component_updater/zxcvbn_data_component_installer.h"
@@ -195,6 +196,7 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
+  RegisterDesktopScreenshotEditorComponent(cus);
   RegisterDesktopSharingHubComponent(cus);
   RegisterZxcvbnDataComponent(cus);
 #endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/download/bubble/download_bubble_controller.cc b/chrome/browser/download/bubble/download_bubble_controller.cc
new file mode 100644
index 0000000..2f4a01e0
--- /dev/null
+++ b/chrome/browser/download/bubble/download_bubble_controller.cc
@@ -0,0 +1,281 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/download/bubble/download_bubble_controller.h"
+#include "base/files/file_path.h"
+#include "base/time/time.h"
+#include "chrome/browser/content_index/content_index_provider_impl.h"
+#include "chrome/browser/download/download_item_model.h"
+#include "chrome/browser/download/offline_item_model_manager.h"
+#include "chrome/browser/download/offline_item_model_manager_factory.h"
+#include "chrome/browser/download/offline_item_utils.h"
+#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
+#include "chrome/browser/profiles/profile_key.h"
+#include "components/download/public/common/download_item.h"
+#include "components/offline_items_collection/core/offline_content_aggregator.h"
+#include "content/public/browser/download_manager.h"
+
+using DownloadCreationType = ::download::DownloadItem::DownloadCreationType;
+
+namespace {
+constexpr int kShowDownloadsInBubbleForNumDays = 1;
+
+bool FindOfflineItemByContentId(const ContentId& to_find,
+                                const OfflineItem& candidate) {
+  return candidate.id == to_find;
+}
+
+bool DownloadUIModelIsRecent(const DownloadUIModelPtr& model,
+                             base::Time cutoff_time) {
+  return ((model->GetStartTime().is_null() && !model->IsDone()) ||
+          model->GetStartTime() > cutoff_time);
+}
+
+using DownloadUIModelPtrList = std::list<DownloadUIModelPtr>;
+struct StartTimeComparator {
+  bool operator()(const DownloadUIModelPtrList::iterator& a_iter,
+                  const DownloadUIModelPtrList::iterator& b_iter) const {
+    DownloadUIModel* a = (*a_iter).get();
+    DownloadUIModel* b = (*b_iter).get();
+    // Offline items have start time not populated, so display only not finished
+    // ones.
+    bool is_a_active_offline = (a->GetStartTime().is_null() && !a->IsDone());
+    bool is_b_active_offline = (b->GetStartTime().is_null() && !b->IsDone());
+    // a definitely shown before b if, 1) b is not an active offline item, and
+    // 2) Either a is active offline item, or a is more recent
+    return (!is_b_active_offline &&
+            (is_a_active_offline || (a->GetStartTime() > b->GetStartTime())));
+  }
+};
+using SortedDownloadUIModelSet =
+    std::multiset<DownloadUIModelPtrList::iterator, StartTimeComparator>;
+
+}  // namespace
+
+DownloadBubbleUIController::DownloadBubbleUIController(Profile* profile)
+    : profile_(profile),
+      download_manager_(profile_->GetDownloadManager()),
+      download_notifier_(download_manager_, this),
+      aggregator_(OfflineContentAggregatorFactory::GetForKey(
+          profile_->GetProfileKey())),
+      offline_manager_(
+          OfflineItemModelManagerFactory::GetForBrowserContext(profile_)) {
+  observation_.Observe(aggregator_.get());
+}
+
+DownloadBubbleUIController::~DownloadBubbleUIController() = default;
+
+bool DownloadBubbleUIController::MaybeAddOfflineItem(const OfflineItem& item,
+                                                     bool is_new) {
+  if (profile_->IsOffTheRecord() != item.is_off_the_record)
+    return false;
+
+  if (OfflineItemUtils::IsDownload(item.id))
+    return false;
+
+  if (item.state == OfflineItemState::CANCELLED)
+    return false;
+
+  if (item.id.name_space == ContentIndexProviderImpl::kProviderNamespace)
+    return false;
+
+  if (!OfflineItemModel::Wrap(offline_manager_, item)->ShouldShowInBubble())
+    return false;
+
+  offline_items_.push_back(item);
+  if (is_new) {
+    partial_view_ids_.insert(item.id);
+  }
+  return true;
+}
+
+void DownloadBubbleUIController::MaybeAddOfflineItems(
+    base::OnceCallback<void()> callback,
+    bool is_new,
+    const OfflineItemList& offline_items) {
+  for (const OfflineItem& item : offline_items) {
+    MaybeAddOfflineItem(item, is_new);
+  }
+  std::move(callback).Run();
+}
+
+void DownloadBubbleUIController::InitOfflineItems(
+    DownloadDisplayController* display_controller,
+    base::OnceCallback<void()> callback) {
+  display_controller_ = display_controller;
+  aggregator_->GetAllItems(base::BindOnce(
+      &DownloadBubbleUIController::MaybeAddOfflineItems,
+      weak_factory_.GetWeakPtr(), std::move(callback), /*is_new=*/false));
+}
+
+const OfflineItemList& DownloadBubbleUIController::GetOfflineItems() {
+  PruneOfflineItems();
+  return offline_items_;
+}
+
+void DownloadBubbleUIController::OnManagerGoingDown(
+    content::DownloadManager* manager) {
+  if (manager == download_manager_) {
+    download_manager_ = nullptr;
+  }
+}
+
+void DownloadBubbleUIController::OnContentProviderGoingDown() {
+  observation_.Reset();
+}
+
+void DownloadBubbleUIController::OnItemsAdded(
+    const OfflineContentProvider::OfflineItemList& items) {
+  bool any_new = false;
+  bool any_in_progress = false;
+  for (const OfflineItem& item : items) {
+    if (MaybeAddOfflineItem(item, /*is_new=*/true)) {
+      if (item.state == OfflineItemState::IN_PROGRESS) {
+        any_in_progress = true;
+      }
+      any_new = true;
+    }
+  }
+  if (any_new) {
+    display_controller_->OnNewItem(any_in_progress);
+  }
+}
+
+void DownloadBubbleUIController::OnDownloadCreated(
+    content::DownloadManager* manager,
+    download::DownloadItem* item) {
+  DownloadUIModelPtr model = DownloadItemModel::Wrap(
+      item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>());
+  if (item && model->ShouldShowInBubble() &&
+      model->download()->GetDownloadCreationType() !=
+          DownloadCreationType::TYPE_HISTORY_IMPORT) {
+    partial_view_ids_.insert(model->GetContentId());
+    display_controller_->OnNewItem(item->GetState() ==
+                                   download::DownloadItem::IN_PROGRESS);
+  }
+}
+
+void DownloadBubbleUIController::OnItemRemoved(const ContentId& id) {
+  offline_items_.erase(
+      std::remove_if(offline_items_.begin(), offline_items_.end(),
+                     [&id](const OfflineItem& candidate) {
+                       return FindOfflineItemByContentId(id, candidate);
+                     }),
+      offline_items_.end());
+  partial_view_ids_.erase(id);
+  display_controller_->OnRemovedItem();
+}
+
+void DownloadBubbleUIController::OnDownloadRemoved(
+    content::DownloadManager* manager,
+    download::DownloadItem* item) {
+  partial_view_ids_.erase(
+      DownloadItemModel::Wrap(
+          item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>())
+          ->GetContentId());
+  display_controller_->OnRemovedItem();
+}
+
+void DownloadBubbleUIController::OnItemUpdated(
+    const OfflineItem& item,
+    const absl::optional<UpdateDelta>& update_delta) {
+  // Update item
+  offline_items_.erase(
+      std::remove_if(offline_items_.begin(), offline_items_.end(),
+                     [&item](const OfflineItem& candidate) {
+                       return FindOfflineItemByContentId(item.id, candidate);
+                     }),
+      offline_items_.end());
+  MaybeAddOfflineItem(item, /*is_new=*/false);
+  display_controller_->OnUpdatedItem(
+      OfflineItemModel::Wrap(offline_manager_, item)->IsDone());
+}
+
+void DownloadBubbleUIController::OnDownloadUpdated(
+    content::DownloadManager* manager,
+    download::DownloadItem* item) {
+  display_controller_->OnUpdatedItem(
+      DownloadItemModel::Wrap(
+          item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>())
+          ->IsDone());
+}
+
+void DownloadBubbleUIController::RemoveContentIdFromPartialView(
+    const ContentId& id) {
+  partial_view_ids_.erase(id);
+}
+
+void DownloadBubbleUIController::PruneOfflineItems() {
+  base::Time cutoff_time =
+      base::Time::Now() - base::Days(kShowDownloadsInBubbleForNumDays);
+
+  for (auto item_iter = offline_items_.begin();
+       item_iter != offline_items_.end();) {
+    if (!DownloadUIModelIsRecent(
+            OfflineItemModel::Wrap(offline_manager_, *item_iter),
+            cutoff_time)) {
+      partial_view_ids_.erase(item_iter->id);
+      item_iter = offline_items_.erase(item_iter);
+    } else {
+      item_iter++;
+    }
+  }
+}
+
+std::vector<DownloadUIModelPtr> DownloadBubbleUIController::GetDownloadUIModels(
+    bool is_main_view) {
+  // Prune just to keep the list of offline entries small.
+  PruneOfflineItems();
+
+  // Aggregate downloads and offline items
+  std::vector<DownloadUIModelPtr> models_aggregate;
+  for (const OfflineItem& item : offline_items_) {
+    models_aggregate.push_back(OfflineItemModel::Wrap(
+        offline_manager_, item,
+        std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()));
+  }
+  std::vector<download::DownloadItem*> download_items;
+  download_manager_->GetAllDownloads(&download_items);
+  for (download::DownloadItem* item : download_items) {
+    models_aggregate.push_back(DownloadItemModel::Wrap(
+        item, std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()));
+  }
+
+  // Store list of DownloadUIModelPtrs. Sort list iterators in a set, as a set
+  // does not allow move semantics over unique_ptr, preventing us from putting
+  // DownloadUIModelPtr directly in the set.
+  DownloadUIModelPtrList filtered_models_list;
+  SortedDownloadUIModelSet sorted_ui_model_iters;
+  base::Time cutoff_time =
+      base::Time::Now() - base::Days(kShowDownloadsInBubbleForNumDays);
+  for (auto& model : models_aggregate) {
+    // Partial view consists of only the entries in partial_view_ids_, which are
+    // also removed if viewed on the main view.
+    if (model->ShouldShowInBubble() &&
+        DownloadUIModelIsRecent(model, cutoff_time) &&
+        (is_main_view || partial_view_ids_.find(model->GetContentId()) !=
+                             partial_view_ids_.end())) {
+      if (is_main_view) {
+        partial_view_ids_.erase(model->GetContentId());
+      }
+      filtered_models_list.push_front(std::move(model));
+      sorted_ui_model_iters.insert(filtered_models_list.begin());
+    }
+  }
+
+  // Convert set iterators to sorted vector.
+  std::vector<DownloadUIModelPtr> models_return_arr;
+  for (const auto& model_iter : sorted_ui_model_iters) {
+    models_return_arr.push_back(std::move((*model_iter)));
+  }
+  return models_return_arr;
+}
+
+std::vector<DownloadUIModelPtr> DownloadBubbleUIController::GetMainView() {
+  return GetDownloadUIModels(/*is_main_view=*/true);
+}
+
+std::vector<DownloadUIModelPtr> DownloadBubbleUIController::GetPartialView() {
+  return GetDownloadUIModels(/*is_main_view=*/false);
+}
diff --git a/chrome/browser/download/bubble/download_bubble_controller.h b/chrome/browser/download/bubble/download_bubble_controller.h
new file mode 100644
index 0000000..6d5491a
--- /dev/null
+++ b/chrome/browser/download/bubble/download_bubble_controller.h
@@ -0,0 +1,128 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
+#define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
+
+#include "base/scoped_observation.h"
+#include "chrome/browser/download/bubble/download_display_controller.h"
+#include "chrome/browser/download/offline_item_model.h"
+#include "components/download/content/public/all_download_item_notifier.h"
+#include "components/offline_items_collection/core/offline_content_aggregator.h"
+#include "components/offline_items_collection/core/offline_content_provider.h"
+#include "content/public/browser/download_manager.h"
+
+class Profile;
+
+using OfflineItemState = ::offline_items_collection::OfflineItemState;
+using ContentId = ::offline_items_collection::ContentId;
+using OfflineContentProvider =
+    ::offline_items_collection::OfflineContentProvider;
+using OfflineContentAggregator =
+    ::offline_items_collection::OfflineContentAggregator;
+using OfflineItem = ::offline_items_collection::OfflineItem;
+using UpdateDelta = ::offline_items_collection::UpdateDelta;
+using DownloadUIModelPtr = ::OfflineItemModel::DownloadUIModelPtr;
+using OfflineItemList =
+    ::offline_items_collection::OfflineContentAggregator::OfflineItemList;
+
+class DownloadBubbleUIController
+    : public OfflineContentProvider::Observer,
+      public download::AllDownloadItemNotifier::Observer {
+ public:
+  explicit DownloadBubbleUIController(Profile* profile);
+  DownloadBubbleUIController(const DownloadBubbleUIController&) = delete;
+  DownloadBubbleUIController& operator=(const DownloadBubbleUIController&) =
+      delete;
+  ~DownloadBubbleUIController() override;
+
+  // Get the entries for the main view of the Download Bubble. The main view
+  // contains all the recent downloads (finished within the last 24 hours).
+  std::vector<DownloadUIModelPtr> GetMainView();
+
+  // Get the entries for the partial view of the Download Bubble. The partial
+  // view contains in-progress and uninteracted downloads, meant to capture the
+  // user's recent tasks. This can only be opened by the browser in the event of
+  // new downloads, and user action only creates a main view.
+  std::vector<DownloadUIModelPtr> GetPartialView();
+
+  // The list is needed by DownloadDisplayController to check a few things,
+  // for example in progress download count, last completed time, and getting
+  // progress for animation.
+  virtual const OfflineItemList& GetOfflineItems();
+
+  // This function makes sure that the offline items field is
+  // populated, and then calls the given callback. After this, GetOfflineItems
+  // will return a populated list.
+  virtual void InitOfflineItems(DownloadDisplayController* display_controller,
+                                base::OnceCallback<void()> callback);
+
+  // Remove the entry from Partial view candidates.
+  void RemoveContentIdFromPartialView(const ContentId& id);
+
+  download::AllDownloadItemNotifier& get_download_notifier_for_testing() {
+    return download_notifier_;
+  }
+
+  void set_manager_for_testing(content::DownloadManager* manager) {
+    download_manager_ = manager;
+  }
+
+ private:
+  friend class DownloadBubbleUIControllerTest;
+  // AllDownloadItemNotifier::Observer
+  void OnDownloadCreated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void OnDownloadUpdated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void OnDownloadRemoved(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void OnManagerGoingDown(content::DownloadManager* manager) override;
+
+  // OfflineContentProvider::Observer
+  void OnItemsAdded(
+      const OfflineContentProvider::OfflineItemList& items) override;
+  void OnItemRemoved(const ContentId& id) override;
+  void OnItemUpdated(const OfflineItem& item,
+                     const absl::optional<UpdateDelta>& update_delta) override;
+  void OnContentProviderGoingDown() override;
+
+  // Try to add the items to the set/list(s) and calling callback on completion.
+  void MaybeAddOfflineItems(base::OnceCallback<void()> callback,
+                            bool is_new,
+                            const OfflineItemList& offline_items);
+
+  // Try to add the new item to the list, returning success status.
+  bool MaybeAddOfflineItem(const OfflineItem& item, bool is_new);
+
+  // Prune OfflineItems to recent items to in-progress offline items, or
+  // downloads started in the last day.
+  void PruneOfflineItems();
+
+  // Common method for getting main and partial views.
+  std::vector<DownloadUIModelPtr> GetDownloadUIModels(bool is_main_view);
+
+  raw_ptr<Profile> profile_;
+  raw_ptr<content::DownloadManager> download_manager_;
+  download::AllDownloadItemNotifier download_notifier_;
+  raw_ptr<OfflineContentAggregator> aggregator_;
+  raw_ptr<OfflineItemModelManager> offline_manager_;
+  base::ScopedObservation<OfflineContentProvider,
+                          OfflineContentProvider::Observer>
+      observation_{this};
+  // DownloadDisplayController and DownloadBubbleUIController have the same
+  // lifetime. Both are owned, constructed together, and destructed together by
+  // DownloadToolbarButtonView. If one is valid, so is the other.
+  raw_ptr<DownloadDisplayController> display_controller_;
+
+  // Pruned list of offline items.
+  OfflineItemList offline_items_;
+
+  // set of ids to be shown in partial_view.
+  std::set<ContentId> partial_view_ids_;
+
+  base::WeakPtrFactory<DownloadBubbleUIController> weak_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/download/bubble/download_bubble_controller_unittest.cc b/chrome/browser/download/bubble/download_bubble_controller_unittest.cc
new file mode 100644
index 0000000..89866e1c
--- /dev/null
+++ b/chrome/browser/download/bubble/download_bubble_controller_unittest.cc
@@ -0,0 +1,290 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/download/bubble/download_bubble_controller.h"
+#include "chrome/browser/download/bubble/download_display.h"
+#include "chrome/browser/download/bubble/download_display_controller.h"
+#include "chrome/browser/download/bubble/download_icon_state.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_core_service.h"
+#include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
+#include "chrome/browser/profiles/profile_key.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/download/public/common/mock_download_item.h"
+#include "components/offline_items_collection/core/offline_item.h"
+#include "components/offline_items_collection/core/test_support/mock_offline_content_provider.h"
+#include "content/public/browser/download_item_utils.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/mock_download_manager.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+using testing::SetArgPointee;
+
+namespace {
+using StrictMockDownloadItem = testing::StrictMock<download::MockDownloadItem>;
+using DownloadIconState = download::DownloadIconState;
+using DownloadState = download::DownloadItem::DownloadState;
+const char kProviderNamespace[] = "mock_namespace";
+
+class MockDownloadDisplayController : public DownloadDisplayController {
+ public:
+  MockDownloadDisplayController(Profile* profile,
+                                DownloadBubbleUIController* bubble_controller)
+      : DownloadDisplayController(nullptr, profile, bubble_controller) {}
+  void MaybeShowButtonWhenCreated() override {}
+  MOCK_METHOD1(OnNewItem, void(bool));
+  MOCK_METHOD1(OnUpdatedItem, void(bool));
+  MOCK_METHOD0(OnRemovedItem, void());
+};
+
+}  // namespace
+
+class DownloadBubbleUIControllerTest : public testing::Test {
+ public:
+  DownloadBubbleUIControllerTest()
+      : manager_(std::make_unique<NiceMock<content::MockDownloadManager>>()),
+        testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+  DownloadBubbleUIControllerTest(const DownloadBubbleUIControllerTest&) =
+      delete;
+  DownloadBubbleUIControllerTest& operator=(
+      const DownloadBubbleUIControllerTest&) = delete;
+
+  void SetUp() override {
+    ASSERT_TRUE(testing_profile_manager_.SetUp());
+
+    profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile");
+    EXPECT_CALL(*manager_.get(), GetBrowserContext())
+        .WillRepeatedly(Return(profile_));
+
+    // Set test delegate to get the corresponding download prefs.
+    auto delegate = std::make_unique<ChromeDownloadManagerDelegate>(profile_);
+    DownloadCoreServiceFactory::GetForBrowserContext(profile_)
+        ->SetDownloadManagerDelegateForTesting(std::move(delegate));
+
+    content_provider_ = std::make_unique<
+        NiceMock<offline_items_collection::MockOfflineContentProvider>>();
+    OfflineContentAggregatorFactory::GetForKey(profile_->GetProfileKey())
+        ->RegisterProvider(kProviderNamespace, content_provider_.get());
+
+    controller_ = std::make_unique<DownloadBubbleUIController>(profile_);
+    display_controller_ =
+        std::make_unique<NiceMock<MockDownloadDisplayController>>(
+            profile_, controller_.get());
+    controller_->set_manager_for_testing(manager_.get());
+  }
+
+  void TearDown() override {
+    for (auto& item : items_) {
+      item->RemoveObserver(&controller_->get_download_notifier_for_testing());
+    }
+    // The controller needs to be reset before download manager, because the
+    // download_notifier_ will unregister itself from the manager.
+    controller_.reset();
+  }
+
+ protected:
+  NiceMock<content::MockDownloadManager>& manager() { return *manager_.get(); }
+  download::MockDownloadItem& item(size_t index) { return *items_[index]; }
+  NiceMock<MockDownloadDisplayController>& display_controller() {
+    return *display_controller_;
+  }
+  DownloadBubbleUIController& controller() { return *controller_; }
+  NiceMock<offline_items_collection::MockOfflineContentProvider>&
+  content_provider() {
+    return *content_provider_;
+  }
+
+  void InitDownloadItem(const base::FilePath::CharType* path,
+                        DownloadState state,
+                        std::string& id,
+                        bool is_transient = false,
+                        base::Time start_time = base::Time::Now()) {
+    size_t index = items_.size();
+    items_.push_back(std::make_unique<StrictMockDownloadItem>());
+    EXPECT_CALL(item(index), GetId())
+        .WillRepeatedly(Return(static_cast<uint32_t>(items_.size() + 1)));
+    EXPECT_CALL(item(index), GetGuid()).WillRepeatedly(testing::ReturnRef(id));
+    EXPECT_CALL(item(index), GetState()).WillRepeatedly(Return(state));
+    EXPECT_CALL(item(index), GetStartTime()).WillRepeatedly(Return(start_time));
+    int received_bytes =
+        state == download::DownloadItem::IN_PROGRESS ? 50 : 100;
+    EXPECT_CALL(item(index), GetReceivedBytes())
+        .WillRepeatedly(Return(received_bytes));
+    EXPECT_CALL(item(index), GetTotalBytes()).WillRepeatedly(Return(100));
+    EXPECT_CALL(item(index), IsDone()).WillRepeatedly(Return(false));
+    EXPECT_CALL(item(index), IsTransient())
+        .WillRepeatedly(Return(is_transient));
+    EXPECT_CALL(item(index), GetDownloadCreationType())
+        .WillRepeatedly(Return(download::DownloadItem::DownloadCreationType::
+                                   TYPE_ACTIVE_DOWNLOAD));
+    std::vector<download::DownloadItem*> items;
+    for (size_t i = 0; i < items_.size(); ++i) {
+      items.push_back(&item(i));
+    }
+    EXPECT_CALL(*manager_.get(), GetAllDownloads(_))
+        .WillRepeatedly(SetArgPointee<0>(items));
+    item(index).AddObserver(&controller().get_download_notifier_for_testing());
+    content::DownloadItemUtils::AttachInfoForTesting(&(item(index)), profile_,
+                                                     nullptr);
+    controller().OnDownloadCreated(&manager(), &item(index));
+  }
+
+  void UpdateDownloadItem(int item_index, DownloadState state) {
+    DCHECK_GT(items_.size(), static_cast<size_t>(item_index));
+
+    EXPECT_CALL(item(item_index), GetState()).WillRepeatedly(Return(state));
+    if (state == DownloadState::COMPLETE) {
+      EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(true));
+      DownloadPrefs::FromDownloadManager(&manager())
+          ->SetLastCompleteTime(base::Time::Now());
+    } else {
+      EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(false));
+    }
+    item(item_index).NotifyObserversDownloadUpdated();
+  }
+
+  void InitOfflineItem(OfflineItemState state, std::string id) {
+    OfflineItem item;
+    item.state = state;
+    item.id.id = id;
+    offline_items_.push_back(item);
+    content_provider().NotifyOnItemsAdded({item});
+  }
+
+  void UpdateOfflineItem(int item_index, OfflineItemState state) {
+    offline_items_[item_index].state = state;
+    UpdateDelta delta;
+    delta.state_changed = true;
+    content_provider().NotifyOnItemUpdated(offline_items_[item_index], delta);
+  }
+
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+ private:
+  std::unique_ptr<DownloadBubbleUIController> controller_;
+  std::unique_ptr<NiceMock<MockDownloadDisplayController>> display_controller_;
+  std::vector<std::unique_ptr<StrictMockDownloadItem>> items_;
+  OfflineItemList offline_items_;
+  std::unique_ptr<NiceMock<content::MockDownloadManager>> manager_;
+  TestingProfileManager testing_profile_manager_;
+  std::unique_ptr<
+      NiceMock<offline_items_collection::MockOfflineContentProvider>>
+      content_provider_;
+  Profile* profile_;
+};
+
+TEST_F(DownloadBubbleUIControllerTest, ProcessesNewItems) {
+  std::vector<std::string> ids = {"Download 1", "Download 2", "Offline 1"};
+  EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[0]);
+  EXPECT_CALL(display_controller(), OnNewItem(false)).Times(1);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
+                   download::DownloadItem::COMPLETE, ids[1]);
+  EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1);
+  InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[2]);
+}
+
+TEST_F(DownloadBubbleUIControllerTest, ProcessesUpdatedItems) {
+  std::vector<std::string> ids = {"Download 1", "Offline 1"};
+  EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[0]);
+  EXPECT_CALL(display_controller(), OnUpdatedItem(false)).Times(1);
+  UpdateDownloadItem(/*item_index=*/0, DownloadState::IN_PROGRESS);
+  EXPECT_CALL(display_controller(), OnUpdatedItem(true)).Times(1);
+  UpdateDownloadItem(/*item_index=*/0, DownloadState::COMPLETE);
+
+  EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1);
+  InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[1]);
+  EXPECT_CALL(display_controller(), OnUpdatedItem(true)).Times(1);
+  UpdateOfflineItem(/*item_index=*/0, OfflineItemState::COMPLETE);
+}
+
+TEST_F(DownloadBubbleUIControllerTest, TransientDownloadShouldNotShow) {
+  std::vector<std::string> ids = {"Download 1", "Download 2"};
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[0],
+                   /*is_transient=*/true);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[1],
+                   /*is_transient=*/false);
+  std::vector<DownloadUIModelPtr> models = controller().GetMainView();
+  EXPECT_EQ(models.size(), 1ul);
+  EXPECT_EQ(models[0]->GetContentId().id, ids[1]);
+}
+
+TEST_F(DownloadBubbleUIControllerTest, ListIsSorted) {
+  std::vector<std::string> ids = {"Download 1", "Download 2", "Download 3",
+                                  "Offline 1"};
+  std::vector<base::TimeDelta> start_time_offsets = {
+      base::Hours(1), base::Hours(4), base::Hours(2)};
+  std::vector<std::string> sorted_ids = {"Offline 1", "Download 1",
+                                         "Download 3", "Download 2"};
+  base::Time now = base::Time::Now();
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[0],
+                   /*is_transient=*/false, now - start_time_offsets[0]);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[1],
+                   /*is_transient=*/false, now - start_time_offsets[1]);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar3.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[2],
+                   /*is_transient=*/false, now - start_time_offsets[2]);
+  InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[3]);
+  std::vector<DownloadUIModelPtr> models = controller().GetMainView();
+  EXPECT_EQ(models.size(), 4ul);
+  for (unsigned long i = 0; i < models.size(); i++) {
+    EXPECT_EQ(models[i]->GetContentId().id, sorted_ids[i]);
+  }
+}
+
+TEST_F(DownloadBubbleUIControllerTest, ListIsRecent) {
+  std::vector<std::string> ids = {"Download 1", "Download 2", "Download 3",
+                                  "Offline 1"};
+  std::vector<base::TimeDelta> start_time_offsets = {
+      base::Hours(1), base::Hours(25), base::Hours(2)};
+  std::vector<std::string> sorted_ids = {"Offline 1", "Download 1",
+                                         "Download 3"};
+  base::Time now = base::Time::Now();
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[0],
+                   /*is_transient=*/false, now - start_time_offsets[0]);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[1],
+                   /*is_transient=*/false, now - start_time_offsets[1]);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar3.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[2],
+                   /*is_transient=*/false, now - start_time_offsets[2]);
+  InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[3]);
+  std::vector<DownloadUIModelPtr> models = controller().GetMainView();
+  EXPECT_EQ(models.size(), 3ul);
+  for (unsigned long i = 0; i < models.size(); i++) {
+    EXPECT_EQ(models[i]->GetContentId().id, sorted_ids[i]);
+  }
+}
+
+TEST_F(DownloadBubbleUIControllerTest,
+       OpeningMainViewRemovesEntryFromPartialView) {
+  std::vector<std::string> ids = {"Download 1", "Offline 1"};
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS, ids[0]);
+  InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[1]);
+  std::vector<DownloadUIModelPtr> partial_view = controller().GetPartialView();
+  EXPECT_EQ(partial_view.size(), 2ul);
+  std::vector<DownloadUIModelPtr> main_view = controller().GetMainView();
+  EXPECT_EQ(main_view.size(), 2ul);
+  std::vector<DownloadUIModelPtr> partial_view_empty =
+      controller().GetPartialView();
+  EXPECT_EQ(partial_view_empty.size(), 0ul);
+}
diff --git a/chrome/browser/download/bubble/download_bubble_prefs.cc b/chrome/browser/download/bubble/download_bubble_prefs.cc
new file mode 100644
index 0000000..055b1eb3
--- /dev/null
+++ b/chrome/browser/download/bubble/download_bubble_prefs.cc
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/download/bubble/download_bubble_prefs.h"
+
+#include "base/feature_list.h"
+#include "chrome/browser/enterprise/connectors/connectors_service.h"
+#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
+#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
+#include "components/safe_browsing/core/common/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+
+namespace download {
+
+bool IsDownloadBubbleEnabled(Profile* profile) {
+  if (!base::FeatureList::IsEnabled(safe_browsing::kDownloadBubble)) {
+    return false;
+  }
+
+  // TODO(crbug.com/1307021): Enable download bubble for enhanced protection
+  // users, advanced protection users and enterprise connector users once it
+  // supports deep scanning.
+  if (safe_browsing::IsEnhancedProtectionEnabled(*profile->GetPrefs())) {
+    return false;
+  }
+
+  auto* advanced_protection_manager =
+      safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
+          profile);
+  if (advanced_protection_manager &&
+      advanced_protection_manager->IsUnderAdvancedProtection()) {
+    return false;
+  }
+
+  auto* connector_service =
+      enterprise_connectors::ConnectorsServiceFactory::GetForBrowserContext(
+          profile);
+  if (connector_service &&
+      connector_service->IsConnectorEnabled(
+          enterprise_connectors::AnalysisConnector::FILE_DOWNLOADED)) {
+    return false;
+  }
+
+  // TODO(crbug.com/1307021): Create an enterprise policy DownloadBubbleEnabled
+  // and check here.
+  return true;
+}
+
+}  // namespace download
diff --git a/chrome/browser/download/bubble/download_bubble_prefs.h b/chrome/browser/download/bubble/download_bubble_prefs.h
new file mode 100644
index 0000000..f424d5d
--- /dev/null
+++ b/chrome/browser/download/bubble/download_bubble_prefs.h
@@ -0,0 +1,16 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PREFS_H_
+#define CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PREFS_H_
+
+#include "chrome/browser/profiles/profile.h"
+
+namespace download {
+
+bool IsDownloadBubbleEnabled(Profile* profile);
+
+}  // namespace download
+
+#endif  // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_PREFS_H_
diff --git a/chrome/browser/download/bubble/download_bubble_prefs_unittest.cc b/chrome/browser/download/bubble/download_bubble_prefs_unittest.cc
new file mode 100644
index 0000000..e76f9ef
--- /dev/null
+++ b/chrome/browser/download/bubble/download_bubble_prefs_unittest.cc
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/download/bubble/download_bubble_prefs.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
+#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/safe_browsing/core/common/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace download {
+
+class DownloadBubblePrefsTest : public testing::Test {
+ public:
+  DownloadBubblePrefsTest()
+      : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+  DownloadBubblePrefsTest(const DownloadBubblePrefsTest&) = delete;
+  DownloadBubblePrefsTest& operator=(const DownloadBubblePrefsTest&) = delete;
+
+  void SetUp() override {
+    ASSERT_TRUE(testing_profile_manager_.SetUp());
+
+    profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile");
+  }
+
+ protected:
+  Profile* profile_;
+  base::test::ScopedFeatureList feature_list_;
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  TestingProfileManager testing_profile_manager_;
+};
+
+TEST_F(DownloadBubblePrefsTest, FeatureFlagEnabled) {
+  feature_list_.InitAndEnableFeature(safe_browsing::kDownloadBubble);
+  profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
+  EXPECT_TRUE(IsDownloadBubbleEnabled(profile_));
+}
+
+TEST_F(DownloadBubblePrefsTest, FeatureFlagDisabled) {
+  feature_list_.InitAndDisableFeature(safe_browsing::kDownloadBubble);
+  profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
+  EXPECT_FALSE(IsDownloadBubbleEnabled(profile_));
+}
+
+TEST_F(DownloadBubblePrefsTest, EnhancedProtectionEnabled) {
+  feature_list_.InitAndEnableFeature(safe_browsing::kDownloadBubble);
+  profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, true);
+  EXPECT_FALSE(IsDownloadBubbleEnabled(profile_));
+}
+
+TEST_F(DownloadBubblePrefsTest, AdvancedProtectionEnabled) {
+  feature_list_.InitAndEnableFeature(safe_browsing::kDownloadBubble);
+  profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
+  safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(profile_)
+      ->SetAdvancedProtectionStatusForTesting(true);
+  EXPECT_FALSE(IsDownloadBubbleEnabled(profile_));
+}
+
+}  // namespace download
diff --git a/chrome/browser/download/bubble/download_display_controller.cc b/chrome/browser/download/bubble/download_display_controller.cc
index 46d16ffa..d59fa8b 100644
--- a/chrome/browser/download/bubble/download_display_controller.cc
+++ b/chrome/browser/download/bubble/download_display_controller.cc
@@ -6,10 +6,13 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "chrome/browser/download/bubble/download_bubble_controller.h"
 #include "chrome/browser/download/bubble/download_display.h"
 #include "chrome/browser/download/bubble/download_icon_state.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_prefs.h"
+#include "components/offline_items_collection/core/offline_item.h"
+#include "components/offline_items_collection/core/offline_item_state.h"
 
 namespace {
 
@@ -26,36 +29,39 @@
 
 DownloadDisplayController::DownloadDisplayController(
     DownloadDisplay* display,
-    content::DownloadManager* download_manager)
+    Profile* profile,
+    DownloadBubbleUIController* bubble_controller)
     : display_(display),
-      download_manager_(download_manager),
-      download_notifier_(download_manager, this) {
-  MaybeShowButtonWhenCreated();
+      download_manager_(profile->GetDownloadManager()),
+      download_notifier_(download_manager_, this),
+      bubble_controller_(bubble_controller) {
+  bubble_controller_->InitOfflineItems(
+      this,
+      base::BindOnce(&DownloadDisplayController::MaybeShowButtonWhenCreated,
+                     weak_factory_.GetWeakPtr()));
 }
 
 DownloadDisplayController::~DownloadDisplayController() = default;
 
-void DownloadDisplayController::OnDownloadCreated(
-    content::DownloadManager* manager,
-    download::DownloadItem* item) {
+void DownloadDisplayController::OnNewItem(bool in_progress) {
   UpdateToolbarButtonState();
   // Only show details if the created download is in progress.
-  if (item->GetState() == download::DownloadItem::IN_PROGRESS) {
+  if (in_progress) {
     display_->ShowDetails();
   }
 }
 
-void DownloadDisplayController::OnDownloadUpdated(
-    content::DownloadManager* manager,
-    download::DownloadItem* item) {
-  DownloadItemModel item_model(item);
-
-  if (item_model.IsDone()) {
+void DownloadDisplayController::OnUpdatedItem(bool is_done) {
+  if (is_done) {
     ScheduleToolbarDisappearance(kToolbarIconVisibilityTimeInterval);
   }
   UpdateToolbarButtonState();
 }
 
+void DownloadDisplayController::OnRemovedItem() {
+  UpdateToolbarButtonState();
+}
+
 void DownloadDisplayController::OnManagerGoingDown(
     content::DownloadManager* manager) {
   if (download_manager_ == manager) {
@@ -86,21 +92,27 @@
 }
 
 void DownloadDisplayController::UpdateToolbarButtonState() {
-  if (download_manager_->InProgressCount() > 0) {
+  const auto& offline_items = bubble_controller_->GetOfflineItems();
+  int in_progress_count = download_manager_->InProgressCount();
+  for (const auto& offline_item : offline_items) {
+    in_progress_count += (offline_item.state == OfflineItemState::IN_PROGRESS);
+  }
+  if (in_progress_count > 0) {
     ShowToolbarButton();
     icon_info_.icon_state = DownloadIconState::kProgress;
     icon_info_.is_active = true;
+    display_->UpdateDownloadIcon();
   } else {
     icon_info_.icon_state = DownloadIconState::kComplete;
-    if (HasRecentCompleteDownload(kToolbarIconActiveTimeInterval)) {
+    if (HasRecentCompleteDownload(kToolbarIconActiveTimeInterval,
+                                  GetLastCompleteTime(offline_items))) {
       icon_info_.is_active = true;
       ScheduleToolbarInactive(kToolbarIconActiveTimeInterval);
     } else {
       icon_info_.is_active = false;
     }
+    display_->UpdateDownloadIcon();
   }
-
-  display_->UpdateDownloadIcon();
 }
 
 void DownloadDisplayController::UpdateDownloadIconToInactive() {
@@ -123,31 +135,41 @@
       &DownloadDisplayController::UpdateDownloadIconToInactive);
 }
 
+base::Time DownloadDisplayController::GetLastCompleteTime(
+    const offline_items_collection::OfflineContentAggregator::OfflineItemList&
+        offline_items) {
+  base::Time last_time = DownloadPrefs::FromDownloadManager(download_manager_)
+                             ->GetLastCompleteTime();
+  for (const auto& offline_item : offline_items) {
+    if (last_time < offline_item.completion_time)
+      last_time = offline_item.completion_time;
+  }
+  return last_time;
+}
+
 void DownloadDisplayController::MaybeShowButtonWhenCreated() {
-  if (!HasRecentCompleteDownload(kToolbarIconVisibilityTimeInterval)) {
+  base::Time last_complete_time =
+      GetLastCompleteTime(bubble_controller_->GetOfflineItems());
+  if (!HasRecentCompleteDownload(kToolbarIconVisibilityTimeInterval,
+                                 last_complete_time)) {
     return;
   }
   // If the last download complete time is less than
-  // `kToolbarIconVisibilityTimeInterval` ago, show the button immediately.
+  // `kToolbarIconVisibilityTimeInterval` ago, show the button
+  // immediately.
   ShowToolbarButton();
   icon_info_.icon_state = DownloadIconState::kComplete;
   // The initial state should be inactive, because there is no active
   // download.
   icon_info_.is_active = false;
   display_->UpdateDownloadIcon();
-
-  base::TimeDelta time_since_last_completion =
-      base::Time::Now() - DownloadPrefs::FromDownloadManager(download_manager_)
-                              ->GetLastCompleteTime();
   ScheduleToolbarDisappearance(kToolbarIconVisibilityTimeInterval -
-                               time_since_last_completion);
+                               (base::Time::Now() - last_complete_time));
 }
 
 bool DownloadDisplayController::HasRecentCompleteDownload(
-    base::TimeDelta interval) {
-  base::Time last_complete_time =
-      DownloadPrefs::FromDownloadManager(download_manager_)
-          ->GetLastCompleteTime();
+    base::TimeDelta interval,
+    base::Time last_complete_time) {
   base::Time current_time = base::Time::Now();
   base::TimeDelta time_since_last_completion =
       current_time - last_complete_time;
@@ -164,7 +186,6 @@
 DownloadDisplayController::ProgressInfo
 DownloadDisplayController::GetProgress() {
   DownloadDisplayController::ProgressInfo progress_info;
-
   int64_t received_bytes = 0;
   int64_t total_bytes = 0;
 
@@ -183,6 +204,19 @@
     }
   }
 
+  for (const auto& item : bubble_controller_->GetOfflineItems()) {
+    if (item.state == OfflineItemState::IN_PROGRESS) {
+      ++progress_info.download_count;
+      if (item.total_size_bytes <= 0) {
+        // There may or may not be more data coming down this pipe.
+        progress_info.progress_certain = false;
+      } else {
+        received_bytes += item.received_bytes;
+        total_bytes += item.total_size_bytes;
+      }
+    }
+  }
+
   if (total_bytes > 0) {
     progress_info.progress_percentage =
         base::ClampFloor(received_bytes * 100.0 / total_bytes);
diff --git a/chrome/browser/download/bubble/download_display_controller.h b/chrome/browser/download/bubble/download_display_controller.h
index 53c535b..92cbd16 100644
--- a/chrome/browser/download/bubble/download_display_controller.h
+++ b/chrome/browser/download/bubble/download_display_controller.h
@@ -7,12 +7,19 @@
 
 #include "base/timer/timer.h"
 #include "chrome/browser/download/bubble/download_icon_state.h"
+#include "chrome/browser/download/offline_item_model.h"
 #include "components/download/content/public/all_download_item_notifier.h"
+#include "components/offline_items_collection/core/offline_content_aggregator.h"
+#include "components/offline_items_collection/core/offline_content_provider.h"
 
 namespace content {
 class DownloadManager;
 }  // namespace content
 
+class Profile;
+class DownloadBubbleUIController;
+using DownloadUIModelPtr = ::OfflineItemModel::DownloadUIModelPtr;
+
 namespace base {
 class TimeDelta;
 class OneShotTimer;
@@ -20,11 +27,16 @@
 
 class DownloadDisplay;
 
+// Used to control the DownloadToolbar Button, through the DownloadDisplay
+// interface. Supports both regular Download and Offline items. When in the
+// future OfflineItems include regular Download on Desktop platforms,
+// we can remove AllDownloadItemNotifier::Observer.
 class DownloadDisplayController
     : public download::AllDownloadItemNotifier::Observer {
  public:
   DownloadDisplayController(DownloadDisplay* display,
-                            content::DownloadManager* download_manager);
+                            Profile* profile,
+                            DownloadBubbleUIController* bubble_controller);
   DownloadDisplayController(const DownloadDisplayController&) = delete;
   DownloadDisplayController& operator=(const DownloadDisplayController&) =
       delete;
@@ -49,16 +61,33 @@
   //
   // This implementation will match the one in download_status_updater.cc
   ProgressInfo GetProgress();
+
   // Returns an IconInfo that contains current state of the icon.
   IconInfo GetIconInfo();
 
   // Notifies the controller that the button is pressed. Called by `display_`.
   void OnButtonPressed();
 
+  // Common methods for new downloads or new offline items.
+  // Called from bubble controller when new item(s) are added, with
+  // |in_progress| as argument for if any was in progress.
+  // These methods are virtual so that they can be overridden for fake
+  // controllers in testing.
+  virtual void OnNewItem(bool in_progress);
+  // Called from bubble controller when an item is updated, with |is_done|
+  // indicating if it was marked done.
+  virtual void OnUpdatedItem(bool is_done);
+  // Called from bubble controller when an item is deleted.
+  virtual void OnRemovedItem();
+
   download::AllDownloadItemNotifier& get_download_notifier_for_testing() {
     return download_notifier_;
   }
 
+  void set_manager_for_testing(content::DownloadManager* manager) {
+    download_manager_ = manager;
+  }
+
  private:
   friend class DownloadDisplayControllerTest;
 
@@ -83,24 +112,31 @@
   void UpdateDownloadIconToInactive();
 
   // Decides whether the toolbar button should be shown when it is created.
-  void MaybeShowButtonWhenCreated();
+  virtual void MaybeShowButtonWhenCreated();
   // Whether the last download complete time is less than `interval` ago.
-  bool HasRecentCompleteDownload(base::TimeDelta interval);
+  bool HasRecentCompleteDownload(base::TimeDelta interval,
+                                 base::Time last_complete_time);
 
   // AllDownloadItemNotifier::Observer
-  void OnDownloadCreated(content::DownloadManager* manager,
-                         download::DownloadItem* item) override;
-  void OnDownloadUpdated(content::DownloadManager* manager,
-                         download::DownloadItem* item) override;
   void OnManagerGoingDown(content::DownloadManager* manager) override;
 
+  base::Time GetLastCompleteTime(
+      const offline_items_collection::OfflineContentAggregator::OfflineItemList&
+          offline_items);
+
   // The pointer is created in ToolbarView and owned by ToolbarView.
-  DownloadDisplay* const display_;
-  content::DownloadManager* download_manager_;
+  raw_ptr<DownloadDisplay> const display_;
+  raw_ptr<content::DownloadManager> download_manager_;
   download::AllDownloadItemNotifier download_notifier_;
   base::OneShotTimer icon_disappearance_timer_;
   base::OneShotTimer icon_inactive_timer_;
   IconInfo icon_info_;
+  // DownloadDisplayController and DownloadBubbleUIController have the same
+  // lifetime. Both are owned, constructed together, and destructed together by
+  // DownloadToolbarButtonView. If one is valid, so is the other.
+  raw_ptr<DownloadBubbleUIController> bubble_controller_;
+
+  base::WeakPtrFactory<DownloadDisplayController> weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_DOWNLOAD_BUBBLE_DOWNLOAD_DISPLAY_CONTROLLER_H_
diff --git a/chrome/browser/download/bubble/download_display_controller_unittest.cc b/chrome/browser/download/bubble/download_display_controller_unittest.cc
index 707e34e9..e4e3545 100644
--- a/chrome/browser/download/bubble/download_display_controller_unittest.cc
+++ b/chrome/browser/download/bubble/download_display_controller_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/download/bubble/download_display_controller.h"
+#include "chrome/browser/download/bubble/download_bubble_controller.h"
 #include "chrome/browser/download/bubble/download_display.h"
 #include "chrome/browser/download/bubble/download_icon_state.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
@@ -13,6 +14,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/download/public/common/mock_download_item.h"
+#include "components/offline_items_collection/core/offline_item.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/mock_download_manager.h"
 #include "content/public/test/test_utils.h"
@@ -27,6 +29,7 @@
 using StrictMockDownloadItem = testing::StrictMock<download::MockDownloadItem>;
 using DownloadIconState = download::DownloadIconState;
 using DownloadState = download::DownloadItem::DownloadState;
+using OfflineItemState = offline_items_collection::OfflineItemState;
 
 class FakeDownloadDisplay : public DownloadDisplay {
  public:
@@ -73,6 +76,25 @@
   DownloadDisplayController* controller_ = nullptr;
 };
 
+class FakeDownloadBubbleUIController : public DownloadBubbleUIController {
+ public:
+  explicit FakeDownloadBubbleUIController(Profile* profile)
+      : DownloadBubbleUIController(profile) {}
+  ~FakeDownloadBubbleUIController() override = default;
+  const OfflineItemList& GetOfflineItems() override { return offline_items_; }
+  void InitOfflineItems(DownloadDisplayController* display_controller,
+                        base::OnceCallback<void()> callback) override {
+    std::move(callback).Run();
+  }
+  void AddOfflineItem(OfflineItem& item) { offline_items_.push_back(item); }
+  void UpdateOfflineItem(int index, OfflineItemState state) {
+    offline_items_[index].state = state;
+  }
+
+ protected:
+  OfflineItemList offline_items_;
+};
+
 }  // namespace
 
 class DownloadDisplayControllerTest : public testing::Test {
@@ -87,19 +109,21 @@
   void SetUp() override {
     ASSERT_TRUE(testing_profile_manager_.SetUp());
 
-    Profile* profile =
-        testing_profile_manager_.CreateTestingProfile("testing_profile");
+    profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile");
     EXPECT_CALL(*manager_.get(), GetBrowserContext())
-        .WillRepeatedly(Return(profile));
+        .WillRepeatedly(Return(profile_));
 
     // Set test delegate to get the corresponding download prefs.
-    auto delegate = std::make_unique<ChromeDownloadManagerDelegate>(profile);
-    DownloadCoreServiceFactory::GetForBrowserContext(profile)
+    auto delegate = std::make_unique<ChromeDownloadManagerDelegate>(profile_);
+    DownloadCoreServiceFactory::GetForBrowserContext(profile_)
         ->SetDownloadManagerDelegateForTesting(std::move(delegate));
 
     display_ = std::make_unique<FakeDownloadDisplay>();
-    controller_ = std::make_unique<DownloadDisplayController>(display_.get(),
-                                                              manager_.get());
+    bubble_controller_ =
+        std::make_unique<FakeDownloadBubbleUIController>(profile_);
+    controller_ = std::make_unique<DownloadDisplayController>(
+        display_.get(), profile_, bubble_controller_.get());
+    controller_->set_manager_for_testing(manager_.get());
     display_->SetController(controller_.get());
   }
 
@@ -117,6 +141,10 @@
   download::MockDownloadItem& item(size_t index) { return *items_[index]; }
   FakeDownloadDisplay& display() { return *display_; }
   DownloadDisplayController& controller() { return *controller_; }
+  FakeDownloadBubbleUIController& bubble_controller() {
+    return *bubble_controller_;
+  }
+  Profile* profile() { return profile_; }
 
   void InitDownloadItem(const base::FilePath::CharType* path,
                         DownloadState state) {
@@ -144,7 +172,21 @@
     EXPECT_CALL(*manager_.get(), GetAllDownloads(_))
         .WillRepeatedly(SetArgPointee<0>(items));
     item(index).AddObserver(&controller().get_download_notifier_for_testing());
-    controller().OnDownloadCreated(&manager(), &item(index));
+    controller().OnNewItem(state == download::DownloadItem::IN_PROGRESS);
+  }
+
+  void InitOfflineItem(OfflineItemState state) {
+    OfflineItem item;
+    item.state = state;
+    bubble_controller().AddOfflineItem(item);
+    controller().OnNewItem(state == OfflineItemState::IN_PROGRESS);
+  }
+
+  void UpdateOfflineItem(int item_index, OfflineItemState state) {
+    if (state == OfflineItemState::COMPLETE) {
+      bubble_controller().UpdateOfflineItem(item_index, state);
+    }
+    controller().OnUpdatedItem(state == OfflineItemState::COMPLETE);
   }
 
   void UpdateDownloadItem(int item_index, DownloadState state) {
@@ -161,7 +203,7 @@
     } else {
       EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(false));
     }
-    item(item_index).NotifyObserversDownloadUpdated();
+    controller().OnUpdatedItem(state == DownloadState::COMPLETE);
   }
 
   bool VerifyDisplayState(bool shown,
@@ -203,8 +245,11 @@
   std::unique_ptr<DownloadDisplayController> controller_;
   std::unique_ptr<FakeDownloadDisplay> display_;
   std::vector<std::unique_ptr<StrictMockDownloadItem>> items_;
+
   std::unique_ptr<NiceMock<content::MockDownloadManager>> manager_;
+  std::unique_ptr<FakeDownloadBubbleUIController> bubble_controller_;
   TestingProfileManager testing_profile_manager_;
+  Profile* profile_;
 };
 
 TEST_F(DownloadDisplayControllerTest, GetProgressItemsInProgress) {
@@ -220,6 +265,22 @@
   EXPECT_EQ(progress.progress_percentage, 50);
 }
 
+TEST_F(DownloadDisplayControllerTest, OfflineItemsUncertainProgress) {
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
+                   download::DownloadItem::IN_PROGRESS);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"),
+                   download::DownloadItem::COMPLETE);
+  InitDownloadItem(FILE_PATH_LITERAL("/foo/bar4.pdf"),
+                   download::DownloadItem::IN_PROGRESS);
+  // This offline item has uncertain progress
+  InitOfflineItem(OfflineItemState::IN_PROGRESS);
+  DownloadDisplayController::ProgressInfo progress = controller().GetProgress();
+
+  EXPECT_EQ(progress.download_count, 3);
+  EXPECT_EQ(progress.progress_percentage, 50);
+  EXPECT_FALSE(progress.progress_certain);
+}
+
 TEST_F(DownloadDisplayControllerTest, GetProgressItemsAllComplete) {
   InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"),
                    download::DownloadItem::COMPLETE);
@@ -306,6 +367,17 @@
   EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/false,
                                  /*icon_state=*/DownloadIconState::kComplete,
                                  /*is_active=*/true));
+
+  InitOfflineItem(OfflineItemState::IN_PROGRESS);
+  EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/true,
+                                 /*icon_state=*/DownloadIconState::kProgress,
+                                 /*is_active=*/true));
+  display().SetDetailsShown(false);
+
+  UpdateOfflineItem(/*item_index=*/0, OfflineItemState::COMPLETE);
+  EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/false,
+                                 /*icon_state=*/DownloadIconState::kComplete,
+                                 /*is_active=*/true));
 }
 
 TEST_F(DownloadDisplayControllerTest,
@@ -328,7 +400,8 @@
   DownloadPrefs::FromDownloadManager(&manager())
       ->SetLastCompleteTime(current_time - base::Hours(25));
 
-  DownloadDisplayController controller(&display(), &manager());
+  DownloadDisplayController controller(&display(), profile(),
+                                       &bubble_controller());
   EXPECT_TRUE(VerifyDisplayState(/*shown=*/false, /*detail_shown=*/false,
                                  /*icon_state=*/DownloadIconState::kComplete,
                                  /*is_active=*/false));
@@ -340,7 +413,8 @@
   DownloadPrefs::FromDownloadManager(&manager())
       ->SetLastCompleteTime(current_time - base::Hours(23));
 
-  DownloadDisplayController controller(&display(), &manager());
+  DownloadDisplayController controller(&display(), profile(),
+                                       &bubble_controller());
   // The initial state should not display details.
   EXPECT_TRUE(VerifyDisplayState(/*shown=*/true, /*detail_shown=*/false,
                                  /*icon_state=*/DownloadIconState::kComplete,
@@ -355,7 +429,8 @@
 }
 
 TEST_F(DownloadDisplayControllerTest, InitialState_NoLastDownload) {
-  DownloadDisplayController controller(&display(), &manager());
+  DownloadDisplayController controller(&display(), profile(),
+                                       &bubble_controller());
   EXPECT_TRUE(VerifyDisplayState(/*shown=*/false, /*detail_shown=*/false,
                                  /*icon_state=*/DownloadIconState::kComplete,
                                  /*is_active=*/false));
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index a53f074..0006dda 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -299,8 +299,8 @@
   std::move(callback).Run(
       target_info->target_path, target_info->target_disposition,
       target_info->danger_type, target_info->mixed_content_status,
-      target_info->intermediate_path, std::move(target_info->download_schedule),
-      target_info->result);
+      target_info->intermediate_path, target_info->display_name,
+      std::move(target_info->download_schedule), target_info->result);
 }
 
 #if BUILDFLAG(IS_ANDROID)
@@ -313,13 +313,13 @@
     bool should_download) {
   // If the download should be blocked, we can call the callback directly.
   if (!should_download) {
-    std::move(callback).Run(target_info->target_path,
-                            target_info->target_disposition,
-                            download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-                            DownloadItem::MixedContentStatus::SILENT_BLOCK,
-                            target_info->intermediate_path,
-                            std::move(target_info->download_schedule),
-                            download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED);
+    std::move(callback).Run(
+        target_info->target_path, target_info->target_disposition,
+        download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+        DownloadItem::MixedContentStatus::SILENT_BLOCK,
+        target_info->intermediate_path, target_info->display_name,
+        std::move(target_info->download_schedule),
+        download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED);
     return;
   }
   target_info->mixed_content_status =
@@ -1241,9 +1241,9 @@
 void ChromeDownloadManagerDelegate::DetermineLocalPath(
     DownloadItem* download,
     const base::FilePath& virtual_path,
-    DownloadTargetDeterminerDelegate::LocalPathCallback callback) {
+    download::LocalPathCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  std::move(callback).Run(virtual_path);
+  download::DetermineLocalPath(download, virtual_path, std::move(callback));
 }
 
 void ChromeDownloadManagerDelegate::CheckDownloadUrl(
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h
index dffd12e..b4871ec 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.h
+++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -215,7 +215,7 @@
                            ConfirmationCallback callback) override;
   void DetermineLocalPath(download::DownloadItem* download,
                           const base::FilePath& virtual_path,
-                          LocalPathCallback callback) override;
+                          download::LocalPathCallback callback) override;
   void CheckDownloadUrl(download::DownloadItem* download,
                         const base::FilePath& suggested_virtual_path,
                         CheckDownloadUrlCallback callback) override;
diff --git a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
index 4309fcca..0734db4 100644
--- a/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate_unittest.cc
@@ -112,23 +112,20 @@
 
 // Struct for holding the result of calling DetermineDownloadTarget.
 struct DetermineDownloadTargetResult {
-  DetermineDownloadTargetResult();
-
   base::FilePath target_path;
-  download::DownloadItem::TargetDisposition disposition;
-  download::DownloadDangerType danger_type;
-  download::DownloadItem::MixedContentStatus mixed_content_status;
+  download::DownloadItem::TargetDisposition disposition =
+      download::DownloadItem::TARGET_DISPOSITION_OVERWRITE;
+  download::DownloadDangerType danger_type =
+      download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
+  download::DownloadItem::MixedContentStatus mixed_content_status =
+      download::DownloadItem::MixedContentStatus::UNKNOWN;
   base::FilePath intermediate_path;
-  download::DownloadInterruptReason interrupt_reason;
+  base::FilePath display_name;
+  download::DownloadInterruptReason interrupt_reason =
+      download::DOWNLOAD_INTERRUPT_REASON_NONE;
   absl::optional<download::DownloadSchedule> download_schedule;
 };
 
-DetermineDownloadTargetResult::DetermineDownloadTargetResult()
-    : disposition(download::DownloadItem::TARGET_DISPOSITION_OVERWRITE),
-      danger_type(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS),
-      mixed_content_status(download::DownloadItem::MixedContentStatus::UNKNOWN),
-      interrupt_reason(download::DOWNLOAD_INTERRUPT_REASON_NONE) {}
-
 // Subclass of the ChromeDownloadManagerDelegate that replaces a few interaction
 // points for ease of testing.
 class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
@@ -236,6 +233,12 @@
       const base::FilePath& path,
       DownloadTargetDeterminerDelegate::ConfirmationCallback) override {}
 
+  void DetermineLocalPath(download::DownloadItem* download,
+                          const base::FilePath& virtual_path,
+                          download::LocalPathCallback callback) override {
+    std::move(callback).Run(virtual_path, virtual_path.BaseName());
+  }
+
  private:
   friend class ChromeDownloadManagerDelegateTest;
 };
@@ -407,6 +410,7 @@
     download::DownloadDangerType danger_type,
     download::DownloadItem::MixedContentStatus mixed_content_status,
     const base::FilePath& intermediate_path,
+    const base::FilePath& display_name,
     absl::optional<download::DownloadSchedule> download_schedule,
     download::DownloadInterruptReason interrupt_reason) {
   result->target_path = target_path;
@@ -414,6 +418,7 @@
   result->danger_type = danger_type;
   result->mixed_content_status = mixed_content_status;
   result->intermediate_path = intermediate_path;
+  result->display_name = display_name;
   result->interrupt_reason = interrupt_reason;
   result->download_schedule = std::move(download_schedule);
   quit_runloop.Run();
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc
index 98fc8a5..de2ab5d 100644
--- a/chrome/browser/download/download_item_model.cc
+++ b/chrome/browser/download/download_item_model.cc
@@ -154,9 +154,7 @@
 }
 
 DownloadItemModel::DownloadItemModel(DownloadItem* download)
-    : download_(download) {
-  download_->AddObserver(this);
-}
+    : DownloadItemModel(download, std::make_unique<StatusTextBuilder>()) {}
 
 DownloadItemModel::DownloadItemModel(
     download::DownloadItem* download,
@@ -511,6 +509,10 @@
   return download_->TimeRemaining(remaining);
 }
 
+base::Time DownloadItemModel::GetStartTime() const {
+  return download_->GetStartTime();
+}
+
 base::Time DownloadItemModel::GetEndTime() const {
   return download_->GetEndTime();
 }
diff --git a/chrome/browser/download/download_item_model.h b/chrome/browser/download/download_item_model.h
index 6de605e..d7bdc6b 100644
--- a/chrome/browser/download/download_item_model.h
+++ b/chrome/browser/download/download_item_model.h
@@ -84,6 +84,7 @@
   bool GetOpenWhenComplete() const override;
   bool IsOpenWhenCompleteByPolicy() const override;
   bool TimeRemaining(base::TimeDelta* remaining) const override;
+  base::Time GetStartTime() const override;
   base::Time GetEndTime() const override;
   bool GetOpened() const override;
   void SetOpened(bool opened) override;
diff --git a/chrome/browser/download/download_shelf_controller.cc b/chrome/browser/download/download_shelf_controller.cc
index 99825e5..042cebe 100644
--- a/chrome/browser/download/download_shelf_controller.cc
+++ b/chrome/browser/download/download_shelf_controller.cc
@@ -80,7 +80,7 @@
     DownloadUIModel::DownloadUIModelPtr model) {
   Browser* browser = chrome::FindLastActiveWithProfile(profile_);
 
-  if (browser && browser->window()) {
+  if (browser && browser->window() && browser->window()->GetDownloadShelf()) {
     // Add the offline item to DownloadShelf in the browser window.
     browser->window()->GetDownloadShelf()->AddDownload(std::move(model));
   }
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index ec2bca8..0d144ef2 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -603,7 +603,8 @@
 }
 
 void DownloadTargetDeterminer::DetermineLocalPathDone(
-    const base::FilePath& local_path) {
+    const base::FilePath& local_path,
+    const base::FilePath& file_name) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DVLOG(20) << "Local path: " << local_path.AsUTF8Unsafe();
   if (local_path.empty()) {
@@ -619,6 +620,14 @@
   DCHECK_EQ(STATE_DETERMINE_MIME_TYPE, next_state_);
 
   local_path_ = local_path;
+#if BUILDFLAG(IS_ANDROID)
+  // If the |local path_| is a content Uri while the |virtual_path_| is a
+  // canonical path, replace the file name with the new name we got from
+  // the system so safebrowsing can check file extensions properly.
+  if (local_path_.IsContentUri() && !virtual_path_.IsContentUri()) {
+    virtual_path_ = virtual_path_.DirName().Append(file_name);
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
   DoLoop();
 }
 
@@ -905,7 +914,6 @@
   // If the local path is a content URI, the download should be from resumption
   // and we can just use the current path.
   if (local_path_.IsContentUri()) {
-    DCHECK(is_resumption_);
     intermediate_path_ = local_path_;
     return COMPLETE;
   }
@@ -1004,6 +1012,12 @@
   target_info->is_filetype_handled_safely = is_filetype_handled_safely_;
   target_info->mixed_content_status = mixed_content_status_;
   target_info->download_schedule = std::move(download_schedule_);
+#if BUILDFLAG(IS_ANDROID)
+  // If |virtual_path_| is content URI, there is no need to prompt the user.
+  if (local_path_.IsContentUri() && !virtual_path_.IsContentUri()) {
+    target_info->display_name = virtual_path_.BaseName();
+  }
+#endif
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
diff --git a/chrome/browser/download/download_target_determiner.h b/chrome/browser/download/download_target_determiner.h
index a983719..eb7b04c 100644
--- a/chrome/browser/download/download_target_determiner.h
+++ b/chrome/browser/download/download_target_determiner.h
@@ -223,8 +223,12 @@
   // - STATE_DETERMINE_MIME_TYPE.
   Result DoDetermineLocalPath();
 
-  // Callback invoked when the delegate has determined local path.
-  void DetermineLocalPathDone(const base::FilePath& local_path);
+  // Callback invoked when the delegate has determined local path. |file_name|
+  // is supplied in case it cannot be determined from local_path (e.g. local
+  // path is a content Uri: content://media/12345). |file_name| could be empty
+  // if it is the last component of |local_path|.
+  void DetermineLocalPathDone(const base::FilePath& local_path,
+                              const base::FilePath& file_name);
 
   // Determine the MIME type corresponding to the local file path. This is only
   // done if the local path and the virtual path was the same. I.e. The file is
diff --git a/chrome/browser/download/download_target_determiner_delegate.h b/chrome/browser/download/download_target_determiner_delegate.h
index dc1f526..e9eae91 100644
--- a/chrome/browser/download/download_target_determiner_delegate.h
+++ b/chrome/browser/download/download_target_determiner_delegate.h
@@ -14,6 +14,7 @@
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/download_path_reservation_tracker.h"
 #include "components/download/public/common/download_schedule.h"
+#include "components/download/public/common/download_utils.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
@@ -55,12 +56,6 @@
       const base::FilePath& virtual_path,
       absl::optional<download::DownloadSchedule> download_schedule)>;
 
-  // Callback to be invoked when DetermineLocalPath() completes. The argument
-  // should be the determined local path. It should be non-empty on success. If
-  // |virtual_path| is already a local path, then |virtual_path| should be
-  // returned as-is.
-  using LocalPathCallback = base::OnceCallback<void(const base::FilePath&)>;
-
   // Callback to be invoked after CheckDownloadUrl() completes. The parameter to
   // the callback should indicate the danger type of the download based on the
   // results of the URL check.
@@ -120,7 +115,7 @@
   // invoked to return the path.
   virtual void DetermineLocalPath(download::DownloadItem* download,
                                   const base::FilePath& virtual_path,
-                                  LocalPathCallback callback) = 0;
+                                  download::LocalPathCallback callback) = 0;
 
   // Check whether the download URL is malicious and invoke |callback| with a
   // suggested danger type for the download.
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index 6468749d..bd5498f 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -226,11 +226,13 @@
                     ConfirmationCallback&));
   void DetermineLocalPath(DownloadItem* item,
                           const base::FilePath& path,
-                          LocalPathCallback cb) override {
+                          download::LocalPathCallback cb) override {
     DetermineLocalPath_(item, path, cb);
   }
   MOCK_METHOD3(DetermineLocalPath_,
-               void(DownloadItem*, const base::FilePath&, LocalPathCallback&));
+               void(DownloadItem*,
+                    const base::FilePath&,
+                    download::LocalPathCallback&));
   void ReserveVirtualPath(
       DownloadItem* download,
       const base::FilePath& virtual_path,
@@ -287,7 +289,7 @@
                              ConfirmationCallback& callback);
   static void NullDetermineLocalPath(DownloadItem* download,
                                      const base::FilePath& virtual_path,
-                                     LocalPathCallback& callback);
+                                     download::LocalPathCallback& callback);
 };
 
 class DownloadTargetDeterminerTest : public ChromeRenderViewHostTestHarness {
@@ -624,8 +626,8 @@
 void MockDownloadTargetDeterminerDelegate::NullDetermineLocalPath(
     DownloadItem* download,
     const base::FilePath& virtual_path,
-    LocalPathCallback& callback) {
-  std::move(callback).Run(virtual_path);
+    download::LocalPathCallback& callback) {
+  std::move(callback).Run(virtual_path, virtual_path.BaseName());
 }
 
 // NotifyExtensions implementation that overrides the path so that the target
@@ -926,8 +928,9 @@
                                  _, last_selected_dir.AppendASCII("foo.txt"),
                                  DownloadConfirmationReason::SAVE_AS, _));
     EXPECT_CALL(*delegate(), DetermineLocalPath_(_, virtual_path, _))
-        .WillOnce(WithArg<2>(ScheduleCallback(
-            GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")))));
+        .WillOnce(WithArg<2>(ScheduleCallback2(
+            GetPathInDownloadDir(FILE_PATH_LITERAL("bar.txt")),
+            base::FilePath())));
     RunTestCasesWithActiveItem(kLastSavePathTestCasesVirtual,
                                std::size(kLastSavePathTestCasesVirtual));
   }
@@ -954,8 +957,9 @@
 
         EXPECT_LOCAL_PATH};
     EXPECT_CALL(*delegate(), DetermineLocalPath_(_, _, _))
-        .WillOnce(WithArg<2>(ScheduleCallback(
-            GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")))));
+        .WillOnce(WithArg<2>(ScheduleCallback2(
+            GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")),
+            base::FilePath())));
     RunTestCasesWithActiveItem(&kAutomaticDownloadToVirtualDir, 1);
   }
 
@@ -974,8 +978,9 @@
 
         EXPECT_LOCAL_PATH};
     EXPECT_CALL(*delegate(), DetermineLocalPath_(_, _, _))
-        .WillOnce(WithArg<2>(ScheduleCallback(
-            GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")))));
+        .WillOnce(WithArg<2>(ScheduleCallback2(
+            GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")),
+            base::FilePath())));
     EXPECT_CALL(*delegate(), RequestConfirmation_(
                                  _, test_virtual_dir().AppendASCII("bar.txt"),
                                  DownloadConfirmationReason::SAVE_AS, _))
@@ -1145,7 +1150,8 @@
       *delegate(),
       DetermineLocalPath_(
           _, GetPathInDownloadDir(FILE_PATH_LITERAL("virtual/foo.txt")), _))
-      .WillOnce(WithArg<2>(ScheduleCallback(base::FilePath())));
+      .WillOnce(
+          WithArg<2>(ScheduleCallback2(base::FilePath(), base::FilePath())));
   RunTestCasesWithActiveItem(kLocalPathFailedCases,
                              std::size(kLocalPathFailedCases));
 }
@@ -2680,4 +2686,38 @@
 
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
+#if BUILDFLAG(IS_ANDROID)
+// If a content URI is returned when determining local path, virtual path
+// is updated.
+TEST_F(DownloadTargetDeterminerTest, DetermineLocalPathReturnsContentUri) {
+  const DownloadTestCase kLocalPathContentUriCase = {
+      AUTOMATIC,
+      download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/foo.txt",
+      "text/plain",
+      FILE_PATH_LITERAL(""),
+      FILE_PATH_LITERAL("content://media/123"),
+      DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+      EXPECT_LOCAL_PATH};
+
+  std::unique_ptr<download::MockDownloadItem> item =
+      CreateActiveDownloadItem(0, kLocalPathContentUriCase);
+  // The default download directory is the virtual path.
+  download_prefs()->SetDownloadPath(test_virtual_dir());
+
+  EXPECT_CALL(
+      *delegate(),
+      DetermineLocalPath_(
+          _, GetPathInDownloadDir(FILE_PATH_LITERAL("virtual/foo.txt")), _))
+      .WillOnce(WithArg<2>(ScheduleCallback2(
+          base::FilePath("content://media/123"), base::FilePath("foor.txt"))));
+  std::unique_ptr<DownloadTargetInfo> target_info =
+      RunDownloadTargetDeterminer(base::FilePath(), item.get());
+
+  EXPECT_EQ(target_info->display_name.value(), "foor.txt");
+  EXPECT_EQ(target_info->target_path.value(), "content://media/123");
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
 }  // namespace
diff --git a/chrome/browser/download/download_target_info.h b/chrome/browser/download/download_target_info.h
index 3a6e884..fb3330e 100644
--- a/chrome/browser/download/download_target_info.h
+++ b/chrome/browser/download/download_target_info.h
@@ -85,6 +85,9 @@
 
   // Defines when to start the download, used by download later feature.
   absl::optional<download::DownloadSchedule> download_schedule;
+
+  // Display name of the file.
+  base::FilePath display_name;
 };
 
 #endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_TARGET_INFO_H_
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc
index 2bfb093..013b925 100644
--- a/chrome/browser/download/download_ui_controller.cc
+++ b/chrome/browser/download/download_ui_controller.cc
@@ -100,7 +100,7 @@
   if (browser == nullptr)
     browser = chrome::FindLastActiveWithProfile(profile_);
 
-  if (browser && browser->window() &&
+  if (browser && browser->window() && browser->window()->GetDownloadShelf() &&
       DownloadItemModel(item).ShouldShowInShelf()) {
     DownloadUIModel::DownloadUIModelPtr model = DownloadItemModel::Wrap(item);
 
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 7339f2f..9f07bdb 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -421,6 +421,10 @@
 
 void DownloadUIModel::SetShouldShowInShelf(bool should_show) {}
 
+bool DownloadUIModel::ShouldShowInBubble() const {
+  return ShouldShowInShelf();
+}
+
 bool DownloadUIModel::ShouldNotifyUI() const {
   return true;
 }
@@ -505,6 +509,10 @@
   return false;
 }
 
+base::Time DownloadUIModel::GetStartTime() const {
+  return base::Time();
+}
+
 base::Time DownloadUIModel::GetEndTime() const {
   return base::Time();
 }
@@ -867,14 +875,19 @@
   if (!status_text.empty())
     return status_text;
 
-  std::u16string total_text = ui::FormatBytes(model_->GetTotalBytes());
-  std::u16string delta_str = ui::TimeFormat::Simple(
-      ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
-      base::Time::Now() - model_->GetEndTime());
-  return base::StrCat(
-      {total_text,
-       l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR),
-       delta_str});
+  if (model_->GetEndTime().is_null()) {
+    // Offline items have these null.
+    return l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_DONE);
+  } else {
+    std::u16string total_text = ui::FormatBytes(model_->GetTotalBytes());
+    std::u16string delta_str = ui::TimeFormat::Simple(
+        ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
+        base::Time::Now() - model_->GetEndTime());
+    return base::StrCat(
+        {total_text,
+         l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR),
+         delta_str});
+  }
 }
 
 // To clarify variable / method names in methods below that help form failure
diff --git a/chrome/browser/download/download_ui_model.h b/chrome/browser/download/download_ui_model.h
index a8e5fabc..3b3983ee 100644
--- a/chrome/browser/download/download_ui_model.h
+++ b/chrome/browser/download/download_ui_model.h
@@ -208,6 +208,9 @@
   // Returns |true| if this download should be displayed in the downloads shelf.
   virtual bool ShouldShowInShelf() const;
 
+  // Returns |true| if this download should be displayed in the download bubble.
+  virtual bool ShouldShowInBubble() const;
+
   // Change whether the download should be displayed on the downloads
   // shelf. Setting this is only effective if the download hasn't already been
   // displayed in the shelf.
@@ -298,6 +301,9 @@
   // and so can't give an estimate.
   virtual bool TimeRemaining(base::TimeDelta* remaining) const;
 
+  // Returns the creation time for a download.
+  virtual base::Time GetStartTime() const;
+
   // Returns the end/completion time for a completed download. base::Time()
   // if the download has not completed yet.
   virtual base::Time GetEndTime() const;
diff --git a/chrome/browser/download/offline_item_model.cc b/chrome/browser/download/offline_item_model.cc
index 9bda738..ea50e3b 100644
--- a/chrome/browser/download/offline_item_model.cc
+++ b/chrome/browser/download/offline_item_model.cc
@@ -31,9 +31,30 @@
   return model;
 }
 
+DownloadUIModel::DownloadUIModelPtr OfflineItemModel::Wrap(
+    OfflineItemModelManager* manager,
+    const OfflineItem& offline_item,
+    std::unique_ptr<DownloadUIModel::StatusTextBuilderBase>
+        status_text_builder) {
+  DownloadUIModel::DownloadUIModelPtr model(
+      new OfflineItemModel(manager, offline_item,
+                           std::move(status_text_builder)),
+      base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
+  return model;
+}
+
 OfflineItemModel::OfflineItemModel(OfflineItemModelManager* manager,
                                    const OfflineItem& offline_item)
-    : manager_(manager),
+    : OfflineItemModel(manager,
+                       offline_item,
+                       std::make_unique<StatusTextBuilder>()) {}
+
+OfflineItemModel::OfflineItemModel(
+    OfflineItemModelManager* manager,
+    const OfflineItem& offline_item,
+    std::unique_ptr<DownloadUIModel::StatusTextBuilderBase> status_text_builder)
+    : DownloadUIModel(std::move(status_text_builder)),
+      manager_(manager),
       offline_item_(std::make_unique<OfflineItem>(offline_item)) {
   Profile* profile = Profile::FromBrowserContext(manager_->browser_context());
   offline_items_collection::OfflineContentAggregator* aggregator =
@@ -169,6 +190,10 @@
   return true;
 }
 
+base::Time OfflineItemModel::GetStartTime() const {
+  return offline_item_->creation_time;
+}
+
 base::Time OfflineItemModel::GetEndTime() const {
   return offline_item_->completion_time;
 }
diff --git a/chrome/browser/download/offline_item_model.h b/chrome/browser/download/offline_item_model.h
index 1acb8a5..f52c02e 100644
--- a/chrome/browser/download/offline_item_model.h
+++ b/chrome/browser/download/offline_item_model.h
@@ -28,10 +28,19 @@
  public:
   static DownloadUIModelPtr Wrap(OfflineItemModelManager* manager,
                                  const OfflineItem& offline_item);
+  static DownloadUIModelPtr Wrap(
+      OfflineItemModelManager* manager,
+      const OfflineItem& offline_item,
+      std::unique_ptr<DownloadUIModel::StatusTextBuilderBase>
+          status_text_builder);
 
   // Constructs a OfflineItemModel.
   OfflineItemModel(OfflineItemModelManager* manager,
                    const OfflineItem& offline_item);
+  OfflineItemModel(OfflineItemModelManager* manager,
+                   const OfflineItem& offline_item,
+                   std::unique_ptr<DownloadUIModel::StatusTextBuilderBase>
+                       status_text_builder);
 
   OfflineItemModel(const OfflineItemModel&) = delete;
   OfflineItemModel& operator=(const OfflineItemModel&) = delete;
@@ -56,6 +65,7 @@
   download::DownloadItem::DownloadState GetState() const override;
   bool IsPaused() const override;
   bool TimeRemaining(base::TimeDelta* remaining) const override;
+  base::Time GetStartTime() const override;
   base::Time GetEndTime() const override;
   bool IsDone() const override;
   base::FilePath GetFullPath() const override;
diff --git a/chrome/browser/enterprise/browser_management/browser_management_status_provider.h b/chrome/browser/enterprise/browser_management/browser_management_status_provider.h
index fd6d618..6f842720 100644
--- a/chrome/browser/enterprise/browser_management/browser_management_status_provider.h
+++ b/chrome/browser/enterprise/browser_management/browser_management_status_provider.h
@@ -58,6 +58,7 @@
 };
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+// This is both a device and browser management status provider for ChromeOS.
 class DeviceManagementStatusProvider final
     : public policy::ManagementStatusProvider {
  public:
diff --git a/chrome/browser/enterprise/browser_management/management_service_factory.cc b/chrome/browser/enterprise/browser_management/management_service_factory.cc
index c628ed0d..175f2ba 100644
--- a/chrome/browser/enterprise/browser_management/management_service_factory.cc
+++ b/chrome/browser/enterprise/browser_management/management_service_factory.cc
@@ -6,6 +6,7 @@
 
 #include "base/memory/singleton.h"
 #include "base/no_destructor.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/enterprise/browser_management/browser_management_service.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -14,6 +15,12 @@
 #include "components/policy/core/common/management/platform_management_service.h"
 #include "content/public/browser/browser_context.h"
 #include "extensions/buildflags/buildflags.h"
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/enterprise/browser_management/browser_management_status_provider.h"
+#endif
 
 namespace policy {
 
@@ -25,7 +32,20 @@
 
 // static
 ManagementService* ManagementServiceFactory::GetForPlatform() {
-  return PlatformManagementService::GetInstance();
+  auto* instance = PlatformManagementService::GetInstance();
+  // This has to be done here since `DeviceManagementStatusProvider` cannot be
+  // defined in `components/policy/`, also we need we need the
+  // `g_browser_process->platform_part()`.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (!instance->has_cros_status_provider() && g_browser_process &&
+      g_browser_process->platform_part()) {
+    instance->AddChromeOsStatusProvider(
+        std::make_unique<DeviceManagementStatusProvider>(
+            g_browser_process->platform_part()
+                ->browser_policy_connector_ash()));
+  }
+#endif
+  return instance;
 }
 
 // static
diff --git a/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc b/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc
index da1e4f8..2a6b65c 100644
--- a/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc
+++ b/chrome/browser/enterprise/connectors/enterprise_connectors_policy_handler_unittest.cc
@@ -131,7 +131,7 @@
 
   auto* value_set_in_map = policy_map.GetValue(kPolicyName);
   if (value_set_in_map) {
-    ASSERT_TRUE(value_set_in_map->Equals(value_set_in_pref));
+    ASSERT_EQ(*value_set_in_map, *value_set_in_pref);
     if (policy_scope())
       ASSERT_EQ(policy::POLICY_SCOPE_MACHINE, pref_scope);
   } else {
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
index 38c4681..c5a6b1a 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -99,16 +99,15 @@
 void ChromeExtensionsAPIClient::AddAdditionalValueStoreCaches(
     content::BrowserContext* context,
     const scoped_refptr<value_store::ValueStoreFactory>& factory,
-    const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>&
-        observers,
+    SettingsChangedCallback observer,
     std::map<settings_namespace::Namespace, ValueStoreCache*>* caches) {
   // Add support for chrome.storage.sync.
   (*caches)[settings_namespace::SYNC] =
-      new SyncValueStoreCache(factory, observers, context->GetPath());
+      new SyncValueStoreCache(factory, observer, context->GetPath());
 
   // Add support for chrome.storage.managed.
   (*caches)[settings_namespace::MANAGED] =
-      new ManagedValueStoreCache(context, factory, observers);
+      new ManagedValueStoreCache(context, factory, observer);
 }
 
 void ChromeExtensionsAPIClient::AttachWebContentsHelpers(
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.h b/chrome/browser/extensions/api/chrome_extensions_api_client.h
index 2cca3e8e..adf68a63 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client.h
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client.h
@@ -29,8 +29,7 @@
   void AddAdditionalValueStoreCaches(
       content::BrowserContext* context,
       const scoped_refptr<value_store::ValueStoreFactory>& factory,
-      const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>&
-          observers,
+      SettingsChangedCallback observer,
       std::map<settings_namespace::Namespace, ValueStoreCache*>* caches)
       override;
   void AttachWebContentsHelpers(content::WebContents* web_contents) const
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc
index 4126629f..d26484d 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -1140,7 +1140,7 @@
         *it, off_record
                  ? profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
                  : profile->GetOriginalProfile()));
-    json_results->Append(std::move(json_item));
+    json_results->Append(base::Value::FromUniquePtrValue(std::move(json_item)));
   }
   RecordApiFunctions(DOWNLOADS_FUNCTION_SEARCH);
   return RespondNow(
@@ -1928,10 +1928,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!EventRouter::Get(profile_))
     return;
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
-  args->Append(std::move(arg));
+  base::ListValue args;
+  args.Append(base::Value::FromUniquePtrValue(std::move(arg)));
   std::string json_args;
-  base::JSONWriter::Write(*args, &json_args);
+  base::JSONWriter::Write(args, &json_args);
   // The downloads system wants to share on-record events with off-record
   // extension renderers even in incognito_split_mode because that's how
   // chrome://downloads works. The "restrict_to_profile" mechanism does not
@@ -1945,7 +1945,7 @@
       (include_incognito && !profile_->IsOffTheRecord()) ? nullptr
                                                          : profile_.get();
   auto event = std::make_unique<Event>(histogram_value, event_name,
-                                       std::move(*args).TakeListDeprecated(),
+                                       std::move(args).TakeListDeprecated(),
                                        restrict_to_browser_context);
   event->will_dispatch_callback = std::move(will_dispatch_callback);
   EventRouter::Get(profile_)->BroadcastEvent(std::move(event));
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
index ff8f886..60cdfc7 100644
--- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
+++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
@@ -245,13 +245,13 @@
   }
   DCHECK(result->is_certificates());
 
-  auto client_certs = std::make_unique<base::ListValue>();
+  base::Value::List client_certs;
   for (std::vector<uint8_t>& cert : result->get_certificates()) {
-    client_certs->Append(std::make_unique<base::Value>(std::move(cert)));
+    client_certs.Append(base::Value(std::move(cert)));
   }
 
   auto results = std::make_unique<base::ListValue>();
-  results->Append(std::move(client_certs));
+  results->Append(base::Value(std::move(client_certs)));
   Respond(ArgumentList(std::move(results)));
 }
 
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
index cc6eb41..24607aa 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -166,7 +166,7 @@
                               segments, &error)) {
     std::unique_ptr<base::ListValue> results =
         std::make_unique<base::ListValue>();
-    results->Append(std::make_unique<base::Value>(false));
+    results->Append(false);
     return RespondNow(ErrorWithArguments(
         std::move(results), InformativeError(error, static_function_name())));
   }
@@ -187,7 +187,7 @@
                           &error)) {
     std::unique_ptr<base::ListValue> results =
         std::make_unique<base::ListValue>();
-    results->Append(std::make_unique<base::Value>(false));
+    results->Append(false);
     return RespondNow(ErrorWithArguments(
         std::move(results), InformativeError(error, static_function_name())));
   }
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
index 8d09cba..9a0028bc 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -982,7 +982,7 @@
   bool success = engine->ClearComposition(params.context_id, &error);
   std::unique_ptr<base::ListValue> results =
       std::make_unique<base::ListValue>();
-  results->Append(std::make_unique<base::Value>(success));
+  results->Append(success);
   return RespondNow(success
                         ? ArgumentList(std::move(results))
                         : ErrorWithArguments(
@@ -1076,7 +1076,7 @@
       !engine->SetCandidateWindowVisible(*properties.visible, &error)) {
     std::unique_ptr<base::ListValue> results =
         std::make_unique<base::ListValue>();
-    results->Append(std::make_unique<base::Value>(false));
+    results->Append(false);
     return RespondNow(ErrorWithArguments(
         std::move(results), InformativeError(error, static_function_name())));
   }
@@ -1169,7 +1169,7 @@
       engine->SetCandidates(params.context_id, candidates_out, &error);
   std::unique_ptr<base::ListValue> results =
       std::make_unique<base::ListValue>();
-  results->Append(std::make_unique<base::Value>(success));
+  results->Append(success);
   return RespondNow(success
                         ? ArgumentList(std::move(results))
                         : ErrorWithArguments(
@@ -1194,7 +1194,7 @@
       engine->SetCursorPosition(params.context_id, params.candidate_id, &error);
   std::unique_ptr<base::ListValue> results =
       std::make_unique<base::ListValue>();
-  results->Append(std::make_unique<base::Value>(success));
+  results->Append(success);
   return RespondNow(success
                         ? ArgumentList(std::move(results))
                         : ErrorWithArguments(
@@ -1324,18 +1324,17 @@
   if (!engine)
     return RespondNow(Error(InformativeError(error, static_function_name())));
 
-  auto bounds_list = std::make_unique<base::ListValue>();
+  base::Value::List bounds_list;
   for (const auto& bounds : engine->composition_bounds()) {
-    auto bounds_value = std::make_unique<base::DictionaryValue>();
-    bounds_value->SetIntKey("x", bounds.x());
-    bounds_value->SetIntKey("y", bounds.y());
-    bounds_value->SetIntKey("w", bounds.width());
-    bounds_value->SetIntKey("h", bounds.height());
-    bounds_list->Append(std::move(bounds_value));
+    base::Value::Dict bounds_value;
+    bounds_value.Set("x", bounds.x());
+    bounds_value.Set("y", bounds.y());
+    bounds_value.Set("w", bounds.width());
+    bounds_value.Set("h", bounds.height());
+    bounds_list.Append(std::move(bounds_value));
   }
 
-  return RespondNow(
-      OneArgument(base::Value::FromUniquePtrValue(std::move(bounds_list))));
+  return RespondNow(OneArgument(base::Value(std::move(bounds_list))));
 }
 
 void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
diff --git a/chrome/browser/extensions/api/scripting/scripting_api.cc b/chrome/browser/extensions/api/scripting/scripting_api.cc
index 4cdb29e..4343e0c 100644
--- a/chrome/browser/extensions/api/scripting/scripting_api.cc
+++ b/chrome/browser/extensions/api/scripting/scripting_api.cc
@@ -305,6 +305,62 @@
   return permissions.CanAccessPage(committed_url, tab_id, error);
 }
 
+// Collects the frames for injection. Method will return false if an error is
+// encountered.
+bool CollectFramesForInjection(const api::scripting::InjectionTarget& target,
+                               content::WebContents* tab,
+                               std::set<int>& frame_ids,
+                               std::set<content::RenderFrameHost*>& frames,
+                               std::string* error_out) {
+  if (target.document_ids) {
+    for (const auto& id : *target.document_ids) {
+      ExtensionApiFrameIdMap::DocumentId document_id =
+          ExtensionApiFrameIdMap::DocumentIdFromString(id);
+
+      if (!document_id) {
+        *error_out = base::StringPrintf("Invalid document id %s", id.c_str());
+        return false;
+      }
+
+      content::RenderFrameHost* frame =
+          ExtensionApiFrameIdMap::Get()->GetRenderFrameHostByDocumentId(
+              document_id);
+
+      // If the frame was not found or it matched another tab reject this
+      // request.
+      if (!frame || content::WebContents::FromRenderFrameHost(frame) != tab) {
+        *error_out =
+            base::StringPrintf("No document with id %s in tab with id %d",
+                               id.c_str(), target.tab_id);
+        return false;
+      }
+
+      // Convert the documentId into a frameId since the content will be
+      // injected synchronously.
+      frame_ids.insert(ExtensionApiFrameIdMap::GetFrameId(frame));
+      frames.insert(frame);
+    }
+  } else {
+    if (target.frame_ids) {
+      frame_ids.insert(target.frame_ids->begin(), target.frame_ids->end());
+    } else {
+      frame_ids.insert(ExtensionApiFrameIdMap::kTopFrameId);
+    }
+
+    for (int frame_id : frame_ids) {
+      content::RenderFrameHost* frame =
+          ExtensionApiFrameIdMap::GetRenderFrameHostById(tab, frame_id);
+      if (!frame) {
+        *error_out = base::StringPrintf("No frame with id %d in tab with id %d",
+                                        frame_id, target.tab_id);
+        return false;
+      }
+      frames.insert(frame);
+    }
+  }
+  return true;
+}
+
 // Returns true if the `target` can be accessed with the given `permissions`.
 // If the target can be accessed, populates `script_executor_out`,
 // `frame_scope_out`, and `frame_ids_out` with the appropriate values;
@@ -327,8 +383,16 @@
     return false;
   }
 
-  if ((target.all_frames && *target.all_frames == true) && target.frame_ids) {
-    *error_out = "Cannot specify both 'allFrames' and 'frameIds'.";
+  if ((target.all_frames && *target.all_frames == true) &&
+      (target.frame_ids || target.document_ids)) {
+    *error_out =
+        "Cannot specify 'allFrames' if either 'frameIds' or 'documentIds' is "
+        "specified.";
+    return false;
+  }
+
+  if (target.frame_ids && target.document_ids) {
+    *error_out = "Cannot specify both 'frameIds' and 'documentIds'.";
     return false;
   }
 
@@ -341,25 +405,15 @@
           : ScriptExecutor::SPECIFIED_FRAMES;
 
   std::set<int> frame_ids;
-  if (target.frame_ids) {
-    frame_ids.insert(target.frame_ids->begin(), target.frame_ids->end());
-  } else {
-    frame_ids.insert(ExtensionApiFrameIdMap::kTopFrameId);
-  }
+  std::set<content::RenderFrameHost*> frames;
+  if (!CollectFramesForInjection(target, tab, frame_ids, frames, error_out))
+    return false;
 
   // TODO(devlin): If `allFrames` is true, we error out if the extension
   // doesn't have access to the top frame (even if it may inject in child
   // frames). This is inconsistent with content scripts (which can execute on
   // child frames), but consistent with the old tabs.executeScript() API.
-  for (int frame_id : frame_ids) {
-    content::RenderFrameHost* frame =
-        ExtensionApiFrameIdMap::GetRenderFrameHostById(tab, frame_id);
-    if (!frame) {
-      *error_out = base::StringPrintf("No frame with id %d in tab with id %d",
-                                      frame_id, target.tab_id);
-      return false;
-    }
-
+  for (content::RenderFrameHost* frame : frames) {
     DCHECK_EQ(content::WebContents::FromRenderFrameHost(frame), tab);
     if (!HasPermissionToInjectIntoFrame(permissions, target.tab_id, frame,
                                         error_out)) {
@@ -627,6 +681,8 @@
     injection_result.result =
         base::Value::ToUniquePtrValue(std::move(result.value));
     injection_result.frame_id = result.frame_id;
+    if (result.document_id)
+      injection_result.document_id = result.document_id.ToString();
 
     // Put the top frame first; otherwise, any order.
     if (result.frame_id == ExtensionApiFrameIdMap::kTopFrameId) {
diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
index 02f22e0..6a075f54 100644
--- a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
+++ b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
@@ -231,12 +231,14 @@
 ManagedValueStoreCache::ManagedValueStoreCache(
     BrowserContext* context,
     scoped_refptr<value_store::ValueStoreFactory> factory,
-    scoped_refptr<SettingsObserverList> observers)
+    SettingsChangedCallback observer)
     : profile_(Profile::FromBrowserContext(context)),
       policy_domain_(GetPolicyDomain(profile_)),
       policy_service_(profile_->GetProfilePolicyConnector()->policy_service()),
       storage_factory_(std::move(factory)),
-      observers_(std::move(observers)) {
+      observer_(GetSequenceBoundSettingsChangedCallback(
+          base::SequencedTaskRunnerHandle::Get(),
+          std::move(observer))) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   policy_service_->AddObserver(policy_domain_, this);
@@ -363,7 +365,7 @@
   // Create the store now, and serve the cached policy until the PolicyService
   // sends updated values.
   std::unique_ptr<PolicyValueStore> store(new PolicyValueStore(
-      extension_id, observers_,
+      extension_id, observer_,
       value_store_util::CreateSettingsStore(settings_namespace::MANAGED,
                                             kManagedModelType, extension_id,
                                             storage_factory_)));
diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.h b/chrome/browser/extensions/api/storage/managed_value_store_cache.h
index 2903cbb..4c273eb 100644
--- a/chrome/browser/extensions/api/storage/managed_value_store_cache.h
+++ b/chrome/browser/extensions/api/storage/managed_value_store_cache.h
@@ -47,7 +47,7 @@
   // changes.
   ManagedValueStoreCache(content::BrowserContext* context,
                          scoped_refptr<value_store::ValueStoreFactory> factory,
-                         scoped_refptr<SettingsObserverList> observers);
+                         SettingsChangedCallback observer);
 
   ManagedValueStoreCache(const ManagedValueStoreCache&) = delete;
   ManagedValueStoreCache& operator=(const ManagedValueStoreCache&) = delete;
@@ -101,7 +101,7 @@
 
   // These live on the FILE thread.
   scoped_refptr<value_store::ValueStoreFactory> storage_factory_;
-  scoped_refptr<SettingsObserverList> observers_;
+  SequenceBoundSettingsChangedCallback observer_;
 
   // All the PolicyValueStores live on the FILE thread, and |store_map_| can be
   // accessed only on the FILE thread as well.
diff --git a/chrome/browser/extensions/api/storage/policy_value_store.cc b/chrome/browser/extensions/api/storage/policy_value_store.cc
index a38d2be..7f42fba0 100644
--- a/chrome/browser/extensions/api/storage/policy_value_store.cc
+++ b/chrome/browser/extensions/api/storage/policy_value_store.cc
@@ -29,10 +29,10 @@
 
 PolicyValueStore::PolicyValueStore(
     const std::string& extension_id,
-    scoped_refptr<SettingsObserverList> observers,
+    SequenceBoundSettingsChangedCallback observer,
     std::unique_ptr<ValueStore> delegate)
     : extension_id_(extension_id),
-      observers_(std::move(observers)),
+      observer_(std::move(observer)),
       delegate_(std::move(delegate)) {}
 
 PolicyValueStore::~PolicyValueStore() {}
@@ -99,10 +99,8 @@
   }
 
   if (!changes.empty()) {
-    observers_->Notify(
-        FROM_HERE, &SettingsObserver::OnSettingsChanged, extension_id_,
-        StorageAreaNamespace::kManaged,
-        value_store::ValueStoreChange::ToValue(std::move(changes)));
+    observer_->Run(extension_id_, StorageAreaNamespace::kManaged,
+                   value_store::ValueStoreChange::ToValue(std::move(changes)));
   }
 }
 
diff --git a/chrome/browser/extensions/api/storage/policy_value_store.h b/chrome/browser/extensions/api/storage/policy_value_store.h
index 562f9c4..e202a7c 100644
--- a/chrome/browser/extensions/api/storage/policy_value_store.h
+++ b/chrome/browser/extensions/api/storage/policy_value_store.h
@@ -29,7 +29,7 @@
 class PolicyValueStore : public value_store::ValueStore {
  public:
   PolicyValueStore(const std::string& extension_id,
-                   scoped_refptr<SettingsObserverList> observers,
+                   SequenceBoundSettingsChangedCallback observer,
                    std::unique_ptr<value_store::ValueStore> delegate);
 
   PolicyValueStore(const PolicyValueStore&) = delete;
@@ -65,7 +65,7 @@
 
  private:
   std::string extension_id_;
-  scoped_refptr<SettingsObserverList> observers_;
+  SequenceBoundSettingsChangedCallback observer_;
   std::unique_ptr<value_store::ValueStore> delegate_;
 };
 
diff --git a/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc b/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc
index 4fc6e48..7c2b390 100644
--- a/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc
+++ b/chrome/browser/extensions/api/storage/policy_value_store_unittest.cc
@@ -50,7 +50,7 @@
       value_store::ValueStoreChange::ToValue(std::move(changes)));
 }
 
-class MockSettingsObserver : public SettingsObserver {
+class MockSettingsObserver {
  public:
   MOCK_METHOD3(OnSettingsChangedJSON,
                void(const std::string& extension_id,
@@ -59,8 +59,9 @@
 
   void OnSettingsChanged(const std::string& extension_id,
                          StorageAreaNamespace storage_area,
-                         const base::Value& changes) override {
-    OnSettingsChangedJSON(extension_id, storage_area, ValueToJson(changes));
+                         base::Value changes) {
+    OnSettingsChangedJSON(extension_id, storage_area,
+                          ValueToJson(std::move(changes)));
   }
 };
 
@@ -70,11 +71,12 @@
 class MutablePolicyValueStore : public PolicyValueStore {
  public:
   explicit MutablePolicyValueStore(const base::FilePath& path)
-      : PolicyValueStore(kTestExtensionId,
-                         base::MakeRefCounted<SettingsObserverList>(),
-                         std::make_unique<value_store::LeveldbValueStore>(
-                             kDatabaseUMAClientName,
-                             path)) {}
+      : PolicyValueStore(
+            kTestExtensionId,
+            SequenceBoundSettingsChangedCallback(base::DoNothing()),
+            std::make_unique<value_store::LeveldbValueStore>(
+                kDatabaseUMAClientName,
+                path)) {}
 
   MutablePolicyValueStore(const MutablePolicyValueStore&) = delete;
   MutablePolicyValueStore& operator=(const MutablePolicyValueStore&) = delete;
@@ -120,18 +122,16 @@
 
   void SetUp() override {
     ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
-    observers_ = new SettingsObserverList();
-    observers_->AddObserver(&observer_);
     store_ = std::make_unique<PolicyValueStore>(
-        kTestExtensionId, observers_,
+        kTestExtensionId,
+        SequenceBoundSettingsChangedCallback(
+            base::BindRepeating(&MockSettingsObserver::OnSettingsChanged,
+                                base::Unretained(&observer_))),
         std::make_unique<value_store::LeveldbValueStore>(
             kDatabaseUMAClientName, scoped_temp_dir_.GetPath()));
   }
 
-  void TearDown() override {
-    observers_->RemoveObserver(&observer_);
-    store_.reset();
-  }
+  void TearDown() override { store_.reset(); }
 
  protected:
   void SetCurrentPolicy(const policy::PolicyMap& policies) {
@@ -151,7 +151,6 @@
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<PolicyValueStore> store_;
   NiceMock<MockSettingsObserver> observer_;
-  scoped_refptr<SettingsObserverList> observers_;
 };
 
 TEST_F(PolicyValueStoreTest, DontProvideRecommendedPolicies) {
diff --git a/chrome/browser/extensions/api/storage/sync_storage_backend.cc b/chrome/browser/extensions/api/storage/sync_storage_backend.cc
index 4919a65..961c263 100644
--- a/chrome/browser/extensions/api/storage/sync_storage_backend.cc
+++ b/chrome/browser/extensions/api/storage/sync_storage_backend.cc
@@ -52,12 +52,12 @@
 SyncStorageBackend::SyncStorageBackend(
     scoped_refptr<value_store::ValueStoreFactory> storage_factory,
     const SettingsStorageQuotaEnforcer::Limits& quota,
-    scoped_refptr<SettingsObserverList> observers,
+    SequenceBoundSettingsChangedCallback observer,
     syncer::ModelType sync_type,
     const syncer::SyncableService::StartSyncFlare& flare)
     : storage_factory_(std::move(storage_factory)),
       quota_(quota),
-      observers_(std::move(observers)),
+      observer_(std::move(observer)),
       sync_type_(sync_type),
       flare_(flare) {
   DCHECK(IsOnBackendSequence());
@@ -92,7 +92,7 @@
   // It's fine to create the quota enforcer underneath the sync layer, since
   // sync will only go ahead if each underlying storage operation succeeds.
   std::unique_ptr<SyncableSettingsStorage> syncable_storage(
-      new SyncableSettingsStorage(observers_, extension_id,
+      new SyncableSettingsStorage(observer_, extension_id,
                                   settings_storage.release(), sync_type_,
                                   flare_));
   SyncableSettingsStorage* raw_syncable_storage = syncable_storage.get();
diff --git a/chrome/browser/extensions/api/storage/sync_storage_backend.h b/chrome/browser/extensions/api/storage/sync_storage_backend.h
index 9c8a4c5..0691443e 100644
--- a/chrome/browser/extensions/api/storage/sync_storage_backend.h
+++ b/chrome/browser/extensions/api/storage/sync_storage_backend.h
@@ -39,7 +39,7 @@
   SyncStorageBackend(
       scoped_refptr<value_store::ValueStoreFactory> storage_factory,
       const SettingsStorageQuotaEnforcer::Limits& quota,
-      scoped_refptr<SettingsObserverList> observers,
+      SequenceBoundSettingsChangedCallback observer,
       syncer::ModelType sync_type,
       const syncer::SyncableService::StartSyncFlare& flare);
 
@@ -81,8 +81,8 @@
   // Quota limits (see SettingsStorageQuotaEnforcer).
   const SettingsStorageQuotaEnforcer::Limits quota_;
 
-  // The list of observers to settings changes.
-  const scoped_refptr<SettingsObserverList> observers_;
+  // Observer to settings changes.
+  SequenceBoundSettingsChangedCallback observer_;
 
   // A cache of ValueStore objects that have already been created.
   // Ensure that there is only ever one created per extension.
diff --git a/chrome/browser/extensions/api/storage/sync_value_store_cache.cc b/chrome/browser/extensions/api/storage/sync_value_store_cache.cc
index 6f2bf3bc..b4163012 100644
--- a/chrome/browser/extensions/api/storage/sync_value_store_cache.cc
+++ b/chrome/browser/extensions/api/storage/sync_value_store_cache.cc
@@ -37,7 +37,7 @@
 
 SyncValueStoreCache::SyncValueStoreCache(
     scoped_refptr<value_store::ValueStoreFactory> factory,
-    scoped_refptr<SettingsObserverList> observers,
+    SettingsChangedCallback observer,
     const base::FilePath& profile_path)
     : initialized_(false) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -48,7 +48,10 @@
   GetBackendTaskRunner()->PostTask(
       FROM_HERE, base::BindOnce(&SyncValueStoreCache::InitOnBackend,
                                 base::Unretained(this), std::move(factory),
-                                std::move(observers), profile_path));
+                                GetSequenceBoundSettingsChangedCallback(
+                                    base::SequencedTaskRunnerHandle::Get(),
+                                    std::move(observer)),
+                                profile_path));
 }
 
 SyncValueStoreCache::~SyncValueStoreCache() {
@@ -93,15 +96,15 @@
 
 void SyncValueStoreCache::InitOnBackend(
     scoped_refptr<value_store::ValueStoreFactory> factory,
-    scoped_refptr<SettingsObserverList> observers,
+    SequenceBoundSettingsChangedCallback observer,
     const base::FilePath& profile_path) {
   DCHECK(IsOnBackendSequence());
   DCHECK(!initialized_);
   app_backend_ = std::make_unique<SyncStorageBackend>(
-      factory, GetSyncQuotaLimits(), observers, syncer::APP_SETTINGS,
+      factory, GetSyncQuotaLimits(), observer, syncer::APP_SETTINGS,
       sync_start_util::GetFlareForSyncableService(profile_path));
   extension_backend_ = std::make_unique<SyncStorageBackend>(
-      std::move(factory), GetSyncQuotaLimits(), std::move(observers),
+      std::move(factory), GetSyncQuotaLimits(), std::move(observer),
       syncer::EXTENSION_SETTINGS,
       sync_start_util::GetFlareForSyncableService(profile_path));
   initialized_ = true;
diff --git a/chrome/browser/extensions/api/storage/sync_value_store_cache.h b/chrome/browser/extensions/api/storage/sync_value_store_cache.h
index 3e603e3..8b264e5 100644
--- a/chrome/browser/extensions/api/storage/sync_value_store_cache.h
+++ b/chrome/browser/extensions/api/storage/sync_value_store_cache.h
@@ -35,7 +35,7 @@
 class SyncValueStoreCache : public ValueStoreCache {
  public:
   SyncValueStoreCache(scoped_refptr<value_store::ValueStoreFactory> factory,
-                      scoped_refptr<SettingsObserverList> observers,
+                      SettingsChangedCallback observer,
                       const base::FilePath& profile_path);
 
   SyncValueStoreCache(const SyncValueStoreCache&) = delete;
@@ -54,7 +54,7 @@
 
  private:
   void InitOnBackend(scoped_refptr<value_store::ValueStoreFactory> factory,
-                     scoped_refptr<SettingsObserverList> observers,
+                     SequenceBoundSettingsChangedCallback observer,
                      const base::FilePath& profile_path);
 
   bool initialized_;
diff --git a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc b/chrome/browser/extensions/api/storage/syncable_settings_storage.cc
index 668018b..f43309e 100644
--- a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc
+++ b/chrome/browser/extensions/api/storage/syncable_settings_storage.cc
@@ -22,12 +22,12 @@
 namespace extensions {
 
 SyncableSettingsStorage::SyncableSettingsStorage(
-    scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>> observers,
+    SequenceBoundSettingsChangedCallback observer,
     const std::string& extension_id,
     ValueStore* delegate,
     syncer::ModelType sync_type,
     const syncer::SyncableService::StartSyncFlare& flare)
-    : observers_(std::move(observers)),
+    : observer_(std::move(observer)),
       extension_id_(extension_id),
       delegate_(delegate),
       sync_type_(sync_type),
@@ -332,10 +332,8 @@
 
   sync_processor_->NotifyChanges(changes);
 
-  observers_->Notify(
-      FROM_HERE, &SettingsObserver::OnSettingsChanged, extension_id_,
-      StorageAreaNamespace::kSync,
-      value_store::ValueStoreChange::ToValue(std::move(changes)));
+  observer_->Run(extension_id_, StorageAreaNamespace::kSync,
+                 value_store::ValueStoreChange::ToValue(std::move(changes)));
 
   // TODO(kalman): Something sensible with multiple errors.
   if (errors.empty())
diff --git a/chrome/browser/extensions/api/storage/syncable_settings_storage.h b/chrome/browser/extensions/api/storage/syncable_settings_storage.h
index 4bf52b908..15d9abc 100644
--- a/chrome/browser/extensions/api/storage/syncable_settings_storage.h
+++ b/chrome/browser/extensions/api/storage/syncable_settings_storage.h
@@ -31,7 +31,7 @@
 // Decorates a ValueStore with sync behaviour.
 class SyncableSettingsStorage : public value_store::ValueStore {
  public:
-  SyncableSettingsStorage(scoped_refptr<SettingsObserverList> observers,
+  SyncableSettingsStorage(SequenceBoundSettingsChangedCallback observer,
                           const std::string& extension_id,
                           // Ownership taken.
                           value_store::ValueStore* delegate,
@@ -114,8 +114,8 @@
                                  std::unique_ptr<base::Value> old_value,
                                  value_store::ValueStoreChangeList* changes);
 
-  // List of observers to settings changes.
-  const scoped_refptr<SettingsObserverList> observers_;
+  // Observer to settings changes.
+  SequenceBoundSettingsChangedCallback observer_;
 
   // Id of the extension these settings are for.
   std::string const extension_id_;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 15ce5b9..b797f4e8 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -56,7 +56,7 @@
   {
     "name": "add-passwords-in-settings",
     "owners": [ "vidhanj", "mamir", "lizapopova" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 103
   },
   {
     "name": "add-to-homescreen-iph",
@@ -212,7 +212,7 @@
   {
     "name": "arc-file-picker-experiment",
     "owners": [ "youkichihosoi", "risan", "niwa" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "arc-ghost-window",
@@ -1139,7 +1139,7 @@
   {
     "name": "default-wkwebview-context-menu",
     "owners": [ "gambard", "bling-flags@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 105
   },
   {
     "name": "deferred-font-shaping",
@@ -1513,7 +1513,7 @@
   {
     "name": "enable-app-discovery-for-oobe",
     "owners": [ "melzhang", "tsergeant", "chromeos-apps-foundation-team" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "enable-app-provisioning-static-server",
@@ -1623,6 +1623,11 @@
     "expiry_milestone": 99
   },
   {
+    "name": "enable-cast-remoting-query-blocklist",
+    "owners": [ "rwkeane@google.com", "openscreen-eng@google.com" ],
+    "expiry_milestone": 115
+  },
+  {
     "name": "enable-cast-streaming-av1",
     "owners": [ "jophba@google.com", "openscreen-eng@google.com" ],
     "expiry_milestone": 105
@@ -2138,12 +2143,12 @@
   {
     "name": "enable-fre-default-browser-screen-testing",
     "owners": [ "alionadangla", "gambard" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 105
   },
   {
     "name": "enable-fre-ui-module-ios-with-options",
     "owners": [ "gambard", "veronguyen", "vincb" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 102
   },
   {
     "name": "enable-fullscreen-api",
@@ -2344,7 +2349,7 @@
     "expiry_milestone": 90
   },
   {
-    "name": "enable-lens-region-search",
+    "name": "enable-lens-standalone",
     "owners": [ "stanfield@google.com", "benwgold@google.com", "juanmojica@google.com" ],
     "expiry_milestone": 103
   },
@@ -3233,7 +3238,7 @@
   {
     "name": "expanded-tab-strip",
     "owners": [ "rkgibson@google.com", "gambard", "bling-flags@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 105
   },
   {
     "name": "explore-sites",
@@ -3413,7 +3418,7 @@
   {
     "name": "force-control-face-ae",
     "owners": [ "chromeos-camera-eng@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "force-disable-extended-sync-promos",
@@ -3428,6 +3433,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "force-enable-cast-remoting-query",
+    "owners": [ "rwkeane@google.com", "openscreen-eng@google.com" ],
+    "expiry_milestone": 115
+  },
+  {
     "name": "force-major-version-to-minor",
     "owners": [ "brgoldstein", "potassium-katabolism@google.com" ],
     "expiry_milestone": 110
@@ -3759,6 +3769,11 @@
     "expiry_milestone": 95
   },
   {
+    "name": "intent-chip-skips-intent-picker",
+    "owners": ["tsergeant", "chromeos-apps-foundation-team@google.com"],
+    "expiry_milestone": 105
+  },
+  {
     "name": "interest-feed-notice-card-auto-dismiss",
     "owners": [ "//chrome/android/feed/OWNERS", "feed@chromium.org", "edchin@chromium.org" ],
     "expiry_milestone": 95
@@ -3979,6 +3994,11 @@
     "expiry_milestone": 104
   },
   {
+    "name": "link-capturing-infobar",
+    "owners": ["tsergeant", "chromeos-apps-foundation-team@google.com"],
+    "expiry_milestone": 105
+  },
+  {
     "name": "link-capturing-ui-update",
     "owners": ["tsergeant", "chromeos-apps-foundation-team@google.com"],
     "expiry_milestone": 105
@@ -4003,6 +4023,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "location-bar-model-optimizations",
+    "owners": [ "pnoland@google.com"],
+    "expiry_milestone": 105
+  },
+  {
     "name": "mac-syscall-sandbox",
     "owners": [ "kerrnel@google.com" ],
     "expiry_milestone": 88
@@ -4780,7 +4805,12 @@
   {
     "name": "partitioned-cookies",
     "owners": [ "dylancutler" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
+  },
+  {
+    "name": "partitioned-cookies-bypass-origin-trial",
+    "owners": [ "dylancutler" ],
+    "expiry_milestone": 110
   },
   {
     "name": "password-change-in-settings",
@@ -4817,7 +4847,7 @@
   {
     "name": "passwords-account-storage-revised-opt-in-flow",
     "owners": [ "mamir", "treib" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 103
   },
   {
     "name": "pcie-billboard-notification",
@@ -4851,28 +4881,28 @@
   },
   {
     "name": "permission-chip",
-    "owners": [ "bsep", "engedy", "olesiamarukhno@google.com" ],
-    "expiry_milestone": 100
+    "owners": [ "elklm", "bsep", "engedy", "olesiamarukhno@google.com" ],
+    "expiry_milestone": 110
   },
   {
     "name": "permission-chip-gesture",
-    "owners": [ "bsep", "engedy", "olesiamarukhno@google.com" ],
-    "expiry_milestone": 100
+    "owners": [ "elklm", "bsep", "engedy", "olesiamarukhno@google.com" ],
+    "expiry_milestone": 110
   },
   {
     "name": "permission-chip-request-type",
-    "owners": [ "bsep", "engedy", "olesiamarukhno@google.com" ],
-    "expiry_milestone": 100
+    "owners": [ "elklm", "bsep", "engedy", "olesiamarukhno@google.com" ],
+    "expiry_milestone": 110
   },
   {
     "name": "permission-predictions",
     "owners": [ "andypaicu", "engedy", "ravjit", "mkwst", "hkamila", "elklm" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "permission-quiet-chip",
     "owners": [ "elklm", "engedy"],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "persist-share-hub-on-app-switch",
@@ -5565,7 +5595,7 @@
   {
     "name": "smart-lock-ui-revamp",
     "owners": [ "cclem", "better-together-dev@google.com" ],
-    "expiry_milestone": 101
+    "expiry_milestone": 103
   },
   {
     "name": "smart-suggestion-for-large-downloads",
@@ -6162,7 +6192,7 @@
   {
     "name": "web-otp-backend",
     "owners": [ "yigu" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 105
   },
   {
     "name": "web-share",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 11592fe5..4d22ac78 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1200,13 +1200,11 @@
 const char kEnableAutomaticSnoozeDescription[] =
     "Enables automatic snoozing on In-Product Help with no snooze button.";
 
-const char kEnableLensRegionSearchFlagId[] = "enable-lens-region-search";
-const char kEnableLensRegionSearchName[] =
-    "Search your screen with Google Lens";
-const char kEnableLensRegionSearchDescription[] =
-    "Right click and select \"Search images with Google Lens\" to "
-    "search any region of the site to learn more about the visual content you "
-    "see while you browse and shop on the web.";
+const char kEnableLensStandaloneFlagId[] = "enable-lens-standalone";
+const char kEnableLensStandaloneName[] = "Enable Lens features in Chrome.";
+const char kEnableLensStandaloneDescription[] =
+    "Enables Lens image and region search to learn about the visual content "
+    "you see while you browse and shop on the web.";
 
 const char kEnableLoginDetectionName[] = "Enable login detection";
 const char kEnableLoginDetectionDescription[] =
@@ -1439,6 +1437,19 @@
     "Enables the inclusion of VP9 codec video encoding in Cast mirroring "
     "session negotiations.";
 
+const char kCastUseBlocklistForRemotingQueryName[] =
+    "Use blocklist for controlling remoting capabilities queries";
+const char kCastUseBlocklistForRemotingQueryDescription[] =
+    "Enables the use of the hard-coded blocklist for controlling whether a "
+    "device should be queried for remoting capabilities when configuring a "
+    "mirroring session.";
+
+const char kCastForceEnableRemotingQueryName[] =
+    "Force enable remoting capabilities queries";
+const char kCastForceEnableRemotingQueryDescription[] =
+    "Enables querying for remoting capabilities for ALL devices, regardless of "
+    "the contents of the allowlist or blocklist.";
+
 const char kGoogleLensSdkIntentName[] =
     "Enable the use of the Lens SDK when starting intent into Lens.";
 const char kGoogleLensSdkIntentDescription[] =
@@ -1613,6 +1624,11 @@
     "Enable an entry point to Google Lens to allow users to search what they "
     "see using their mobile camera.";
 
+const char kLocationBarModelOptimizationsName[] =
+    "LocationBarModel optimizations";
+const char kLocationBarModelOptimizationsDescription[] =
+    "Cache commonly used data in LocationBarModel to improve performance";
+
 const char kLogJsConsoleMessagesName[] =
     "Log JS console messages in system logs";
 const char kLogJsConsoleMessagesDescription[] =
@@ -2244,6 +2260,12 @@
 const char kPartitionedCookiesName[] = "Partitioned cookies";
 const char kPartitionedCookiesDescription[] =
     "Controls if the Partitioned cookie attribute is enabled.";
+const char kPartitionedCookiesBypassOriginTrialName[] =
+    "Partitioned cookies: bypass origin trial";
+const char kPartitionedCookiesBypassOriginTrialDescription[] =
+    "If this flag is enabled, Chrome will not require a site to opt into the "
+    "origin trial in order to send or receive cookies set with the Partitioned "
+    "attribute.";
 
 const char kScrollableTabStripFlagId[] = "scrollable-tabstrip";
 const char kScrollableTabStripName[] = "Tab Scrolling";
@@ -4525,40 +4547,6 @@
 const char kDriveFsBidirectionalNativeMessagingDescription[] =
     "Enable enhanced native messaging host to communicate with DriveFS.";
 
-const char kCrOSDspBasedAecAllowedName[] =
-    "Allow CRAS to use a DSP-based AEC if available";
-const char kCrOSDspBasedAecAllowedDescription[] =
-    "Allows the system variant of the AEC in CRAS to be run on DSP ";
-
-const char kCrOSDspBasedNsAllowedName[] =
-    "Allow CRAS to use a DSP-based NS if available";
-const char kCrOSDspBasedNsAllowedDescription[] =
-    "Allows the system variant of the NS in CRAS to be run on DSP ";
-
-const char kCrOSDspBasedAgcAllowedName[] =
-    "Allow CRAS to use a DSP-based AGC if available";
-const char kCrOSDspBasedAgcAllowedDescription[] =
-    "Allows the system variant of the AGC in CRAS to be run on DSP ";
-
-const char kCrOSEnforceSystemAecName[] = "Enforce using the system AEC in CrAS";
-const char kCrOSEnforceSystemAecDescription[] =
-    "Enforces using the system variant in CrAS of the AEC";
-
-const char kCrOSEnforceSystemAecAgcName[] =
-    "Enforce using the system AEC and AGC in CrAS";
-const char kCrOSEnforceSystemAecAgcDescription[] =
-    "Enforces using the system variants in CrAS of the AEC and AGC.";
-
-const char kCrOSEnforceSystemAecNsName[] =
-    "Enforce using the system AEC and NS in CrAS";
-const char kCrOSEnforceSystemAecNsDescription[] =
-    "Enforces using the system variants in CrAS of the AEC and NS.";
-
-const char kCrOSEnforceSystemAecNsAgcName[] =
-    "Enforce using the system AEC, NS and AGC in CrAS";
-const char kCrOSEnforceSystemAecNsAgcDescription[] =
-    "Enforces using the system variants in CrAS of the AEC, NS and AGC.";
-
 const char kEnableAppReinstallZeroStateName[] =
     "Enable Zero State App Reinstall Suggestions.";
 const char kEnableAppReinstallZeroStateDescription[] =
@@ -5560,6 +5548,40 @@
     "scanners that filter low energy advertisements in a power-efficient "
     "manner.";
 
+const char kCrOSDspBasedAecAllowedName[] =
+    "Allow CRAS to use a DSP-based AEC if available";
+const char kCrOSDspBasedAecAllowedDescription[] =
+    "Allows the system variant of the AEC in CRAS to be run on DSP ";
+
+const char kCrOSDspBasedNsAllowedName[] =
+    "Allow CRAS to use a DSP-based NS if available";
+const char kCrOSDspBasedNsAllowedDescription[] =
+    "Allows the system variant of the NS in CRAS to be run on DSP ";
+
+const char kCrOSDspBasedAgcAllowedName[] =
+    "Allow CRAS to use a DSP-based AGC if available";
+const char kCrOSDspBasedAgcAllowedDescription[] =
+    "Allows the system variant of the AGC in CRAS to be run on DSP ";
+
+const char kCrOSEnforceSystemAecName[] = "Enforce using the system AEC in CrAS";
+const char kCrOSEnforceSystemAecDescription[] =
+    "Enforces using the system variant in CrAS of the AEC";
+
+const char kCrOSEnforceSystemAecAgcName[] =
+    "Enforce using the system AEC and AGC in CrAS";
+const char kCrOSEnforceSystemAecAgcDescription[] =
+    "Enforces using the system variants in CrAS of the AEC and AGC.";
+
+const char kCrOSEnforceSystemAecNsName[] =
+    "Enforce using the system AEC and NS in CrAS";
+const char kCrOSEnforceSystemAecNsDescription[] =
+    "Enforces using the system variants in CrAS of the AEC and NS.";
+
+const char kCrOSEnforceSystemAecNsAgcName[] =
+    "Enforce using the system AEC, NS and AGC in CrAS";
+const char kCrOSEnforceSystemAecNsAgcDescription[] =
+    "Enforces using the system variants in CrAS of the AEC, NS and AGC.";
+
 const char kDefaultCalculatorWebAppName[] = "Default install Calculator PWA";
 const char kDefaultCalculatorWebAppDescription[] =
     "Enable default installing of the calculator PWA instead of the deprecated "
@@ -5580,6 +5602,20 @@
 const char kEnableTtsLacrosSupportDescription[] =
     "Enable or disable lacros support for text to speech.";
 
+const char kIntentChipSkipsPickerName[] =
+    "Link capturing intent chip skips the intent picker bubble";
+const char kIntentChipSkipsPickerDescription[] =
+    "When enabled, clicking the intent chip in the Omnibox will skip the "
+    "intent picker bubble and launch directly into the app. If multiple apps "
+    "are installed for a URL, the intent picker will still be shown for "
+    "disambigation.";
+
+const char kLinkCapturingInfoBarName[] = "Enable link capturing info bar";
+const char kLinkCapturingInfoBarDescription[] =
+    "Enables an info bar which appears when launching a web app through the "
+    "Omnibox intent chip, prompting to update the link capturing setting for "
+    "that app.";
+
 const char kLinkCapturingUiUpdateName[] = "Enable updated link capturing UI";
 const char kLinkCapturingUiUpdateDescription[] =
     "Enables updated UI for link capturing flows from the browser to apps, "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 6a4f2c7..0b782bd32 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -650,9 +650,9 @@
 extern const char kEnableGamepadButtonAxisEventsName[];
 extern const char kEnableGamepadButtonAxisEventsDescription[];
 
-extern const char kEnableLensRegionSearchFlagId[];
-extern const char kEnableLensRegionSearchName[];
-extern const char kEnableLensRegionSearchDescription[];
+extern const char kEnableLensStandaloneFlagId[];
+extern const char kEnableLensStandaloneName[];
+extern const char kEnableLensStandaloneDescription[];
 
 extern const char kEnableLoginDetectionName[];
 extern const char kEnableLoginDetectionDescription[];
@@ -809,6 +809,12 @@
 extern const char kCastStreamingVp9Name[];
 extern const char kCastStreamingVp9Description[];
 
+extern const char kCastUseBlocklistForRemotingQueryName[];
+extern const char kCastUseBlocklistForRemotingQueryDescription[];
+
+extern const char kCastForceEnableRemotingQueryName[];
+extern const char kCastForceEnableRemotingQueryDescription[];
+
 extern const char kGoogleLensSdkIntentName[];
 extern const char kGoogleLensSdkIntentDescription[];
 
@@ -918,6 +924,9 @@
 extern const char kLensCameraAssistedSearchName[];
 extern const char kLensCameraAssistedSearchDescription[];
 
+extern const char kLocationBarModelOptimizationsName[];
+extern const char kLocationBarModelOptimizationsDescription[];
+
 extern const char kLogJsConsoleMessagesName[];
 extern const char kLogJsConsoleMessagesDescription[];
 
@@ -1268,6 +1277,9 @@
 
 extern const char kPartitionedCookiesName[];
 extern const char kPartitionedCookiesDescription[];
+// TODO(crbug.com/1296161): Remove this when the CHIPS OT ends.
+extern const char kPartitionedCookiesBypassOriginTrialName[];
+extern const char kPartitionedCookiesBypassOriginTrialDescription[];
 
 extern const char kScrollableTabStripFlagId[];
 extern const char kScrollableTabStripName[];
@@ -2594,27 +2606,6 @@
 extern const char kDriveFsBidirectionalNativeMessagingName[];
 extern const char kDriveFsBidirectionalNativeMessagingDescription[];
 
-extern const char kCrOSDspBasedAecAllowedName[];
-extern const char kCrOSDspBasedAecAllowedDescription[];
-
-extern const char kCrOSDspBasedNsAllowedName[];
-extern const char kCrOSDspBasedNsAllowedDescription[];
-
-extern const char kCrOSDspBasedAgcAllowedName[];
-extern const char kCrOSDspBasedAgcAllowedDescription[];
-
-extern const char kCrOSEnforceSystemAecName[];
-extern const char kCrOSEnforceSystemAecDescription[];
-
-extern const char kCrOSEnforceSystemAecAgcName[];
-extern const char kCrOSEnforceSystemAecAgcDescription[];
-
-extern const char kCrOSEnforceSystemAecNsName[];
-extern const char kCrOSEnforceSystemAecNsDescription[];
-
-extern const char kCrOSEnforceSystemAecNsAgcName[];
-extern const char kCrOSEnforceSystemAecNsAgcDescription[];
-
 extern const char kEnableAppReinstallZeroStateName[];
 extern const char kEnableAppReinstallZeroStateDescription[];
 
@@ -3200,6 +3191,27 @@
 extern const char kBluetoothAdvertisementMonitoringName[];
 extern const char kBluetoothAdvertisementMonitoringDescription[];
 
+extern const char kCrOSDspBasedAecAllowedName[];
+extern const char kCrOSDspBasedAecAllowedDescription[];
+
+extern const char kCrOSDspBasedNsAllowedName[];
+extern const char kCrOSDspBasedNsAllowedDescription[];
+
+extern const char kCrOSDspBasedAgcAllowedName[];
+extern const char kCrOSDspBasedAgcAllowedDescription[];
+
+extern const char kCrOSEnforceSystemAecName[];
+extern const char kCrOSEnforceSystemAecDescription[];
+
+extern const char kCrOSEnforceSystemAecAgcName[];
+extern const char kCrOSEnforceSystemAecAgcDescription[];
+
+extern const char kCrOSEnforceSystemAecNsName[];
+extern const char kCrOSEnforceSystemAecNsDescription[];
+
+extern const char kCrOSEnforceSystemAecNsAgcName[];
+extern const char kCrOSEnforceSystemAecNsAgcDescription[];
+
 extern const char kDefaultCalculatorWebAppName[];
 extern const char kDefaultCalculatorWebAppDescription[];
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -3214,6 +3226,12 @@
 extern const char kEnableTtsLacrosSupportName[];
 extern const char kEnableTtsLacrosSupportDescription[];
 
+extern const char kIntentChipSkipsPickerName[];
+extern const char kIntentChipSkipsPickerDescription[];
+
+extern const char kLinkCapturingInfoBarName[];
+extern const char kLinkCapturingInfoBarDescription[];
+
 extern const char kLinkCapturingUiUpdateName[];
 extern const char kLinkCapturingUiUpdateDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 2970736a..26dd0cc 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -244,6 +244,7 @@
     &kInstantStart,
     &kKitKatSupported,
     &kLensCameraAssistedSearch,
+    &kLocationBarModelOptimizations,
     &kNewWindowAppMenu,
     &kNotificationPermissionVariant,
     &kOfflineIndicatorV2,
@@ -349,10 +350,6 @@
     &password_manager::features::kTouchToFillPasswordSubmission,
     &password_manager::features::kUnifiedCredentialManagerDryRun,
     &password_manager::features::kUnifiedPasswordManagerAndroid,
-    &password_manager::features::kUnifiedPasswordManagerMigration,
-    &password_manager::features::kUnifiedPasswordManagerShadowAndroid,
-    &password_manager::features::
-        kUnifiedPasswordManagerShadowWriteOperationsAndroid,
     &performance_hints::features::kContextMenuPerformanceInfo,
     &policy::features::kChromeManagementPageAndroid,
     &privacy_sandbox::kPrivacySandboxSettings3,
@@ -668,6 +665,9 @@
 const base::Feature kKitKatSupported{"KitKatSupported",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kLocationBarModelOptimizations{
+    "LocationBarModelOptimizations", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kSearchEnginePromoExistingDevice{
     "SearchEnginePromo.ExistingDevice", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 2417ac1a..72b17bb0 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -110,6 +110,7 @@
 extern const base::Feature kKitKatSupported;
 extern const base::Feature kLanguagesPreference;
 extern const base::Feature kLensCameraAssistedSearch;
+extern const base::Feature kLocationBarModelOptimizations;
 extern const base::Feature kNewWindowAppMenu;
 extern const base::Feature kNotificationPermissionVariant;
 extern const base::Feature kOfflineIndicatorV2;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
index 2cb9f9c..655df93 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
@@ -312,6 +312,40 @@
         // TODO(crbug.com/1217708): Assert cached flags values are false/false.
     }
 
+    @Test
+    public void testMultipleStartCheckpoints_normalMode() {
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // There are no safe values.
+        // There are no cached flag values, so the defaults false/false are used.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertFalse(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertFalse(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCleanRun(true, true);
+        // Safe values became false/false.
+        // Cached values became true(flaky)/true.
+
+        startRun();
+        startRun();
+        startRun();
+        startRun();
+        // Crash streak is 0. Do not engage Safe Mode.
+        // Safe values are false/false.
+        // Cached flag values are true(flaky)/true.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+        assertTrue(CachedFeatureFlags.isEnabled(CRASHY_FEATURE));
+        assertTrue(CachedFeatureFlags.isEnabled(OK_FEATURE));
+        endCrashyRun();
+        // Cached values remain true(crashy)/true.
+
+        startRun();
+        // Crash streak is 1, despite the multiple startRun() calls above. Do not engage Safe Mode.
+        assertEquals(Behavior.NOT_ENGAGED_BELOW_THRESHOLD,
+                CachedFeatureFlags.getSafeModeBehaviorForTesting());
+    }
+
     private void startRun() {
         CachedFeatureFlags.isEnabled(CRASHY_FEATURE);
         CachedFeatureFlags.onStartOrResumeCheckpoint();
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
index d2b2d79..f56ae52 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
@@ -24,6 +24,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -63,6 +64,9 @@
 
     private AtomicInteger mBehavior = new AtomicInteger(Behavior.UNKNOWN);
 
+    private AtomicBoolean mStartCheckpointWritten = new AtomicBoolean(false);
+    private AtomicBoolean mEndCheckpointWritten = new AtomicBoolean(false);
+
     CachedFlagsSafeMode() {}
 
     /**
@@ -98,11 +102,20 @@
     }
 
     /**
-     * Call at an early point in the path that leads to caching flags. If onFinishedCachingFlags()
+     * Call at an early point in the path that leads to caching flags. If onEndCheckpoint()
      * does not get called before the next run, this run will be considered a crash for purposes of
      * counting the crash streak and entering Safe Mode.
      */
     public void onStartOrResumeCheckpoint() {
+        if (mEndCheckpointWritten.get()) {
+            // Do not increment the streak if it was already reset.
+            return;
+        }
+        if (mStartCheckpointWritten.getAndSet(true)) {
+            // Limit to one increment per run.
+            return;
+        }
+
         SharedPreferencesManager.getInstance().incrementInt(
                 ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE);
         RecordHistogram.recordEnumeratedHistogram(
@@ -114,6 +127,15 @@
      * incremented in {@link #onStartOrResumeCheckpoint} but does not reset it.
      */
     public void onPauseCheckpoint() {
+        if (mEndCheckpointWritten.get()) {
+            // Do not change the streak if it was already reset.
+            return;
+        }
+        if (!mStartCheckpointWritten.getAndSet(false)) {
+            // Do not change the streak if it hasn't been incremented yet.
+            return;
+        }
+
         int currentStreak = SharedPreferencesManager.getInstance().readInt(
                 ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE);
         assert currentStreak >= 0;
@@ -128,6 +150,11 @@
      * be saved to be used in Safe Mode.
      */
     void onEndCheckpoint(ValuesReturned safeValuesReturned) {
+        if (mEndCheckpointWritten.getAndSet(true)) {
+            // Limit to one reset per run.
+            return;
+        }
+
         SharedPreferencesManager.getInstance().writeInt(
                 ChromePreferenceKeys.FLAGS_CRASH_STREAK_BEFORE_CACHE, 0);
 
@@ -218,6 +245,8 @@
 
     void clearMemoryForTesting() {
         mBehavior.set(Behavior.UNKNOWN);
+        mStartCheckpointWritten.set(false);
+        mEndCheckpointWritten.set(false);
     }
 
     @SuppressLint({"ApplySharedPref"})
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index f84f47f8..597dd53 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -381,6 +381,7 @@
     public static final String KITKAT_SUPPORTED = "KitKatSupported";
     public static final String LEAK_DETECTION_UNAUTHENTICATED = "LeakDetectionUnauthenticated";
     public static final String LIGHTWEIGHT_REACTIONS = "LightweightReactions";
+    public static final String LOCATION_BAR_MODEL_OPTIMIZATIONS = "LocationBarModelOptimizations";
     public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI =
             "LookalikeUrlNavigationSuggestionsUI";
     public static final String MARK_HTTP_AS = "MarkHttpAs";
@@ -533,12 +534,6 @@
     public static final String UNIFIED_CREDENTIAL_MANAGER_DRY_RUN =
             "UnifiedCredentialManagerDryRun";
     public static final String UNIFIED_PASSWORD_MANAGER_ANDROID = "UnifiedPasswordManagerAndroid";
-    public static final String UNIFIED_PASSWORD_MANAGER_MIGRATION =
-            "UnifiedPasswordManagerMigration";
-    public static final String UNIFIED_PASSWORD_MANAGER_SHADOW_ANDROID =
-            "UnifiedPasswordManagerShadowAndroid";
-    public static final String UNIFIED_PASSWORD_MANAGER_SHADOW_WRITE_OPERATIONS_ANDROID =
-            "UnifiedPasswordManagerShadowWriteOperationsAndroid";
     public static final String UPCOMING_SHARING_FEATURES = "UpcomingSharingFeatures";
     public static final String UPDATE_NOTIFICATION_IMMEDIATE_SHOW_OPTION =
             "UpdateNotificationScheduleServiceImmediateShowOption";
diff --git a/chrome/browser/lacros/launcher_search/search_controller_lacros.cc b/chrome/browser/lacros/launcher_search/search_controller_lacros.cc
index c5caefb..7d1f7be0 100644
--- a/chrome/browser/lacros/launcher_search/search_controller_lacros.cc
+++ b/chrome/browser/lacros/launcher_search/search_controller_lacros.cc
@@ -6,11 +6,42 @@
 
 #include <utility>
 
+#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lacros/launcher_search/search_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/lacros/lacros_service.h"
+#include "components/omnibox/browser/autocomplete_classifier.h"
 
 namespace crosapi {
+namespace {
 
-SearchControllerLacros::SearchControllerLacros() {
+int ProviderTypes() {
+  // We use all the default providers except for the document provider, which
+  // suggests Drive files on enterprise devices. This is disabled to avoid
+  // duplication with search results from DriveFS.
+  return AutocompleteClassifier::DefaultOmniboxProviders() &
+         ~AutocompleteProvider::TYPE_DOCUMENT;
+}
+
+}  // namespace
+
+SearchControllerLacros::SearchControllerLacros()
+    : profile_(g_browser_process->profile_manager()->GetProfileByPath(
+          ProfileManager::GetPrimaryUserProfilePath())) {
+  if (!profile_) {
+    // TODO(crbug.com/1228587): Log error metrics if the profile is unavailable.
+    return;
+  }
+
+  profile_observation_.Observe(profile_);
+
+  autocomplete_controller_ = std::make_unique<AutocompleteController>(
+      std::make_unique<ChromeAutocompleteProviderClient>(profile_),
+      ProviderTypes());
+  autocomplete_controller_->AddObserver(this);
+
   chromeos::LacrosService* service = chromeos::LacrosService::Get();
   if (!service->IsAvailable<mojom::SearchControllerRegistry>())
     return;
@@ -20,13 +51,71 @@
 
 SearchControllerLacros::~SearchControllerLacros() = default;
 
+void SearchControllerLacros::OnProfileWillBeDestroyed(Profile* profile) {
+  DCHECK_EQ(profile, profile_);
+  DCHECK(profile_observation_.IsObservingSource(profile_));
+
+  // SearchControllerLacros must shut down before the Profile is destroyed,
+  // otherwise there will be a use-after-free.
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  autocomplete_controller_.reset();
+  profile_observation_.Reset();
+  profile_ = nullptr;
+}
+
 void SearchControllerLacros::Search(const std::u16string& query,
                                     SearchCallback callback) {
+  if (!autocomplete_controller_) {
+    // TODO(crbug.com/1228687): Log error metrics if the autocomplete controller
+    // is unavailable.
+    if (publisher_.is_bound() && publisher_.is_connected()) {
+      publisher_->OnSearchResultsReceived(
+          mojom::SearchStatus::kBackendUnavailable, absl::nullopt);
+    }
+    return;
+  }
+
+  autocomplete_controller_->Stop(false);
+  // If there is an in-flight session, send a cancellation notification.
+  if (publisher_.is_bound() && publisher_.is_connected()) {
+    publisher_->OnSearchResultsReceived(mojom::SearchStatus::kCancelled,
+                                        absl::nullopt);
+  }
+
   // Reset the remote and send a new pending receiver to ash.
   publisher_.reset();
   std::move(callback).Run(publisher_.BindNewEndpointAndPassReceiver());
 
-  // TODO(crbug/1228587): Fill the results here.
+  // Start the search. Results will be returned through the observer interface.
+  AutocompleteInput input =
+      AutocompleteInput(query, metrics::OmniboxEventProto::CHROMEOS_APP_LIST,
+                        ChromeAutocompleteSchemeClassifier(profile_));
+  if (input.text().empty())
+    input.set_focus_type(OmniboxFocusType::ON_FOCUS);
+
+  autocomplete_controller_->Start(input);
+}
+
+void SearchControllerLacros::OnResultChanged(AutocompleteController* controller,
+                                             bool default_match_changed) {
+  DCHECK_EQ(controller, autocomplete_controller_.get());
+
+  std::vector<mojom::SearchResultPtr> results;
+  for (AutocompleteMatch match : autocomplete_controller_->result()) {
+    if (match.search_terms_args) {
+      match.search_terms_args->request_source = TemplateURLRef::CROS_APP_LIST;
+      autocomplete_controller_->SetMatchDestinationURL(&match);
+    }
+
+    auto result = match.answer.has_value() ? CreateAnswerResult(match)
+                                           : CreateResult(match);
+    results.push_back(std::move(result));
+  }
+
+  const auto status = autocomplete_controller_->done()
+                          ? mojom::SearchStatus::kDone
+                          : mojom::SearchStatus::kInProgress;
+  publisher_->OnSearchResultsReceived(status, std::move(results));
 }
 
 }  // namespace crosapi
diff --git a/chrome/browser/lacros/launcher_search/search_controller_lacros.h b/chrome/browser/lacros/launcher_search/search_controller_lacros.h
index f808429..7dce0063 100644
--- a/chrome/browser/lacros/launcher_search/search_controller_lacros.h
+++ b/chrome/browser/lacros/launcher_search/search_controller_lacros.h
@@ -8,27 +8,46 @@
 #include <string>
 
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_observer.h"
 #include "chromeos/crosapi/mojom/launcher_search.mojom.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
 namespace crosapi {
 
 // Implements crosapi interface for launcher search controller.
-class SearchControllerLacros : public mojom::SearchController {
+class SearchControllerLacros : public mojom::SearchController,
+                               public AutocompleteController::Observer,
+                               public ProfileObserver {
  public:
   SearchControllerLacros();
   SearchControllerLacros(const SearchControllerLacros&) = delete;
   SearchControllerLacros& operator=(const SearchControllerLacros&) = delete;
   ~SearchControllerLacros() override;
 
+  // ProfileObserver:
+  void OnProfileWillBeDestroyed(Profile* profile) override;
+
  private:
   // mojom::SearchController:
   void Search(const std::u16string& query, SearchCallback callback) override;
 
+  // AutocompleteController::Observer:
+  void OnResultChanged(AutocompleteController* controller,
+                       bool default_match_changed) override;
+
+  Profile* profile_;
+  std::unique_ptr<AutocompleteController> autocomplete_controller_;
+
   mojo::AssociatedRemote<mojom::SearchResultsPublisher> publisher_;
   mojo::Receiver<mojom::SearchController> receiver_{this};
 
+  // Observes the profile destruction.
+  base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
+
   base::WeakPtrFactory<SearchControllerLacros> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/lacros/launcher_search/search_util.cc b/chrome/browser/lacros/launcher_search/search_util.cc
new file mode 100644
index 0000000..f2bc5faf
--- /dev/null
+++ b/chrome/browser/lacros/launcher_search/search_util.cc
@@ -0,0 +1,166 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/lacros/launcher_search/search_util.h"
+
+#include "components/omnibox/browser/autocomplete_match_type.h"
+#include "components/omnibox/browser/suggestion_answer.h"
+
+namespace crosapi {
+namespace {
+
+using AnswerType = mojom::SearchResult::AnswerType;
+using OmniboxType = mojom::SearchResult::OmniboxType;
+
+AnswerType MatchTypeToAnswerType(const int type) {
+  switch (static_cast<SuggestionAnswer::AnswerType>(type)) {
+    case SuggestionAnswer::ANSWER_TYPE_WEATHER:
+      return AnswerType::kWeather;
+    case SuggestionAnswer::ANSWER_TYPE_CURRENCY:
+      return AnswerType::kCurrency;
+    case SuggestionAnswer::ANSWER_TYPE_DICTIONARY:
+      return AnswerType::kDictionary;
+    case SuggestionAnswer::ANSWER_TYPE_FINANCE:
+      return AnswerType::kFinance;
+    case SuggestionAnswer::ANSWER_TYPE_SUNRISE:
+      return AnswerType::kSunrise;
+    case SuggestionAnswer::ANSWER_TYPE_TRANSLATION:
+      return AnswerType::kTranslation;
+    case SuggestionAnswer::ANSWER_TYPE_WHEN_IS:
+      return AnswerType::kWhenIs;
+    default:
+      return AnswerType::kDefaultAnswer;
+  }
+}
+
+OmniboxType MatchTypeToOmniboxType(const AutocompleteMatchType::Type type) {
+  switch (type) {
+    case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
+    case AutocompleteMatchType::HISTORY_URL:
+    case AutocompleteMatchType::HISTORY_TITLE:
+    case AutocompleteMatchType::HISTORY_BODY:
+    case AutocompleteMatchType::HISTORY_KEYWORD:
+    case AutocompleteMatchType::NAVSUGGEST:
+    case AutocompleteMatchType::BOOKMARK_TITLE:
+    case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED:
+    case AutocompleteMatchType::CLIPBOARD_URL:
+    case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED:
+    case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
+    case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
+    case AutocompleteMatchType::DOCUMENT_SUGGESTION:
+    case AutocompleteMatchType::PEDAL_DEPRECATED:
+      return OmniboxType::kDomain;
+
+    case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
+    case AutocompleteMatchType::SEARCH_SUGGEST:
+    case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
+    case AutocompleteMatchType::SEARCH_SUGGEST_TAIL:
+    case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
+    case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
+    case AutocompleteMatchType::CONTACT_DEPRECATED:
+    case AutocompleteMatchType::VOICE_SUGGEST:
+    case AutocompleteMatchType::CLIPBOARD_TEXT:
+    case AutocompleteMatchType::CLIPBOARD_IMAGE:
+      return OmniboxType::kSearch;
+
+    case AutocompleteMatchType::SEARCH_HISTORY:
+    case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
+      return OmniboxType::kHistory;
+
+    case AutocompleteMatchType::CALCULATOR:
+      return OmniboxType::kCalculator;
+
+    case AutocompleteMatchType::OPEN_TAB:
+      return OmniboxType::kOpenTab;
+
+    case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
+    case AutocompleteMatchType::TILE_SUGGESTION:
+    case AutocompleteMatchType::TILE_NAVSUGGEST:
+    case AutocompleteMatchType::NUM_TYPES:
+      // Not reached.
+      return OmniboxType::kDomain;
+  }
+}
+
+// Returns the first text field from the given ImageLine.
+std::u16string GetFirstText(const SuggestionAnswer::ImageLine& line) {
+  return line.text_fields().empty() ? std::u16string()
+                                    : line.text_fields()[0].text();
+}
+
+std::u16string GetAdditionalText(const SuggestionAnswer::ImageLine& line) {
+  return line.additional_text() ? line.additional_text()->text()
+                                : std::u16string();
+}
+
+mojom::SearchResult::TextType GetAdditionalTextType(
+    const SuggestionAnswer::ImageLine& line) {
+  if (!line.additional_text())
+    return mojom::SearchResult::TextType::kUnset;
+
+  switch (line.additional_text()->style()) {
+    case SuggestionAnswer::TextStyle::POSITIVE:
+      return mojom::SearchResult::TextType::kPositive;
+    case SuggestionAnswer::TextStyle::NEGATIVE:
+      return mojom::SearchResult::TextType::kNegative;
+    default:
+      return mojom::SearchResult::TextType::kUnset;
+  }
+}
+
+mojom::SearchResultPtr CreateBaseResult(const AutocompleteMatch& match) {
+  mojom::SearchResultPtr result = mojom::SearchResult::New();
+
+  result->type = mojom::SearchResultType::kOmniboxResult;
+  result->relevance = match.relevance;
+  result->destination_url = match.destination_url;
+  result->is_omnibox_search = AutocompleteMatch::IsSearchType(match.type)
+                                  ? mojom::SearchResult::OptionalBool::kTrue
+                                  : mojom::SearchResult::OptionalBool::kFalse;
+  return result;
+}
+
+}  // namespace
+
+mojom::SearchResultPtr CreateAnswerResult(const AutocompleteMatch& match) {
+  mojom::SearchResultPtr result = CreateBaseResult(match);
+
+  result->is_answer = mojom::SearchResult::OptionalBool::kTrue;
+  result->answer_type = MatchTypeToAnswerType(match.answer->type());
+
+  if (result->answer_type == AnswerType::kWeather)
+    result->image_url = match.answer->image_url();
+
+  result->contents = match.contents;
+  result->additional_contents = GetAdditionalText(match.answer->first_line());
+
+  const auto& second_line = match.answer->second_line();
+  result->description = GetFirstText(second_line);
+  result->additional_description = GetAdditionalText(second_line);
+  result->additional_description_type = GetAdditionalTextType(second_line);
+
+  return result;
+}
+
+mojom::SearchResultPtr CreateResult(const AutocompleteMatch& match) {
+  mojom::SearchResultPtr result = CreateBaseResult(match);
+
+  result->is_answer = mojom::SearchResult::OptionalBool::kFalse;
+
+  if (match.type == AutocompleteMatchType::SEARCH_SUGGEST_ENTITY &&
+      !match.image_url.is_empty()) {
+    result->omnibox_type = OmniboxType::kRichImage;
+    result->image_url = match.image_url;
+  } else {
+    // TODO(crbug.com/1228687): Implement favicon logic.
+    result->omnibox_type = MatchTypeToOmniboxType(match.type);
+  }
+
+  result->contents = match.contents;
+  result->description = match.description;
+
+  return result;
+}
+
+}  // namespace crosapi
diff --git a/chrome/browser/lacros/launcher_search/search_util.h b/chrome/browser/lacros/launcher_search/search_util.h
new file mode 100644
index 0000000..bb4a788
--- /dev/null
+++ b/chrome/browser/lacros/launcher_search/search_util.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_LACROS_LAUNCHER_SEARCH_SEARCH_UTIL_H_
+#define CHROME_BROWSER_LACROS_LAUNCHER_SEARCH_SEARCH_UTIL_H_
+
+#include "chromeos/crosapi/mojom/launcher_search.mojom.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+
+namespace crosapi {
+
+// Creates an Omnibox answer card result from the AutocompleteMatch.
+mojom::SearchResultPtr CreateAnswerResult(const AutocompleteMatch& match);
+
+// Creates an Omnibox search result from the AutocompleteMatch.
+mojom::SearchResultPtr CreateResult(const AutocompleteMatch& match);
+
+}  // namespace crosapi
+
+#endif  // CHROME_BROWSER_LACROS_LAUNCHER_SEARCH_SEARCH_UTIL_H_
diff --git a/chrome/browser/lacros/launcher_search/search_util_unittest.cc b/chrome/browser/lacros/launcher_search/search_util_unittest.cc
new file mode 100644
index 0000000..4a582c6
--- /dev/null
+++ b/chrome/browser/lacros/launcher_search/search_util_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/lacros/launcher_search/search_util.h"
+
+#include <string>
+
+#include "base/json/json_reader.h"
+#include "base/values.h"
+#include "components/omnibox/browser/autocomplete_match_type.h"
+#include "components/omnibox/browser/suggestion_answer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
+namespace crosapi {
+namespace {
+
+// Tests result conversion for a default answer result.
+TEST(SearchUtilTest, CreateAnswerResult) {
+  AutocompleteMatch match;
+  match.relevance = 1248;
+  match.destination_url = GURL("http://www.example.com/");
+  match.type = AutocompleteMatchType::Type::SEARCH_SUGGEST;
+  match.contents = u"contents";
+  match.description = u"description";
+
+  SuggestionAnswer answer;
+  std::string json =
+      "{ \"l\": ["
+      "  { \"il\": { \"t\": [{ \"t\": \"text one\", \"tt\": 8 }], "
+      "              \"at\": { \"t\": \"additional one\", \"tt\": 42 } } }, "
+      "  { \"il\": { \"t\": [{ \"t\": \"text two\", \"tt\": 5 }], "
+      "              \"at\": { \"t\": \"additional two\", \"tt\": 6 } } } "
+      "] }";
+  absl::optional<base::Value> value = base::JSONReader::Read(json);
+  ASSERT_TRUE(value && value->is_dict());
+  ASSERT_TRUE(SuggestionAnswer::ParseAnswer(value->GetDict(), u"-1", &answer));
+  match.answer = answer;
+
+  const auto result = CreateAnswerResult(match);
+  EXPECT_EQ(result->type, mojom::SearchResultType::kOmniboxResult);
+  EXPECT_EQ(result->relevance, 1248);
+  ASSERT_TRUE(result->destination_url.has_value());
+  EXPECT_EQ(result->destination_url.value(), GURL("http://www.example.com/"));
+  EXPECT_EQ(result->is_omnibox_search,
+            mojom::SearchResult::OptionalBool::kTrue);
+  EXPECT_EQ(result->is_answer, mojom::SearchResult::OptionalBool::kTrue);
+
+  ASSERT_TRUE(result->contents.has_value());
+  EXPECT_EQ(result->contents.value(), u"contents");
+  ASSERT_TRUE(result->additional_contents.has_value());
+  EXPECT_EQ(result->additional_contents.value(), u"additional one");
+  ASSERT_TRUE(result->description.has_value());
+  EXPECT_EQ(result->description.value(), u"text two");
+  ASSERT_TRUE(result->additional_description.has_value());
+  EXPECT_EQ(result->additional_description.value(), u"additional two");
+}
+
+// Tests result conversion for a rich entity Omnibox result.
+TEST(SearchUtilTest, CreateResult) {
+  AutocompleteMatch match;
+  match.relevance = 300;
+  match.destination_url = GURL("http://www.example.com/");
+  match.type = AutocompleteMatchType::Type::SEARCH_SUGGEST_ENTITY;
+  match.image_url = GURL("http://www.example.com/image.jpeg");
+  match.contents = u"contents";
+  match.description = u"description";
+
+  const auto result = CreateResult(match);
+  EXPECT_EQ(result->type, mojom::SearchResultType::kOmniboxResult);
+  EXPECT_EQ(result->relevance, 300);
+  ASSERT_TRUE(result->destination_url.has_value());
+  EXPECT_EQ(result->destination_url.value(), GURL("http://www.example.com/"));
+  EXPECT_EQ(result->is_omnibox_search,
+            mojom::SearchResult::OptionalBool::kTrue);
+  EXPECT_EQ(result->is_answer, mojom::SearchResult::OptionalBool::kFalse);
+  EXPECT_EQ(result->omnibox_type, mojom::SearchResult::OmniboxType::kRichImage);
+  ASSERT_TRUE(result->image_url.has_value());
+  EXPECT_EQ(result->image_url.value(),
+            GURL("http://www.example.com/image.jpeg"));
+  ASSERT_TRUE(result->contents.has_value());
+  EXPECT_EQ(result->contents.value(), u"contents");
+  ASSERT_TRUE(result->description.has_value());
+  EXPECT_EQ(result->description.value(), u"description");
+}
+
+}  // namespace
+}  // namespace crosapi
diff --git a/chrome/browser/lens/region_search/lens_region_search_controller.cc b/chrome/browser/lens/region_search/lens_region_search_controller.cc
index 99ec5de6..fed827a 100644
--- a/chrome/browser/lens/region_search/lens_region_search_controller.cc
+++ b/chrome/browser/lens/region_search/lens_region_search_controller.cc
@@ -192,7 +192,7 @@
     core_tab_helper->SearchWithLensInNewTab(
         image, captured_image.Size(),
         lens::EntryPoint::CHROME_REGION_SEARCH_MENU_ITEM,
-        lens::features::kEnableSidePanelForLensRegionSearch.Get());
+        lens::features::IsLensSidePanelEnabled());
   } else {
     core_tab_helper->SearchByImageInNewTab(image, captured_image.Size());
   }
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index 5546ae3..7d36830 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -230,11 +230,12 @@
       download::DownloadDangerType danger_type,
       download::DownloadItem::MixedContentStatus mcs,
       const base::FilePath& intermediate_path,
+      const base::FilePath& display_name,
       absl::optional<download::DownloadSchedule> download_schedule,
       download::DownloadInterruptReason reason) {
-    std::move(callback).Run(target_path, disp,
-                            download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, mcs,
-                            intermediate_path, download_schedule, reason);
+    std::move(callback).Run(
+        target_path, disp, download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL, mcs,
+        intermediate_path, display_name, download_schedule, reason);
   }
 };
 
diff --git a/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc b/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc
index 98fea9d8..a68c2ec 100644
--- a/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h"
 #include "base/bind.h"
@@ -18,7 +19,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/system/fake_statistics_provider.h"
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index de50846..72d1bd7 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -345,12 +345,12 @@
   registry->RegisterBooleanPref(prefs::kQuicAllowed, true);
   registry->RegisterBooleanPref(prefs::kGloballyScopeHTTPAuthCacheEnabled,
                                 false);
+  registry->RegisterListPref(prefs::kHSTSPolicyBypassList);
 }
 
 // static
 void ProfileNetworkContextService::RegisterLocalStatePrefs(
     PrefRegistrySimple* registry) {
-  registry->RegisterListPref(prefs::kHSTSPolicyBypassList);
   registry->RegisterIntegerPref(
       prefs::kAmbientAuthenticationInPrivateModesEnabled,
       static_cast<int>(net::AmbientAuthAllowedProfileTypes::REGULAR_ONLY));
@@ -794,7 +794,7 @@
         base::FilePath(chrome::kSCTAuditingPendingReportsFileName);
   }
   const base::Value* hsts_policy_bypass_list =
-      g_browser_process->local_state()->GetList(prefs::kHSTSPolicyBypassList);
+      profile_->GetPrefs()->GetList(prefs::kHSTSPolicyBypassList);
   for (const auto& value : hsts_policy_bypass_list->GetListDeprecated()) {
     const std::string* string_value = value.GetIfString();
     if (!string_value)
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java
index efd5895..f654f35 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java
@@ -126,13 +126,13 @@
      *        using notifications, false for invoking on startup.
      */
     public void requestPermissionIfNeeded(boolean contextual) {
-        // Notifications only require permission starting at Android T.
-        if (!BuildInfo.isAtLeastT()) {
+        if (!BuildInfo.isAtLeastT() || !BuildInfo.targetsAtLeastT()) {
             return;
         }
 
-        // Record the state of the notification permission before trying to ask.
-        recordNotificationPermissionState();
+        // Record the state of the notification permission before trying to ask but after verifying
+        // we are running on Android T.
+        recordCurrentNotificationPermissionStatus();
 
         @PermissionRequestMode
         int requestMode = shouldRequestPermission();
@@ -156,7 +156,12 @@
 
     @PermissionRequestMode
     int shouldRequestPermission() {
-        if (!BuildInfo.isAtLeastT()) return PermissionRequestMode.DO_NOT_REQUEST;
+        // Notifications only require permission starting at Android T. And apps targeting < T can't
+        // request permission as the OS prompts the user automatically.
+        if (!BuildInfo.isAtLeastT() || !BuildInfo.targetsAtLeastT()) {
+            return PermissionRequestMode.DO_NOT_REQUEST;
+        }
+
         if (mAndroidPermissionDelegate.hasPermission(PermissionConstants.NOTIFICATION_PERMISSION)) {
             return PermissionRequestMode.DO_NOT_REQUEST;
         }
@@ -188,7 +193,11 @@
                                    : PermissionRequestMode.REQUEST_ANDROID_PERMISSION;
     }
 
-    private void recordNotificationPermissionState() {
+    /**
+     * Records the current status of the notification permission (Allowed/Denied) and if denied we
+     * include how many times we've asked or if the permission is denied by policy.
+     */
+    private void recordCurrentNotificationPermissionStatus() {
         if (mAndroidPermissionDelegate.hasPermission(PermissionConstants.NOTIFICATION_PERMISSION)) {
             NotificationUmaTracker.getInstance().recordNotificationPermissionState(
                     NotificationPermissionState.ALLOWED);
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java
index 18c9ea1..2f1f5e59 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java
@@ -69,6 +69,10 @@
                         .FIELD_TRIAL_ALWAYS_SHOW_RATIONALE_BEFORE_REQUESTING_PERMISSION,
                 "false");
         FeatureList.setTestValues(testValues);
+
+        // These tests only apply on builds targeting T running on a T device.
+        ShadowBuildInfo.setTargetsAtLeastT(true);
+        ShadowBuildInfo.setIsAtLeastT(true);
     }
 
     @After
@@ -160,8 +164,53 @@
     }
 
     @Test
+    public void testNotificationPrompt_nothingHappensWhenNotTargetingT() {
+        // Build targeting a <T SDK.
+        ShadowBuildInfo.setTargetsAtLeastT(false);
+
+        mActivityScenarios.getScenario().onActivity(activity -> {
+            TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
+            TestAndroidPermissionDelegate permissionDelegate =
+                    new TestAndroidPermissionDelegate(new WeakReference<>(activity));
+            NotificationPermissionController notificationPermissionController =
+                    createNotificationPermissionController(rationaleDelegate, permissionDelegate);
+
+            notificationPermissionController.requestPermissionIfNeeded();
+
+            long permissionRequestTimestamp =
+                    PermissionPrefs.getAndroidNotificationPermissionRequestTimestamp();
+
+            // We shouldn't have requested for permission or shown the rationale.
+            assertEquals(0, rationaleDelegate.getCallCount());
+            assertEquals(0, permissionRequestTimestamp);
+        });
+    }
+
+    @Test
+    public void testNotificationPrompt_nothingHappensWhenNotRunningOnT() {
+        // Running on a <T device.
+        ShadowBuildInfo.setIsAtLeastT(false);
+
+        mActivityScenarios.getScenario().onActivity(activity -> {
+            TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
+            TestAndroidPermissionDelegate permissionDelegate =
+                    new TestAndroidPermissionDelegate(new WeakReference<>(activity));
+            NotificationPermissionController notificationPermissionController =
+                    createNotificationPermissionController(rationaleDelegate, permissionDelegate);
+
+            notificationPermissionController.requestPermissionIfNeeded();
+
+            long permissionRequestTimestamp =
+                    PermissionPrefs.getAndroidNotificationPermissionRequestTimestamp();
+
+            // We shouldn't have requested for permission or shown the rationale.
+            assertEquals(0, rationaleDelegate.getCallCount());
+            assertEquals(0, permissionRequestTimestamp);
+        });
+    }
+
+    @Test
     public void testNotificationPrompt_alreadyHasPermission() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             TestAndroidPermissionDelegate permissionDelegate =
@@ -226,7 +275,6 @@
 
     @Test
     public void testNotificationPromptShownOnStartup_noPermissionsYet_shouldShowOSPrompt() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             NotificationPermissionController notificationPermissionController =
                     createNotificationPermissionController(activity);
@@ -248,7 +296,6 @@
 
     @Test
     public void testNotificationPromptShownOnStartup_alwaysShowRationale() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         FeatureList.TestValues testValues = new FeatureList.TestValues();
         testValues.addFieldTrialParamOverride(ChromeFeatureList.NOTIFICATION_PERMISSION_VARIANT,
                 NotificationPermissionController
@@ -318,7 +365,6 @@
 
     @Test
     public void testNotificationPrompt_showOSPromptAndAccept() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             TestAndroidPermissionDelegate permissionDelegate =
@@ -363,7 +409,6 @@
 
     @Test
     public void testNotificationPrompt_showOSPromptAndDismiss_tooSoonForSecondTime() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             TestAndroidPermissionDelegate permissionDelegate =
@@ -404,7 +449,6 @@
 
     @Test
     public void testNotificationPrompt_showOSPromptAndReject_tooSoonForSecondTime() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             TestAndroidPermissionDelegate permissionDelegate =
@@ -445,7 +489,6 @@
 
     @Test
     public void testNotificationPrompt_showOSPromptAndDismiss_showAgainAfterTimePasses() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestAndroidPermissionDelegate permissionDelegate =
                     new TestAndroidPermissionDelegate(new WeakReference<>(activity));
@@ -489,7 +532,6 @@
 
     @Test
     public void testNotificationPrompt_showOSPromptAndReject_showRationaleAfterTimePasses() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             TestAndroidPermissionDelegate permissionDelegate =
@@ -541,7 +583,6 @@
     @Test
     public void
     testNotificationPrompt_showOSPromptAndReject_showRationaleAndAccept_approveSecondOSPrompt() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             TestAndroidPermissionDelegate permissionDelegate =
@@ -599,7 +640,6 @@
     @Test
     public void
     testNotificationPrompt_showOSPromptAndReject_showRationaleAndAccept_rejectSecondOSPrompt() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             TestAndroidPermissionDelegate permissionDelegate =
@@ -674,7 +714,6 @@
 
     @Test
     public void testNotificationPrompt_showOSPromptAndReject_showRationaleAndReject() {
-        ShadowBuildInfo.setIsAtLeastT(true);
         mActivityScenarios.getScenario().onActivity(activity -> {
             TestRationaleDelegate rationaleDelegate = new TestRationaleDelegate();
             NotificationPermissionController notificationPermissionController =
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
index db08205..88cb72d 100644
--- a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
@@ -243,10 +243,10 @@
             run_loop->Quit();
           },
           &run_loop, &results),
-      std::vector<GURL>{
-          GURL("https://www.youtube.com/"),
-          GURL("https://www.chrome.com/"),
-          GURL("https://music.youtube.com/"),
+      std::vector<std::string>{
+          "youtube.com",
+          "chrome.com",
+          "music.youtube.com",
       });
   run_loop.Run();
 
@@ -497,7 +497,7 @@
             run_loop->Quit();
           },
           &run_loop),
-      std::vector<GURL>{GURL("https://www.chromium.org")});
+      std::vector<std::string>{"www.chromium.org"});
 
   run_loop.Run();
 }
diff --git a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java
index 7a06bfe..b89a358d 100644
--- a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java
+++ b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/BuyableProductPageAnnotation.java
@@ -8,18 +8,13 @@
 import org.json.JSONObject;
 
 import org.chromium.base.Log;
-import org.chromium.base.annotations.DoNotClassMerge;
 import org.chromium.chrome.browser.page_annotations.PageAnnotation.PageAnnotationType;
 
 import java.util.Locale;
 
 /**
  * {@link PageAnnotation} for products in a page.
- *
- * This class should not be merged because it is being used as a key in a Map
- * in PageAnnotationUtils.java.
  */
-@DoNotClassMerge
 public class BuyableProductPageAnnotation extends PageAnnotation {
     private static final String TAG = "BPPA";
     private static final String BUYABLE_PRODUCT_KEY = "buyableProduct";
diff --git a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java
index 36ed4d2..b14bcd3e 100644
--- a/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java
+++ b/chrome/browser/page_annotations/android/java/src/org/chromium/chrome/browser/page_annotations/ProductPriceUpdatePageAnnotation.java
@@ -8,18 +8,13 @@
 import org.json.JSONObject;
 
 import org.chromium.base.Log;
-import org.chromium.base.annotations.DoNotClassMerge;
 import org.chromium.chrome.browser.page_annotations.PageAnnotation.PageAnnotationType;
 
 import java.util.Locale;
 
 /**
  * {@link PageAnnotation} for product price updates in a page.
- *
- * This class should not be merged because it is being used as a key in a Map
- * in PageAnnotationUtils.java.
  */
-@DoNotClassMerge
 public class ProductPriceUpdatePageAnnotation extends PageAnnotation {
     private static final String TAG = "PPUPA";
     private static final String PRICE_UPDATE_KEY = "priceUpdate";
@@ -107,4 +102,4 @@
         return json != null && json.has(CURRENCY_CODE_KEY) && !json.isNull(CURRENCY_CODE_KEY)
                 && json.has(AMOUNT_MICROS_KEY) && !json.isNull(AMOUNT_MICROS_KEY);
     }
-}
+}
\ No newline at end of file
diff --git a/chrome/browser/page_info/OWNERS b/chrome/browser/page_info/OWNERS
index 7c601fd..535ff1d 100644
--- a/chrome/browser/page_info/OWNERS
+++ b/chrome/browser/page_info/OWNERS
@@ -1 +1,3 @@
 file://chrome/browser/ui/page_info/OWNERS
+
+per-file ...about_this_site*=dullweber@chromium.org
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 8a23c6c..6c7ef053 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -62,6 +62,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_test_data_source.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/download/public/common/download_item.h"
 #include "components/guest_view/browser/guest_view_manager.h"
@@ -1017,6 +1018,21 @@
 
 class PDFExtensionJSTest : public PDFExtensionTest {
  protected:
+  void SetUpOnMainThread() override {
+    PDFExtensionTest::SetUpOnMainThread();
+
+    // Load the pak file holding the resources served from chrome://webui-test.
+    base::FilePath pak_path;
+    ASSERT_TRUE(base::PathService::Get(base::DIR_MODULE, &pak_path));
+    pak_path = pak_path.AppendASCII("browser_tests.pak");
+    ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
+        pak_path, ui::kScaleFactorNone);
+
+    // Register the chrome://webui-test data source.
+    content::WebUIDataSource::Add(browser()->profile(),
+                                  webui::CreateWebUITestDataSource());
+  }
+
   void RunTestsInJsModule(const std::string& filename,
                           const std::string& pdf_filename) {
     RunTestsInJsModuleHelper(filename, pdf_filename, /*new_tab=*/false);
@@ -1050,12 +1066,16 @@
     constexpr char kModuleLoaderTemplate[] =
         R"(var s = document.createElement('script');
            s.type = 'module';
-           s.src = '_test_resources/pdf/%s';
+           s.src = 'chrome://%s/pdf/%s';
+           s.onerror = function(e) {
+             console.error('Error while loading', e.target.src);
+           };
            document.body.appendChild(s);)";
 
     ASSERT_TRUE(content::ExecuteScript(
         guest_contents,
-        base::StringPrintf(kModuleLoaderTemplate, filename.c_str())));
+        base::StringPrintf(kModuleLoaderTemplate,
+                           chrome::kChromeUIWebUITestHost, filename.c_str())));
 
     if (!catcher.GetNextResult())
       FAIL() << catcher.message();
@@ -1332,6 +1352,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionContentSettingJSTest, BeepThenNoBeep) {
+  content::RenderProcessHost::SetMaxRendererProcessCount(1);
+
   RunTestsInJsModule("beep_test.js", "test-beep.pdf");
   SetPdfJavaScript(/*enabled=*/false);
   RunTestsInJsModuleNewTab("nobeep_test.js", "test-beep.pdf");
@@ -1343,6 +1365,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PDFExtensionContentSettingJSTest, NoBeepThenBeep) {
+  content::RenderProcessHost::SetMaxRendererProcessCount(1);
+
   SetPdfJavaScript(/*enabled=*/false);
   RunTestsInJsModule("nobeep_test.js", "test-beep.pdf");
   SetPdfJavaScript(/*enabled=*/true);
@@ -2068,7 +2092,7 @@
  protected:
   std::vector<base::Feature> GetEnabledFeatures() const override {
     auto enabled = PDFExtensionTest::GetEnabledFeatures();
-    enabled.push_back(lens::features::kLensRegionSearch);
+    enabled.push_back(lens::features::kLensStandalone);
     return enabled;
   }
 };
diff --git a/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc b/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc
index bef0640f..eb244251 100644
--- a/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc
+++ b/chrome/browser/policy/default_geolocation_policy_handler_unittest.cc
@@ -50,7 +50,7 @@
   UpdateProviderPolicy(policy);
   const base::Value* value = nullptr;
   EXPECT_TRUE(store_->GetValue(arc::prefs::kArcLocationServiceEnabled, &value));
-  EXPECT_TRUE(base::Value(false).Equals(value));
+  EXPECT_EQ(base::Value(false), *value);
 }
 
 TEST_F(DefaultGeolocationPolicyHandlerTest, AskGeolocation) {
diff --git a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
index 6de86b3..e389216 100644
--- a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
+++ b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
@@ -293,7 +293,7 @@
           value,
           std::string(negation ? "isn't" : "is") + " equal to " +
               ValueToString(*value)) {
-  return value->Equals(&arg);
+  return *value == arg;
 }
 
 MATCHER(IsListEmpty, std::string(negation ? "isn't" : "is") + " empty.") {
diff --git a/chrome/browser/policy/profile_policy_connector_unittest.cc b/chrome/browser/policy/profile_policy_connector_unittest.cc
index 6858e331..cd94f6b5 100644
--- a/chrome/browser/policy/profile_policy_connector_unittest.cc
+++ b/chrome/browser/policy/profile_policy_connector_unittest.cc
@@ -287,7 +287,7 @@
       connector.policy_service()->GetPolicies(chrome_ns).GetValue(
           key::kAutofillAddressEnabled, base::Value::Type::BOOLEAN);
   ASSERT_TRUE(value);
-  EXPECT_TRUE(base::Value(false).Equals(value));
+  EXPECT_EQ(base::Value(false), *value);
 
   // Now test with a higher-priority provider also setting the policy.
   UpdateChromePolicyToMockProviderAndVerify(&mock_platform_provider, connector);
diff --git a/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc b/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc
index fb9baea..955882e 100644
--- a/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc
+++ b/chrome/browser/profiles/incognito_mode_policy_handler_unittest.cc
@@ -51,7 +51,7 @@
   void VerifyValues(IncognitoModePrefs::Availability availability) {
     const base::Value* value = NULL;
     EXPECT_TRUE(store_->GetValue(prefs::kIncognitoModeAvailability, &value));
-    EXPECT_TRUE(base::Value(static_cast<int>(availability)).Equals(value));
+    EXPECT_EQ(base::Value(static_cast<int>(availability)), *value);
   }
 };
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 67d7beb..5e4b07c 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3037,7 +3037,7 @@
   const bool provider_supports_image_search =
       provider && !provider->image_url().empty() &&
       provider->image_url_ref().IsValid(service->search_terms_data());
-  return base::FeatureList::IsEnabled(lens::features::kLensRegionSearch) &&
+  return base::FeatureList::IsEnabled(lens::features::kLensStandalone) &&
          !IsFrameInPdfViewer(GetRenderFrameHost()) &&
          provider_supports_image_search &&
          !GetDocumentURL(params_).SchemeIs(content::kChromeUIScheme) &&
@@ -3290,7 +3290,7 @@
   core_tab_helper->SearchWithLensInNewTab(
       render_frame_host, params().src_url,
       lens::EntryPoint::CHROME_SEARCH_WITH_GOOGLE_LENS_CONTEXT_MENU_ITEM,
-      lens::features::kEnableSidePanelForLensImageSearch.Get());
+      lens::features::IsLensSidePanelEnabled());
 }
 
 void RenderViewContextMenu::ExecRegionSearch(
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index 763bbc91..3eea68e 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -1565,10 +1565,9 @@
   void SetUp() override {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndEnableFeatureWithParameters(
-        lens::features::kLensRegionSearch,
+        lens::features::kLensStandalone,
         std::map<std::string, std::string>{
-            {lens::features::kEnableSidePanelForLensRegionSearch.name,
-             "false"}});
+            {lens::features::kEnableSidePanelForLens.name, "false"}});
 
     InProcessBrowserTest::SetUp();
   }
@@ -1614,7 +1613,7 @@
 
   GURL GetLensRegionSearchURL() {
     static const std::string kLensRegionSearchURL =
-        lens::features::GetHomepageURLForRegionSearch() + "upload?ep=crs";
+        lens::features::GetHomepageURLForLens() + "upload?ep=crs";
     return GURL(kLensRegionSearchURL);
   }
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index 707dba6..d5a0428f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -743,7 +743,7 @@
 // is enabled.
 TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearch) {
   base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(lens::features::kLensRegionSearch);
+  features.InitAndEnableFeature(lens::features::kLensStandalone);
   SetUserSelectedDefaultSearchProvider("https://www.google.com",
                                        /*supports_image_search=*/true);
   content::ContextMenuParams params = CreateParams(MenuItem::PAGE);
@@ -758,7 +758,7 @@
 TEST_F(RenderViewContextMenuPrefsTest,
        LensRegionSearchEnterprisePoicyDisabled) {
   base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(lens::features::kLensRegionSearch);
+  features.InitAndEnableFeature(lens::features::kLensStandalone);
   SetUserSelectedDefaultSearchProvider("https://www.google.com",
                                        /*supports_image_search=*/true);
   // Set enterprise policy to false.
@@ -774,7 +774,7 @@
 // clicks on an image.
 TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchDisabledOnImage) {
   base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(lens::features::kLensRegionSearch);
+  features.InitAndEnableFeature(lens::features::kLensStandalone);
   SetUserSelectedDefaultSearchProvider("https://www.google.com",
                                        /*supports_image_search=*/true);
   content::ContextMenuParams params = CreateParams(MenuItem::IMAGE);
@@ -791,7 +791,7 @@
 TEST_F(RenderViewContextMenuPrefsTest,
        LensRegionSearchNonGoogleDefaultSearchEngineSupportsImageSearch) {
   base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(lens::features::kLensRegionSearch);
+  features.InitAndEnableFeature(lens::features::kLensStandalone);
   SetUserSelectedDefaultSearchProvider("https://www.search.com",
                                        /*supports_image_search=*/true);
   content::ContextMenuParams params = CreateParams(MenuItem::PAGE);
@@ -807,7 +807,7 @@
 TEST_F(RenderViewContextMenuPrefsTest,
        LensRegionSearchDefaultSearchEngineDoesNotSupportImageSearch) {
   base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(lens::features::kLensRegionSearch);
+  features.InitAndEnableFeature(lens::features::kLensStandalone);
   SetUserSelectedDefaultSearchProvider("https://www.search.com",
                                        /*supports_image_search=*/false);
   content::ContextMenuParams params = CreateParams(MenuItem::PAGE);
@@ -822,7 +822,7 @@
 // is disabled.
 TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchExperimentDisabled) {
   base::test::ScopedFeatureList features;
-  features.InitAndDisableFeature(lens::features::kLensRegionSearch);
+  features.InitAndDisableFeature(lens::features::kLensStandalone);
   SetUserSelectedDefaultSearchProvider("https://www.google.com",
                                        /*supports_image_search=*/true);
   content::ContextMenuParams params = CreateParams(MenuItem::PAGE);
@@ -836,7 +836,7 @@
 // Chrome UI Scheme.
 TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchChromeUIScheme) {
   base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(lens::features::kLensRegionSearch);
+  features.InitAndEnableFeature(lens::features::kLensStandalone);
   SetUserSelectedDefaultSearchProvider("https://www.google.com",
                                        /*supports_image_search=*/true);
   content::ContextMenuParams params = CreateParams(MenuItem::PAGE);
diff --git a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc
index 1beaf9b..e3c904a 100644
--- a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer_browsertest.cc
@@ -75,8 +75,9 @@
     for (const std::string& dict : dictionaries) {
       dictionaries_value.Append(dict);
     }
-    EXPECT_TRUE(dictionaries_value.Equals(menu()->GetPrefs()->GetList(
-        spellcheck::prefs::kSpellCheckDictionaries)));
+    EXPECT_EQ(dictionaries_value,
+              *menu()->GetPrefs()->GetList(
+                  spellcheck::prefs::kSpellCheckDictionaries));
   }
 
   MockRenderViewContextMenu* menu() { return menu_.get(); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index e5896369..58b6b162 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -39,7 +39,6 @@
   "background/command_handler_interface.js",
   "background/custom_automation_event.js",
   "background/editing/editable_line.js",
-  "background/editing/intent_handler.js",
   "background/event_source.js",
   "background/gesture_command_data.js",
   "background/keyboard_handler.js",
@@ -116,6 +115,7 @@
   "background/earcon_engine.js",
   "background/earcons.js",
   "background/editing/editing.js",
+  "background/editing/intent_handler.js",
   "background/es6_loader.js",
   "background/find_handler.js",
   "background/focus_automation_handler.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
index 2a12407..3c8efb7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -6,7 +6,9 @@
  * @fileoverview Processes events related to editing text and emits the
  * appropriate spoken and braille feedback.
  */
+
 import {Color} from '../color.js';
+import {IntentHandler} from './intent_handler.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationIntent = chrome.automation.AutomationIntent;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
index b8c35f8..98aa6e0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler.js
@@ -7,14 +7,6 @@
  * Braille is *not* handled in this module.
  */
 
-goog.provide('IntentHandler');
-
-goog.require('constants');
-goog.require('editing.EditableLine');
-goog.require('Msgs');
-goog.require('Output');
-
-goog.scope(function() {
 const AutomationIntent = chrome.automation.AutomationIntent;
 const Cursor = cursors.Cursor;
 const Dir = constants.Dir;
@@ -28,7 +20,7 @@
 /**
  * A stateless class that turns intents into speech.
  */
-IntentHandler = class {
+export class IntentHandler {
   /**
    * Called when intents are received from an AutomationEvent.
    * @param {!Array<AutomationIntent>} intents
@@ -203,5 +195,4 @@
 
     return false;
   }
-};
-});  // goog.scope
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
index cbecb180..eaa5ff25 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/intent_handler_test.js
@@ -15,11 +15,14 @@
  */
 ChromeVoxIntentHandlerTest = class extends ChromeVoxNextE2ETest {
   /** @override */
-  setUp() {
+  async setUpDeferred() {
+    await super.setUpDeferred();
     window.Dir = constants.Dir;
     window.IntentTextBoundaryType = chrome.automation.IntentTextBoundaryType;
     window.Movement = cursors.Movement;
     window.Unit = cursors.Unit;
+    await importModule(
+        'IntentHandler', '/chromevox/background/editing/intent_handler.js');
   }
 };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index 91b6105..7eba11d7 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -35,7 +35,6 @@
 goog.require('ExtensionBridge');
 goog.require('GestureCommandData');
 goog.require('GestureGranularity');
-goog.require('IntentHandler');
 goog.require('JaPhoneticMap');
 goog.require('KeyCode');
 goog.require('LibLouis.FormType');
diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html
index 277adc3..48b35de 100644
--- a/chrome/browser/resources/chromeos/drive_internals.html
+++ b/chrome/browser/resources/chromeos/drive_internals.html
@@ -34,12 +34,6 @@
         </label>
       </div>
       <div>
-        <label>
-          Mirroring
-          <input type="checkbox" id="mirroring-toggle">
-        </label>
-      </div>
-      <div>
         Tracing
         <button id="button-enable-tracing">Enable</button>
         <button id="button-disable-tracing">Disable</button>
@@ -97,6 +91,23 @@
       </ul>
     </section>
 
+    <section id="mirror-sync-section" hidden>
+      <h2>MirrorSync</h2>
+      <label>
+        Enable Mirroring
+        <input type="checkbox" id="mirroring-toggle">
+      </label>
+      <form id="mirror-path-form" hidden>
+        <input type="text" id="mirror-path-input">
+        <button type="submit">Add Sync Path</button>
+        <span id="mirroring-path-status"></span>
+      </form>
+      <table>
+        <tbody id="mirror-sync-paths" hidden>
+        </tbody>
+      </table>
+    </section>
+
     <section id="delta-update-status-section" hidden>
       <h2>Delta Update Status</h2>
       <table>
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js
index 50a03da..81451cc 100644
--- a/chrome/browser/resources/chromeos/drive_internals.js
+++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -217,6 +217,54 @@
 }
 
 /**
+ * Adds a new row to the syncing paths table upon successful completion.
+ * @param {string} path The path that was synced.
+ * @param {string} status The drive::FileError as a string.
+ */
+function onAddSyncPath(path, status) {
+  $('mirroring-path-status').textContent = status;
+  if (status !== 'FILE_ERROR_OK') {
+    return;
+  }
+
+  // Avoid adding paths to the table if they already exist.
+  if ($(`mirroring-${path}`)) {
+    return;
+  }
+
+  const newRow = document.createElement('tr');
+  newRow.id = `mirroring-${path}`;
+  const deleteButton = createElementFromText('button', 'Delete');
+  deleteButton.addEventListener('click', function(e) {
+    e.preventDefault();
+    chrome.send('removeSyncPath', [path]);
+  });
+  const deleteCell = document.createElement('td');
+  deleteCell.appendChild(deleteButton);
+  newRow.appendChild(deleteCell);
+  const pathCell = createElementFromText('td', path);
+  newRow.appendChild(pathCell);
+  $('mirror-sync-paths').appendChild(newRow);
+}
+
+/**
+ * Remove a path from the syncing table.
+ * @param {string} path The path that was synced.
+ * @param {string} status The drive::FileError as a string.
+ */
+function onRemoveSyncPath(path, status) {
+  if (status !== 'FILE_ERROR_OK') {
+    return;
+  }
+
+  if (!$(`mirroring-${path}`)) {
+    return;
+  }
+
+  $(`mirroring-${path}`).remove();
+}
+
+/**
  * Creates an element named |elementName| containing the content |text|.
  * @param {string} elementName Name of the new element to be created.
  * @param {string} text Text to be contained in the new element.
@@ -319,6 +367,12 @@
     chrome.send('setStartupArguments', [$('startup-arguments-input').value]);
   });
 
+  $('mirror-path-form').addEventListener('submit', function(e) {
+    e.preventDefault();
+    $('mirroring-path-status').textContent = 'adding...';
+    chrome.send('addSyncPath', [$('mirror-path-input').value]);
+  });
+
   $('button-enable-tracing').addEventListener('click', function() {
     chrome.send('enableTracing');
   });
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
index 87f310b..af94d252 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
@@ -214,7 +214,8 @@
         </div>
 
         <!-- Usage stats toggle row -->
-        <div id="usageStats" class="layout horizontal center oobe-optin-row">
+        <div id="usageStats" class="layout horizontal center oobe-optin-row"
+            hidden="[[usageOptinHidden_]]">
           <div class="oobe-optin-content flex">
             <span id="usageTitle" class="oobe-optin-title">
               [[i18nDynamic(locale, 'consolidatedConsentUsageOptInTitle')]]
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
index 8fd3d3f..161a1be 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
@@ -86,6 +86,11 @@
         value: false,
       },
 
+      usageOptinHidden_: {
+        type: Boolean,
+        value: false,
+      },
+
       backupManaged_: {
         type: Boolean,
         value: false,
@@ -160,6 +165,7 @@
             'setBackupMode',
             'setLocationMode',
             'setIsDeviceOwner',
+            'setUsageOptinHidden',
     ];
   }
   // clang-format on
@@ -337,7 +343,8 @@
 
     var tosLoader = new WebViewLoader(
         webview, CONSOLIDATED_CONSENT_ONLINE_LOAD_TIMEOUT_IN_MS,
-        loadFailureCallback, false /* clear_anchors */, false /* inject_css */);
+        loadFailureCallback, this.isDemo_ /* clear_anchors */,
+        false /* inject_css */);
     tosLoader.setUrl(online_tos_url);
   }
 
@@ -384,7 +391,8 @@
 
     var tosLoader = new WebViewLoader(
         webview, CONSOLIDATED_CONSENT_ONLINE_LOAD_TIMEOUT_IN_MS,
-        loadFailureCallback, false /* clear_anchors */, false /* inject_css */);
+        loadFailureCallback, this.isDemo_ /* clear_anchors */,
+        false /* inject_css */);
     tosLoader.setUrl(online_tos_url);
   }
 
@@ -601,6 +609,13 @@
   }
 
   /**
+   * Hides the entire usage opt-in.
+   */
+  setUsageOptinHidden() {
+    this.usageOptinHidden_ = true;
+  }
+
+  /**
    * Sets current backup and restore mode.
    * @param {boolean} enabled Defines the state of backup opt in.
    * @param {boolean} managed Defines whether this setting is set by policy.
@@ -622,7 +637,7 @@
 
   /**
    * Sets isOwner_ property.
-   * @param {boolean} isOwner Defines whether the current user is the  device
+   * @param {boolean} isOwner Defines whether the current user is the device
    *     owner.
    */
   setIsDeviceOwner(isOwner) {
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/OWNERS b/chrome/browser/resources/chromeos/multidevice_internals/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/resources/chromeos/multidevice_internals/OWNERS
+++ b/chrome/browser/resources/chromeos/multidevice_internals/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
index 6e60a75..ed68185a 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
+++ b/chrome/browser/resources/chromeos/multidevice_setup/BUILD.gn
@@ -26,7 +26,7 @@
     excludes = [
       "chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js",
       "chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js",
-      "chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js",
+      "chrome://resources/mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js",
       "chrome://resources/mojo/ash/services/device_sync/public/mojom/device_sync.mojom-lite.js",
       "chrome://resources/mojo/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-lite.js",
     ]
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/OWNERS b/chrome/browser/resources/chromeos/multidevice_setup/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/OWNERS
+++ b/chrome/browser/resources/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/resources/discards/BUILD.gn b/chrome/browser/resources/discards/BUILD.gn
index c63b532..4d51020 100644
--- a/chrome/browser/resources/discards/BUILD.gn
+++ b/chrome/browser/resources/discards/BUILD.gn
@@ -34,6 +34,7 @@
 
 ts_files = [
              "discards.ts",
+             "graph_doc.ts",
              "sorted_table_mixin.ts",
            ] + web_component_files
 
@@ -47,15 +48,9 @@
   out_folder = "$target_gen_dir/$preprocess_folder"
 }
 
-# Intentionally excluding graph_tab_template.html.ts since it will be placed in
-# the folder by generate_graph_tab.
 preprocess_if_expr("preprocess_gen") {
   in_folder = target_gen_dir
-  in_files = [
-    "discards_main.html.ts",
-    "discards_tab.html.ts",
-    "database_tab.html.ts",
-  ]
+  in_files = html_wrapper_files
   out_folder = "$target_gen_dir/$preprocess_folder"
   deps = [ ":html_wrapper_files" ]
 }
@@ -66,21 +61,20 @@
 action("generate_graph_tab") {
   script = "generate_graph_tab.py"
   sources = [
-    "graph_doc.js",
+    "graph_doc.ts",
     "graph_doc_template.html",
     "graph_tab_template.html",
   ]
-  outputs = [ "$target_gen_dir/$preprocess_folder/graph_tab_template.html.ts" ]
+  outputs = [ "$target_gen_dir/graph_tab/graph_tab_template.html.js" ]
 
   args = rebase_path(outputs, root_build_dir) +
+         rebase_path([ "graph_doc_template.html" ], root_build_dir) +
          rebase_path([
-                       "graph_doc_template.html",
-                       "graph_doc.js",
+                       "$target_gen_dir/tsc/graph_doc.js",
+                       "$target_gen_dir/tsc/graph_tab_template.html.js",
                      ],
-                     root_build_dir) +
-         rebase_path([ "$target_gen_dir/graph_tab_template.html.ts" ],
                      root_build_dir)
-  deps = [ ":html_wrapper_files" ]
+  deps = [ ":build_ts" ]
 }
 
 # Action with a transparent name.
@@ -108,6 +102,10 @@
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/$tsc_folder"
   tsconfig_base = "tsconfig_base.json"
+  manifest_excludes = [
+    "graph_doc.ts",
+    "graph_tab_template.html.ts",
+  ]
   in_files = ts_files + html_wrapper_files + [
                "discards.mojom-webui.js",
                "site_data.mojom-webui.js",
@@ -121,17 +119,29 @@
 
   extra_deps = [
     ":copy_mojo",
-    ":generate_graph_tab",
     ":preprocess",
     ":preprocess_gen",
   ]
 }
 
+generate_grd("build_graph_tab_grdp") {
+  input_files = [ "graph_tab_template.html.js" ]
+  input_files_base_dir =
+      rebase_path("$target_gen_dir/graph_tab", root_build_dir)
+  deps = [ ":generate_graph_tab" ]
+  grd_prefix = "discards"
+  out_grd = "$target_gen_dir/discards_graph_tab.grdp"
+}
+
 generate_grd("build_grd") {
   input_files = [ "discards.html" ]
   input_files_base_dir = rebase_path(".", "//")
-  deps = [ ":build_ts" ]
+  deps = [
+    ":build_graph_tab_grdp",
+    ":build_ts",
+  ]
   manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+  grdp_files = [ "$target_gen_dir/discards_graph_tab.grdp" ]
   grd_prefix = "discards"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
 }
diff --git a/chrome/browser/resources/discards/generate_graph_tab.py b/chrome/browser/resources/discards/generate_graph_tab.py
index 5c60ee4..026e495 100644
--- a/chrome/browser/resources/discards/generate_graph_tab.py
+++ b/chrome/browser/resources/discards/generate_graph_tab.py
@@ -12,13 +12,17 @@
 import sys
 
 
-def strip_js_imports(js_contents):
-  """The input JS may use imports for Closure compilation. These must be
-  stripped from the output since the resulting data: URL cannot use imports
-  within its webview."""
-  def not_an_import(line):
-    return not line.startswith('import ')
-  return '\n'.join(filter(not_an_import, js_contents.splitlines()))
+def strip_js_exports(js_contents):
+  """The input JS may use imports for TS compilation, but these should be used
+  for types only and therefore should be stripped by TS compiler.
+  TS compiler also generates an "export" statement - remove this. """
+  lines = js_contents.splitlines();
+  for line in lines:
+    assert not line.startswith('import ')
+
+  def not_an_export(line):
+    return not line.startswith('export ');
+  return '\n'.join(filter(not_an_export, lines))
 
 
 def main():
@@ -33,18 +37,24 @@
   # Slurp the input files.
   js_file_contents = open(args.javascript_file, 'r').read();
   html_template = string.Template(open(args.html_template, 'r').read());
-  output_template = string.Template(open(args.output_template, 'r').read());
+
+  # Note: Don't turn this into a template. It is a .html.js file output from the
+  # TS compiler. TS compiler complains about not finding values if it sees
+  # "${foo}" template syntax, so the input html file needs to use a different
+  # pattern (see DATA_URL_PLACEHOLDER below).
+  output_template = open(args.output_template, 'r').read();
 
   # Stamp the javacript contents into the HTML template.
   html_doc = html_template.substitute(
-      {'javascript_file': strip_js_imports(js_file_contents)})
+      {'javascript_file': strip_js_exports(js_file_contents)})
 
   # Construct the data: URL that contains the combined doc.
   data_url = "data:text/html;base64,%s" % base64.b64encode(
       html_doc.encode()).decode()
 
   # And finally stamp the the data URL into the output template.
-  output = output_template.safe_substitute({'data_url': data_url})
+  DATA_URL_PLACEHOLDER = '{__data_url__}';
+  output = output_template.replace(DATA_URL_PLACEHOLDER, data_url);
 
   current_contents = ''
   if os.path.isfile(args.output_file):
diff --git a/chrome/browser/resources/discards/graph_doc.js b/chrome/browser/resources/discards/graph_doc.ts
similarity index 61%
rename from chrome/browser/resources/discards/graph_doc.js
rename to chrome/browser/resources/discards/graph_doc.ts
index ae6f0b1..18eb6dc7 100644
--- a/chrome/browser/resources/discards/graph_doc.js
+++ b/chrome/browser/resources/discards/graph_doc.ts
@@ -7,66 +7,59 @@
 // WebUI is through postMessage.
 
 // Note that these imports are stripped by a build step before being packaged.
-// They're only present to help Closure compiler do type checks and must be
-// referenced only within Closure annotations.
-import {FavIconInfo, FrameInfo, GraphChangeStreamInterface, PageInfo, ProcessInfo, WorkerInfo} from './chrome/browser/ui/webui/discards/discards.mojom-webui.js';
+// These are used for types only.
+import {FavIconInfo, FrameInfo, GraphChangeStreamInterface, PageInfo, ProcessInfo, WorkerInfo} from './discards.mojom-webui.js';
 
 // Radius of a node circle.
-const /** number */ kNodeRadius = 6;
+const kNodeRadius: number = 6;
 
 // Target y position for page nodes.
-const /** number */ kPageNodesTargetY = 20;
+const kPageNodesTargetY: number = 20;
 
 // Range occupied by page nodes at the top of the graph view.
-const /** number */ kPageNodesYRange = 100;
+const kPageNodesYRange: number = 100;
 
 // Range occupied by process nodes at the bottom of the graph view.
-const /** number */ kProcessNodesYRange = 100;
+const kProcessNodesYRange: number = 100;
 
 // Range occupied by worker nodes at the bottom of the graph view, above
 // process nodes.
-const /** number */ kWorkerNodesYRange = 200;
+const kWorkerNodesYRange: number = 200;
 
 // Target y position for frame nodes.
-const /** number */ kFrameNodesTargetY = kPageNodesYRange + 50;
+const kFrameNodesTargetY: number = kPageNodesYRange + 50;
 
 // Range that frame nodes cannot enter at the top/bottom of the graph view.
-const /** number */ kFrameNodesTopMargin = kPageNodesYRange;
-const /** number */ kFrameNodesBottomMargin = kWorkerNodesYRange + 50;
+const kFrameNodesTopMargin: number = kPageNodesYRange;
+const kFrameNodesBottomMargin: number = kWorkerNodesYRange + 50;
 
 // The maximum strength of a boundary force.
 // According to https://github.com/d3/d3-force#positioning, strength values
 // outside the range [0,1] are "not recommended".
-const /** number */ kMaxBoundaryStrength = 1;
+const kMaxBoundaryStrength: number = 1;
 
 // The strength of a high Y-force. This is appropriate for forces that
 // strongly pull towards an attractor, but can still be overridden by the
 // strongest force.
-const /** number */ kHighYStrength = 0.9;
+const kHighYStrength: number = 0.9;
 
 // The strength of a weak Y-force. This is appropriate for forces that exert
 // some influence but can be easily overridden.
-const /** number */ kWeakYStrength = 0.1;
+const kWeakYStrength: number = 0.1;
 
 class ToolTip {
-  /**
-   * @param {Element} div
-   * @param {GraphNode} node
-   */
-  constructor(div, node) {
-    /** @type {boolean} */
-    this.floating = true;
+  floating: boolean = true;
+  x: number;
+  y: number;
+  node: GraphNode;
+  private div_: d3.Selection<HTMLDivElement, unknown, null, undefined>;
+  private description_json_: string = '';
 
-    /** @type {number} */
+  constructor(div: Element, node: GraphNode) {
     this.x = node.x;
-
-    /** @type {number} */
     this.y = node.y - 28;
-
-    /** @type {GraphNode} */
     this.node = node;
 
-    /** @private {d3.selection} */
     this.div_ = d3.select(div)
                     .append('div')
                     .attr('class', 'tooltip')
@@ -76,10 +69,9 @@
     this.div_.append('table').append('tbody');
     this.div_.transition().duration(200).style('opacity', .9);
 
-    /** @private {string} */
-    this.description_json_ = '';
     // Set up a drag behavior for this object's div.
-    const drag = d3.drag().subject(() => this);
+    const drag = d3.drag().subject(() => this) as unknown as
+        d3.DragBehavior<HTMLDivElement, unknown, unknown>;
     drag.on('start', this.onDragStart_.bind(this));
     drag.on('drag', this.onDrag_.bind(this));
     this.div_.call(drag);
@@ -99,11 +91,10 @@
   }
 
   /**
-   * @return {!Array<number>} The [x, y] center of the ToolTip's div
-   * element.
+   * @return The [x, y] center of the ToolTip's div element.
    */
-  getCenter() {
-    const rect = this.div_.node().getBoundingClientRect();
+  getCenter(): [number, number] {
+    const rect = this.div_.node()!.getBoundingClientRect();
     return [rect.x + rect.width / 2, rect.y + rect.height / 2];
   }
 
@@ -113,9 +104,8 @@
 
   /**
    * Updates the description displayed.
-   * @param {string} description_json A JSON string.
    */
-  onDescription(description_json) {
+  onDescription(description_json: string) {
     if (this.description_json_ === description_json) {
       return;
     }
@@ -123,13 +113,15 @@
     /**
      * Helper for recursively flattening an Object.
      *
-     * @param {!Set} visited The set of visited objects, excluding
+     * @param visited The set of visited objects, excluding
      *          {@code object}.
-     * @param {!Object<?,?>} flattened The flattened object being built.
-     * @param {string} path The current flattened path.
-     * @param {!Object<?,?>} object The nested dict to be flattened.
+     * @param flattened The flattened object being built.
+     * @param path The current flattened path.
+     * @param object The nested dict to be flattened.
      */
-    function flattenObjectRec(visited, flattened, path, object) {
+    function flattenObjectRec(
+        visited: Set<object>, flattened: {[key: string]: any}, path: string,
+        object: {[key: string]: any}) {
       if (typeof object !== 'object' || visited.has(object)) {
         return;
       }
@@ -176,10 +168,8 @@
      *   'baz.z.0': '1',
      *   'baz.y.1': '2'
      * }
-     * @param {!Object<?,?>} object The object to be flattened.
-     * @return {!Object<?,?>} the flattened object.
      */
-    function flattenObject(object) {
+    function flattenObject(object: {[key: string]: any}): {[key: string]: any} {
       const flattened = {};
       flattenObjectRec(new Set(), flattened, '', object);
       return flattened;
@@ -191,12 +181,11 @@
     // 'heading' with [`the describer's name`, null], followed by some number of
     // entries with a two-element list, each representing a key/value pair.
     this.description_json_ = description_json;
-    const description =
-        /** @type {!Object<?,?>} */ (JSON.parse(description_json));
+    const description = JSON.parse(description_json);
     const flattenedDescription = [];
     for (const [title, value] of Object.entries(description)) {
       flattenedDescription.push([title, null]);
-      const flattenedValue = flattenObject(value);
+      const flattenedValue = flattenObject(value as {[key: string]: any});
       for (const [propName, propValue] of Object.entries(flattenedValue)) {
         let strValue = String(propValue);
         if (strValue.length > 50) {
@@ -215,71 +204,67 @@
     tr.exit().remove();
 
     tr = this.div_.selectAll('tr');
-    tr.select('td').attr('colspan', function(d) {
-      return (d3.select(this.parentElement).datum()[1] === null) ? 2 : null;
+    tr.select('td').attr('colspan', function(_d: any) {
+      return ((d3.select((this as HTMLElement).parentElement!).datum() as
+               Array<any>)[1] === null) ?
+          2 :
+          null;
     });
-    tr = tr.attr('class', d => d[1] === null ? 'heading' : 'value');
-    tr.selectAll('td').data(d => d).text(d => d === null ? '' : d);
+    tr = tr.attr(
+        'class',
+        (d: unknown) =>
+            (d as Array<string|null>)[1] === null ? 'heading' : 'value');
+    tr.selectAll('td').data(d => d).text(
+        (d: unknown) => d === null ? '' : d as string);
   }
 
-  /** @private */
-  onDragStart_() {
+  private onDragStart_() {
     this.floating = false;
   }
 
-  /** @private */
-  onDrag_() {
+  private onDrag_() {
     this.x = d3.event.x;
     this.y = d3.event.y;
     this.div_.style('left', `${this.x}px`).style('top', `${this.y}px`);
 
-    graph.updateToolTipLinks();
+    graph!.updateToolTipLinks();
   }
 }
 
-/** @implements {d3.ForceNode} */
-class GraphNode {
-  constructor(id) {
-    /** @type {bigint} */
+class GraphNode implements d3.SimulationNodeDatum {
+  id: bigint;
+  color: string = 'black';
+  iconUrl: string = '';
+  tooltip: ToolTip|null = null;
+
+  /**
+   * Implementation of the d3.SimulationNodeDatum interface.
+   * See https://github.com/d3/d3-force#simulation_nodes.
+   */
+  index?: number;
+  x: number;
+  y: number;
+  vx?: number;
+  vy?: number;
+  fx: number|null = null;
+  fy: number|null = null;
+
+
+  constructor(id: bigint) {
     this.id = id;
-    /** @type {string} */
-    this.color = 'black';
-    /** @type {string} */
-    this.iconUrl = '';
-
-    /** @type {ToolTip} */
-    this.tooltip = null;
-
-    /**
-     * Implementation of the d3.ForceNode interface.
-     * See https://github.com/d3/d3-force#simulation_nodes.
-     * @type {number|undefined}
-     */
-    this.index;
-    /** @type {number} */
-    this.x;
-    /** @type {number} */
-    this.y;
-    /** @type {number|undefined} */
-    this.vx;
-    /** @type {number|undefined} */
-    this.vy;
-    this.fx = null;
-    this.fy = null;
   }
 
-  /** @return {string} */
-  get title() {
+  get title(): string {
     return '';
   }
 
   /**
    * Sets the initial x and y position of this node, also resets
    * vx and vy.
-   * @param {number} graphWidth Width of the graph view (svg).
-   * @param {number} graphHeight Height of the graph view (svg).
+   * @param graphWidth Width of the graph view (svg).
+   * @param graphHeight Height of the graph view (svg).
    */
-  setInitialPosition(graphWidth, graphHeight) {
+  setInitialPosition(graphWidth: number, graphHeight: number) {
     this.x = graphWidth / 2;
     this.y = this.targetYPosition(graphHeight);
     this.vx = 0;
@@ -287,46 +272,44 @@
   }
 
   /**
-   * @param {number} graphHeight Height of the graph view (svg).
-   * @return {number}
+   * @param graphHeight Height of the graph view (svg).
    */
-  targetYPosition(graphHeight) {
+  targetYPosition(graphHeight: number): number {
     const bounds = this.allowedYRange(graphHeight);
     return (bounds[0] + bounds[1]) / 2;
   }
 
   /**
-   * @return {number} The strength of the force that pulls the node towards
+   * @return The strength of the force that pulls the node towards
    *     its target y position.
    */
-  get targetYPositionStrength() {
+  get targetYPositionStrength(): number {
     return kWeakYStrength;
   }
 
   /**
-   * @return {number} A scaling factor applied to the strength of links to this
+   * @return A scaling factor applied to the strength of links to this
    *     node.
    */
-  get linkStrengthScalingFactor() {
+  get linkStrengthScalingFactor(): number {
     return 1;
   }
 
   /**
-   * @param {number} graphHeight Height of the graph view.
-   * @return {!Array<number>}
+   * @param graphHeight Height of the graph view.
    */
-  allowedYRange(graphHeight) {
+  allowedYRange(graphHeight: number): [number, number] {
     // By default, nodes just need to be in bounds of the graph.
     return [0, graphHeight];
   }
 
-  /** @return {number} The strength of the repulsion force with other nodes. */
-  get manyBodyStrength() {
+  /** @return The strength of the repulsion force with other nodes. */
+  get manyBodyStrength(): number {
     return -200;
   }
 
-  /** @return {!Array<bigint>} an array of node ids. */
-  get linkTargets() {
+  /** @return an array of node ids. */
+  get linkTargets(): bigint[] {
     return [];
   }
 
@@ -335,18 +318,17 @@
    * things, but be owned by exactly one (per relationship type). As such, the
    * relationship is expressed on the *owned* object. These links are drawn with
    * an arrow at the beginning of the link, pointing to the owned object.
-   * @return {!Array<bigint>} an array of node ids.
+   * @return an array of node ids.
    */
-  get dashedLinkTargets() {
+  get dashedLinkTargets(): bigint[] {
     return [];
   }
 
   /**
    * Selects a color string from an id.
-   * @param {bigint} id The id the returned color is selected from.
-   * @return {string}
+   * @param id The id the returned color is selected from.
    */
-  selectColor(id) {
+  selectColor(id: bigint): string {
     if (id < 0) {
       id = -id;
     }
@@ -355,47 +337,41 @@
 }
 
 class PageNode extends GraphNode {
-  /** @param {!PageInfo} page */
-  constructor(page) {
+  page: PageInfo;
+
+  constructor(page: PageInfo) {
     super(page.id);
-    /** @type {!PageInfo} */
     this.page = page;
     this.y = kPageNodesTargetY;
   }
 
-  /** override */
-  get title() {
+  override get title() {
     return this.page.mainFrameUrl.url.length > 0 ? this.page.mainFrameUrl.url :
                                                    'Page';
   }
 
-  /** @override */
-  get targetYPositionStrength() {
+  override get targetYPositionStrength() {
     // Gravitate strongly towards the top of the graph. Can be overridden by
     // the bounding force which uses kMaxBoundaryStrength.
     return kHighYStrength;
   }
 
-  /** @override */
-  get linkStrengthScalingFactor() {
+  override get linkStrengthScalingFactor() {
     // Give links from frame nodes to page nodes less weight than links between
     // frame nodes, so the that Y forces pulling page nodes into their area can
     // dominate over link forces pulling them towards frame nodes.
     return 0.5;
   }
 
-  /** override */
-  allowedYRange(graphHeight) {
+  override allowedYRange(_graphHeight: number): [number, number] {
     return [0, kPageNodesYRange];
   }
 
-  /** override */
-  get manyBodyStrength() {
+  override get manyBodyStrength() {
     return -600;
   }
 
-  /** override */
-  get dashedLinkTargets() {
+  override get dashedLinkTargets() {
     const targets = [];
     if (this.page.openerFrameId) {
       targets.push(this.page.openerFrameId);
@@ -408,31 +384,27 @@
 }
 
 class FrameNode extends GraphNode {
-  /** @param {!FrameInfo} frame */
-  constructor(frame) {
+  frame: FrameInfo;
+
+  constructor(frame: FrameInfo) {
     super(frame.id);
-    /** @type {!FrameInfo} frame */
     this.frame = frame;
     this.color = this.selectColor(frame.processId);
   }
 
-  /** override */
-  get title() {
+  override get title() {
     return this.frame.url.url.length > 0 ? this.frame.url.url : 'Frame';
   }
 
-  /** override */
-  targetYPosition(graphHeight) {
+  override targetYPosition(_graphHeight: number) {
     return kFrameNodesTargetY;
   }
 
-  /** override */
-  allowedYRange(graphHeight) {
+  override allowedYRange(graphHeight: number): [number, number] {
     return [kFrameNodesTopMargin, graphHeight - kFrameNodesBottomMargin];
   }
 
-  /** override */
-  get linkTargets() {
+  override get linkTargets() {
     // Only link to the page if there isn't a parent frame.
     return [
       this.frame.parentFrameId || this.frame.pageId, this.frame.processId
@@ -441,82 +413,72 @@
 }
 
 class ProcessNode extends GraphNode {
-  /** @param {!ProcessInfo} process */
-  constructor(process) {
+  process: ProcessInfo;
+
+  constructor(process: ProcessInfo) {
     super(process.id);
-    /** @type {!ProcessInfo} */
     this.process = process;
 
     this.color = this.selectColor(process.id);
   }
 
-  /** override */
-  get title() {
+  override get title() {
     return `PID: ${this.process.pid.pid}`;
   }
 
-  /** @return {number} */
-  get targetYPositionStrength() {
+  override get targetYPositionStrength() {
     // Gravitate strongly towards the bottom of the graph. Can be overridden by
     // the bounding force which uses kMaxBoundaryStrength.
     return kHighYStrength;
   }
 
-  /** @override */
-  get linkStrengthScalingFactor() {
+  override get linkStrengthScalingFactor() {
     // Give links to process nodes less weight than links between frame nodes,
     // so the that Y forces pulling process nodes into their area can dominate
     // over link forces pulling them towards frame nodes.
     return 0.5;
   }
 
-  /** override */
-  allowedYRange(graphHeight) {
+  override allowedYRange(graphHeight: number): [number, number] {
     return [graphHeight - kProcessNodesYRange, graphHeight];
   }
 
-  /** override */
-  get manyBodyStrength() {
+  override get manyBodyStrength() {
     return -600;
   }
 }
 
 class WorkerNode extends GraphNode {
-  /** @param {!WorkerInfo} worker */
-  constructor(worker) {
+  worker: WorkerInfo;
+
+  constructor(worker: WorkerInfo) {
     super(worker.id);
-    /** @type {!WorkerInfo} */
     this.worker = worker;
 
     this.color = this.selectColor(worker.processId);
   }
 
-  /** override */
-  get title() {
+  override get title() {
     return this.worker.url.url.length > 0 ? this.worker.url.url : 'Worker';
   }
 
-  /** @return {number} */
-  get targetYPositionStrength() {
+  override get targetYPositionStrength() {
     // Gravitate strongly towards the worker area of the graph. Can be
     // overridden by the bounding force which uses kMaxBoundaryStrength.
     return kHighYStrength;
   }
 
-  /** override */
-  allowedYRange(graphHeight) {
+  override allowedYRange(graphHeight: number): [number, number] {
     return [
       graphHeight - kWorkerNodesYRange, graphHeight - kProcessNodesYRange
     ];
   }
 
-  /** override */
-  get manyBodyStrength() {
+  override get manyBodyStrength() {
     return -600;
   }
 
-  /** override */
-  get linkTargets() {
+  override get linkTargets() {
     // Link the process, in addition to all the client and child workers.
     return [
       this.worker.processId, ...this.worker.clientFrameIds,
@@ -528,20 +490,16 @@
 /**
  * A force that bounds GraphNodes |allowedYRange| in Y,
  * as well as bounding them to stay in page bounds in X.
- * @param {number} graphHeight
- * @param {number} graphWidth
  */
-function boundingForce(graphHeight, graphWidth) {
-  /** @type {!Array<!GraphNode>} */
-  let nodes = [];
-  /** @type {!Array<!Array>} */
-  let bounds = [];
-  const xBounds = [2 * kNodeRadius, graphWidth - 2 * kNodeRadius];
-  const boundPosition = (pos, bound) =>
+function boundingForce(graphHeight: number, graphWidth: number) {
+  let nodes: GraphNode[] = [];
+  let bounds: Array<[number, number]> = [];
+  const xBounds: [number, number] =
+      [2 * kNodeRadius, graphWidth - 2 * kNodeRadius];
+  const boundPosition = (pos: number, bound: [number, number]) =>
       Math.max(bound[0], Math.min(pos, bound[1]));
 
-  /** @param {number} alpha */
-  function force(alpha) {
+  function force(_alpha: number) {
     const n = nodes.length;
     for (let i = 0; i < n; ++i) {
       const bound = bounds[i];
@@ -549,26 +507,25 @@
 
       // Calculate where the node will end up after movement. If it will be out
       // of bounds apply a counter-force to bring it back in.
-      const yNextPosition = node.y + node.vy;
+      const yNextPosition = node.y + node.vy!;
       const yBoundedPosition = boundPosition(yNextPosition, bound);
       if (yNextPosition !== yBoundedPosition) {
         // Do not include alpha because we want to be strongly repelled from
         // the boundary even if alpha has decayed.
-        node.vy += (yBoundedPosition - yNextPosition) * kMaxBoundaryStrength;
+        node.vy! += (yBoundedPosition - yNextPosition) * kMaxBoundaryStrength;
       }
 
-      const xNextPosition = node.x + node.vx;
+      const xNextPosition = node.x + node.vx!;
       const xBoundedPosition = boundPosition(xNextPosition, xBounds);
       if (xNextPosition !== xBoundedPosition) {
         // Do not include alpha because we want to be strongly repelled from
         // the boundary even if alpha has decayed.
-        node.vx += (xBoundedPosition - xNextPosition) * kMaxBoundaryStrength;
+        node.vx! += (xBoundedPosition - xNextPosition) * kMaxBoundaryStrength;
       }
     }
   }
 
-  /** @param {!Array<!GraphNode>} n */
-  force.initialize = function(n) {
+  force.initialize = function(n: GraphNode[]) {
     nodes = n;
     bounds = nodes.map(node => {
       const nodeBounds = node.allowedYRange(graphHeight);
@@ -582,99 +539,42 @@
   return force;
 }
 
-/**
- * @implements {GraphChangeStreamInterface}
- */
-class Graph {
-  /**
-   * TODO(siggi): This should be SVGElement, but closure doesn't have externs
-   *    for this yet.
-   * @param {Element} svg
-   * @param {Element} div
-   */
-  constructor(svg, div) {
-    /**
-     * TODO(siggi): SVGElement.
-     * @private {Element}
-     */
+class Graph implements GraphChangeStreamInterface {
+  private svg_: SVGElement;
+  private div_: Element;
+  private wasResized_: boolean = false;
+  private width_: number = 0;
+  private height_: number = 0;
+  private simulation_: d3.Simulation<GraphNode, undefined>|null = null;
+  /** A selection for the top-level <g> node that contains all tooltip links. */
+  private toolTipLinkGroup_:
+      d3.Selection<SVGGElement, unknown, null, undefined>|null = null;
+  /** A selection for the top-level <g> node that contains all separators. */
+  private separatorGroup_: d3.Selection<SVGGElement, unknown, null, undefined>|
+      null = null;
+  /** A selection for the top-level <g> node that contains all nodes. */
+  private nodeGroup_: d3.Selection<SVGGElement, unknown, null, undefined>|null =
+      null;
+  /** A selection for the top-level <g> node that contains all edges. */
+  private linkGroup_: d3.Selection<
+      SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>|null =
+      null;
+  /** A selection for the top-level <g> node that contains all dashed edges. */
+  private dashedLinkGroup_: d3.Selection<
+      SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>|null =
+      null;
+  private nodes_: Map<bigint, GraphNode> = new Map();
+  private links_: d3.SimulationLinkDatum<GraphNode>[] = [];
+  private dashedLinks_: d3.SimulationLinkDatum<GraphNode>[] = [];
+  private hostWindow_: Window|null = null;
+  /** The interval timer used to poll for node descriptions. */
+  private pollDescriptionsInterval_: number = 0;
+  /** The d3.drag instance applied to nodes. */
+  private drag_: d3.DragBehavior<SVGGElement, GraphNode, unknown>|null = null;
+
+  constructor(svg: SVGElement, div: Element) {
     this.svg_ = svg;
-
-    /** @private {Element} */
     this.div_ = div;
-
-    /** @private {boolean} */
-    this.wasResized_ = false;
-
-    /** @private {number} */
-    this.width_ = 0;
-    /** @private {number} */
-    this.height_ = 0;
-
-    /** @private {d3.ForceSimulation} */
-    this.simulation_ = null;
-
-    /**
-     * A selection for the top-level <g> node that contains all tooltip links.
-     * @private {d3.selection}
-     */
-    this.toolTipLinkGroup_ = null;
-
-    /**
-     * A selection for the top-level <g> node that contains all separators.
-     * @private {d3.selection}
-     */
-    this.separatorGroup_ = null;
-
-    /**
-     * A selection for the top-level <g> node that contains all nodes.
-     * @private {d3.selection}
-     */
-    this.nodeGroup_ = null;
-
-    /**
-     * A selection for the top-level <g> node that contains all edges.
-     * @private {d3.selection}
-     */
-    this.linkGroup_ = null;
-
-    /**
-     * A selection for the top-level <g> node that contains all dashed edges.
-     * @private {d3.selection}
-     */
-    this.dashedLinkGroup_ = null;
-
-    /** @private {!Map<bigint, !GraphNode>} */
-    this.nodes_ = new Map();
-
-    /**
-     * The links.
-     * @private {!Array<!d3.ForceLink>}
-     */
-    this.links_ = [];
-
-    /**
-     * The dashed links.
-     * @private {!Array<!d3.ForceLink>}
-     */
-    this.dashedLinks_ = [];
-
-    /**
-     * The host window.
-     * @private {Window}
-     */
-    this.hostWindow_ = null;
-
-    /**
-     * The interval timer used to poll for node descriptions.
-     * @private {number}
-     */
-    this.pollDescriptionsInterval_ = 0;
-
-    /**
-     * The d3.drag instance applied to nodes.
-     * @private {?d3.Drag}
-     */
-    this.drag_ = null;
   }
 
   initialize() {
@@ -687,10 +587,14 @@
     window.addEventListener('resize', this.onResize_.bind(this));
 
     // Create the simulation and set up the permanent forces.
-    const simulation = d3.forceSimulation();
+    const simulation =
+        d3.forceSimulation() as d3.Simulation<GraphNode, undefined>;
     simulation.on('tick', this.onTick_.bind(this));
 
-    const linkForce = d3.forceLink().id(d => d.id);
+    const linkForce =
+        (d3.forceLink() as
+         d3.ForceLink<GraphNode, d3.SimulationLinkDatum<GraphNode>>)
+            .id(d => d.id.toString());
     const defaultStrength = linkForce.strength();
 
     // Override the default link strength function to apply scaling factors
@@ -700,14 +604,16 @@
     simulation.force(
         'link',
         linkForce.strength(
-            l => defaultStrength(l) * l.source.linkStrengthScalingFactor *
-                l.target.linkStrengthScalingFactor));
+            (l, i, n) => defaultStrength(l, i, n) *
+                (l.source as GraphNode).linkStrengthScalingFactor *
+                (l.target as GraphNode).linkStrengthScalingFactor));
 
     // Sets the repulsion force between nodes (positive number is attraction,
     // negative number is repulsion).
     simulation.force(
         'charge',
-        d3.forceManyBody().strength(this.getManyBodyStrength_.bind(this)));
+        (d3.forceManyBody() as d3.ForceManyBody<GraphNode>)
+            .strength(this.getManyBodyStrength_.bind(this)));
 
     this.simulation_ = simulation;
 
@@ -715,12 +621,16 @@
     // The link groups are created first so that all links end up behind nodes.
     const svg = d3.select(this.svg_);
     this.toolTipLinkGroup_ = svg.append('g').attr('class', 'tool-tip-links');
-    this.linkGroup_ = svg.append('g').attr('class', 'links');
-    this.dashedLinkGroup_ = svg.append('g').attr('class', 'dashed-links');
+    this.linkGroup_ =
+        svg.append('g').attr('class', 'links') as d3.Selection<
+            SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>;
+    this.dashedLinkGroup_ =
+        svg.append('g').attr('class', 'dashed-links') as d3.Selection<
+            SVGGElement, d3.SimulationLinkDatum<GraphNode>, null, undefined>;
     this.nodeGroup_ = svg.append('g').attr('class', 'nodes');
     this.separatorGroup_ = svg.append('g').attr('class', 'separators');
 
-    const drag = d3.drag();
+    const drag = d3.drag() as d3.DragBehavior<any, GraphNode, unknown>;
     drag.clickDistance(4);
     drag.on('start', this.onDragStart_.bind(this));
     drag.on('drag', this.onDrag_.bind(this));
@@ -728,35 +638,29 @@
     this.drag_ = drag;
   }
 
-  /** @override */
-  frameCreated(frame) {
+  frameCreated(frame: FrameInfo) {
     this.addNode_(new FrameNode(frame));
   }
 
-  /** @override */
-  pageCreated(page) {
+  pageCreated(page: PageInfo) {
     this.addNode_(new PageNode(page));
   }
 
-  /** @override */
-  processCreated(process) {
+  processCreated(process: ProcessInfo) {
     this.addNode_(new ProcessNode(process));
   }
 
-  /** @override */
-  workerCreated(worker) {
+  workerCreated(worker: WorkerInfo) {
     this.addNode_(new WorkerNode(worker));
   }
 
-  /** @override */
-  frameChanged(frame) {
-    const frameNode = /** @type {!FrameNode} */ (this.nodes_.get(frame.id));
+  frameChanged(frame: FrameInfo) {
+    const frameNode = this.nodes_.get(frame.id) as FrameNode;
     frameNode.frame = frame;
   }
 
-  /** @override */
-  pageChanged(page) {
-    const pageNode = /** @type {!PageNode} */ (this.nodes_.get(page.id));
+  pageChanged(page: PageInfo) {
+    const pageNode = this.nodes_.get(page.id) as PageNode;
 
     // Page node dashed links may change dynamically, so account for that here.
     this.removeDashedNodeLinks_(pageNode);
@@ -764,17 +668,13 @@
     this.addDashedNodeLinks_(pageNode);
   }
 
-  /** @override */
-  processChanged(process) {
-    const processNode =
-        /** @type {!ProcessNode} */ (this.nodes_.get(process.id));
+  processChanged(process: ProcessInfo) {
+    const processNode = this.nodes_.get(process.id) as ProcessNode;
     processNode.process = process;
   }
 
-  /** @override */
-  workerChanged(worker) {
-    const workerNode =
-        /** @type {!WorkerNode} */ (this.nodes_.get(worker.id));
+  workerChanged(worker: WorkerInfo) {
+    const workerNode = this.nodes_.get(worker.id) as WorkerNode;
 
     // Worker node links may change dynamically, so account for that here.
     this.removeNodeLinks_(workerNode);
@@ -782,17 +682,15 @@
     this.addNodeLinks_(workerNode);
   }
 
-  /** @override */
-  favIconDataAvailable(iconInfo) {
+  favIconDataAvailable(iconInfo: FavIconInfo) {
     const graphNode = this.nodes_.get(iconInfo.nodeId);
     if (graphNode) {
       graphNode.iconUrl = 'data:image/png;base64,' + iconInfo.iconData;
     }
   }
 
-  /** @override */
-  nodeDeleted(nodeId) {
-    const node = this.nodes_.get(nodeId);
+  nodeDeleted(nodeId: bigint) {
+    const node = this.nodes_.get(nodeId)!;
 
     // Remove any links, and then the node itself.
     this.removeNodeLinks_(node);
@@ -815,52 +713,46 @@
       }
     }
 
-    function setLineEndpoints(d) {
-      const line = d3.select(this);
+    function setLineEndpoints(
+        d: ToolTip, line: d3.Selection<any, unknown, null, unknown>) {
       const center = d.getCenter();
-      line.attr('x1', d => center[0])
-          .attr('y1', d => center[1])
-          .attr('x2', d => d.node.x)
-          .attr('y2', d => d.node.y);
+      line.attr('x1', _d => center[0])
+          .attr('y1', _d => center[1])
+          .attr('x2', d => (d as {node: {x: number, y: number}}).node.x)
+          .attr('y2', d => (d as {node: {x: number, y: number}}).node.y);
     }
 
     const toolTipLinks =
-        this.toolTipLinkGroup_.selectAll('line').data(pinnedTooltips);
+        this.toolTipLinkGroup_!.selectAll('line').data(pinnedTooltips);
     toolTipLinks.enter()
         .append('line')
         .attr('stroke', 'LightGray')
         .attr('stroke-dasharray', '1')
         .attr('stroke-opacity', '0.8')
-        .each(setLineEndpoints);
-    toolTipLinks.each(setLineEndpoints);
+        .each(function(d: ToolTip) {
+          const line = d3.select(this);
+          setLineEndpoints(d, line);
+        });
+    toolTipLinks.each(function(d: ToolTip) {
+      const line = d3.select(this);
+      setLineEndpoints(d, line);
+    });
     toolTipLinks.exit().remove();
   }
 
-  /**
-   * @param {!GraphNode} node
-   * @private
-   */
-  removeNodeLinks_(node) {
+  private removeNodeLinks_(node: GraphNode) {
     // Filter away any links to or from the provided node.
     this.links_ = this.links_.filter(
         link => link.source !== node && link.target !== node);
   }
 
-  /**
-   * @param {!GraphNode} node
-   * @private
-   */
-  removeDashedNodeLinks_(node) {
+  private removeDashedNodeLinks_(node: GraphNode) {
     // Filter away any dashed links to or from the provided node.
     this.dashedLinks_ = this.dashedLinks_.filter(
         link => link.source !== node && link.target !== node);
   }
 
-  /**
-   * @param {!Object<string>} nodeDescriptions
-   * @private
-   */
-  nodeDescriptions_(nodeDescriptions) {
+  private nodeDescriptions_(nodeDescriptions: {[key: string]: any}) {
     for (const nodeId in nodeDescriptions) {
       const node = this.nodes_.get(BigInt(nodeId));
       if (node && node.tooltip) {
@@ -869,11 +761,8 @@
     }
   }
 
-  /**
-   * @private
-   */
-  pollForNodeDescriptions_() {
-    const nodeIds = [];
+  private pollForNodeDescriptions_() {
+    const nodeIds: bigint[] = [];
     for (const node of this.nodes_.values()) {
       if (node.tooltip) {
         nodeIds.push(node.id);
@@ -881,7 +770,7 @@
     }
 
     if (nodeIds.length) {
-      this.hostWindow_.postMessage(['requestNodeDescriptions', nodeIds], '*');
+      this.hostWindow_!.postMessage(['requestNodeDescriptions', nodeIds], '*');
 
       if (this.pollDescriptionsInterval_ === 0) {
         // Start polling if not already in progress.
@@ -896,69 +785,55 @@
   }
 
   /**
-   * @param {!Event} event A graph update event posted from the WebUI.
-   * @private
+   * @param event A graph update event posted from the WebUI.
    */
-  onMessage_(event) {
+  private onMessage_(event: MessageEvent) {
     if (!this.hostWindow_) {
-      this.hostWindow_ = event.source;
+      this.hostWindow_ = event.source as Window;
     }
 
-    const type = /** @type {string} */ (event.data[0]);
-    const data = /** @type {Object|number|bigint} */ (event.data[1]);
+    const type = event.data[0] as string;
+    const data = event.data[1];
     switch (type) {
       case 'frameCreated':
-        this.frameCreated(
-            /** @type {!FrameInfo} */ (data));
+        this.frameCreated(data as FrameInfo);
         break;
       case 'pageCreated':
-        this.pageCreated(
-            /** @type {!PageInfo} */ (data));
+        this.pageCreated(data as PageInfo);
         break;
       case 'processCreated':
-        this.processCreated(
-            /** @type {!ProcessInfo} */ (data));
+        this.processCreated(data as ProcessInfo);
         break;
       case 'workerCreated':
-        this.workerCreated(
-            /** @type {!WorkerInfo} */ (data));
+        this.workerCreated(data as WorkerInfo);
         break;
       case 'frameChanged':
-        this.frameChanged(
-            /** @type {!FrameInfo} */ (data));
+        this.frameChanged(data as FrameInfo);
         break;
       case 'pageChanged':
-        this.pageChanged(
-            /** @type {!PageInfo} */ (data));
+        this.pageChanged(data as PageInfo);
         break;
       case 'processChanged':
-        this.processChanged(
-            /** @type {!ProcessInfo} */ (data));
+        this.processChanged(data as ProcessInfo);
         break;
       case 'favIconDataAvailable':
-        this.favIconDataAvailable(
-            /** @type {!FavIconInfo} */ (data));
+        this.favIconDataAvailable(data as FavIconInfo);
         break;
       case 'workerChanged':
-        this.workerChanged(
-            /** @type {!WorkerInfo} */ (data));
+        this.workerChanged(data as WorkerInfo);
         break;
       case 'nodeDeleted':
-        this.nodeDeleted(/** @type {bigint} */ (data));
+        this.nodeDeleted(data as bigint);
         break;
       case 'nodeDescriptions':
-        this.nodeDescriptions_(/** @type {!Object<string>} */ (data));
+        this.nodeDescriptions_(data as {[key: string]: any});
         break;
     }
 
     this.render_();
   }
 
-  /**
-   * @param {GraphNode} node
-   * @private
-   */
-  onGraphNodeClick_(node) {
+  private onGraphNodeClick_(node: GraphNode) {
     if (node.tooltip) {
       node.tooltip.goAway();
       node.tooltip = null;
@@ -983,12 +858,10 @@
    *      available.
    * Deleted nodes are classed '.dead', and CSS takes care of hiding their
    * image element if it's been populated with an icon.
-   *
-   * @private
    */
-  render_() {
+  private render_() {
     // Select the links.
-    const link = this.linkGroup_.selectAll('line').data(this.links_);
+    const link = this.linkGroup_!.selectAll('line').data(this.links_);
     // Add new links.
     link.enter().append('line');
     // Remove dead links.
@@ -996,7 +869,7 @@
 
     // Select the dashed links.
     const dashedLink =
-        this.dashedLinkGroup_.selectAll('line').data(this.dashedLinks_);
+        this.dashedLinkGroup_!.selectAll('line').data(this.dashedLinks_);
     // Add new dashed links.
     dashedLink.enter().append('line');
     // Remove dead dashed links.
@@ -1004,14 +877,15 @@
 
     // Select the nodes, except for any dead ones that are still transitioning.
     const nodes = Array.from(this.nodes_.values());
-    const node =
-        this.nodeGroup_.selectAll('g:not(.dead)').data(nodes, d => d.id);
+    const node = (this.nodeGroup_!.selectAll('g:not(.dead)') as
+                  d3.Selection<any, GraphNode, SVGGElement, unknown>)
+                     .data(nodes, d => d.id as unknown as number);
 
     // Add new nodes, if any.
     if (!node.enter().empty()) {
       const newNodes = node.enter()
                            .append('g')
-                           .call(this.drag_)
+                           .call(this.drag_!)
                            .on('click', this.onGraphNodeClick_.bind(this));
       const circles = newNodes.append('circle')
                           .attr('id', d => `circle-${d.id}`)
@@ -1028,14 +902,15 @@
       // Transition new nodes to their chosen color in 2 seconds.
       circles.transition()
           .duration(2000)
-          .attr('fill', d => d.color)
+          .attr('fill', (d: unknown) => (d as {color: string}).color)
           .attr('r', kNodeRadius);
     }
 
     if (!node.exit().empty()) {
       // Give dead nodes a distinguishing class to exclude them from the
       // selection above.
-      const deletedNodes = node.exit().classed('dead', true);
+      const deletedNodes = node.exit().classed('dead', true) as
+          d3.Selection<any, GraphNode, SVGGElement, unknown>;
 
       // Interrupt any ongoing transitions.
       deletedNodes.interrupt();
@@ -1043,7 +918,7 @@
       // Turn down the node associated tooltips.
       deletedNodes.each(d => {
         if (d.tooltip) {
-          d.tooltip.goAway();
+          d.tooltip!.goAway();
         }
       });
 
@@ -1059,38 +934,46 @@
     }
 
     // Update the title for all nodes.
-    node.selectAll('title').text(d => d.title);
+    (node.selectAll('title') as d3.Selection<any, GraphNode, any, unknown>)
+        .text(d => d.title);
     // Update the favicon for all nodes.
-    node.selectAll('image').attr('href', d => d.iconUrl);
+    (node.selectAll('image') as d3.Selection<any, GraphNode, any, unknown>)
+        .attr('href', d => d.iconUrl);
 
     // Update and restart the simulation if the graph changed.
     if (!node.enter().empty() || !node.exit().empty() ||
         !link.enter().empty() || !link.exit().empty() ||
         !dashedLink.enter().empty() || !dashedLink.exit().empty()) {
-      this.simulation_.nodes(nodes);
+      this.simulation_!.nodes(nodes);
       const links = this.links_.concat(this.dashedLinks_);
-      this.simulation_.force('link').links(links);
+      (this.simulation_!.force('link')! as d3.ForceLink<GraphNode, any>)
+          .links(links);
 
       this.restartSimulation_();
     }
   }
 
-  /** @private */
-  onTick_() {
-    const nodes = this.nodeGroup_.selectAll('g');
+  private onTick_() {
+    const nodes: d3.Selection<SVGGElement, GraphNode, SVGGElement, unknown> =
+        this.nodeGroup_!.selectAll('g');
     nodes.attr('transform', d => `translate(${d.x},${d.y})`);
 
-    const lines = this.linkGroup_.selectAll('line');
-    lines.attr('x1', d => d.source.x)
-        .attr('y1', d => d.source.y)
-        .attr('x2', d => d.target.x)
-        .attr('y2', d => d.target.y);
+    const lines: d3.Selection<
+        SVGLineElement, d3.SimulationLinkDatum<GraphNode>, SVGGElement,
+        d3.SimulationLinkDatum<GraphNode>> = this.linkGroup_!.selectAll('line');
+    lines.attr('x1', d => (d.source as GraphNode).x)
+        .attr('y1', d => (d.source as GraphNode).y)
+        .attr('x2', d => (d.target as GraphNode).x)
+        .attr('y2', d => (d.target as GraphNode).y);
 
-    const dashedLines = this.dashedLinkGroup_.selectAll('line');
-    dashedLines.attr('x1', d => d.source.x)
-        .attr('y1', d => d.source.y)
-        .attr('x2', d => d.target.x)
-        .attr('y2', d => d.target.y);
+    const dashedLines: d3.Selection<
+        SVGLineElement, d3.SimulationLinkDatum<GraphNode>, SVGGElement,
+        d3.SimulationLinkDatum<GraphNode>> =
+        this.dashedLinkGroup_!.selectAll('line');
+    dashedLines.attr('x1', d => (d.source as GraphNode).x)
+        .attr('y1', d => (d.source as GraphNode).y)
+        .attr('x2', d => (d.target as GraphNode).x)
+        .attr('y2', d => (d.target as GraphNode).y);
 
     this.updateToolTipLinks();
   }
@@ -1098,11 +981,8 @@
   /**
    * Adds a new node to the graph, populates its links and gives it an initial
    * position.
-   *
-   * @param {!GraphNode} node
-   * @private
    */
-  addNode_(node) {
+  private addNode_(node: GraphNode) {
     this.nodes_.set(node.id, node);
     this.addNodeLinks_(node);
     this.addDashedNodeLinks_(node);
@@ -1111,11 +991,8 @@
 
   /**
    * Adds all the links for a node to the graph.
-   *
-   * @param {!GraphNode} node
-   * @private
    */
-  addNodeLinks_(node) {
+  private addNodeLinks_(node: GraphNode) {
     for (const linkTarget of node.linkTargets) {
       const target = this.nodes_.get(linkTarget);
       if (target) {
@@ -1126,11 +1003,8 @@
 
   /**
    * Adds all the dashed links for a node to the graph.
-   *
-   * @param {!GraphNode} node
-   * @private
    */
-  addDashedNodeLinks_(node) {
+  private addDashedNodeLinks_(node: GraphNode) {
     for (const dashedLinkTarget of node.dashedLinkTargets) {
       const target = this.nodes_.get(dashedLinkTarget);
       if (target) {
@@ -1140,10 +1014,9 @@
   }
 
   /**
-   * @param {!GraphNode} d The dragged node.
-   * @private
+   * @param d The dragged node.
    */
-  onDragStart_(d) {
+  private onDragStart_(d: GraphNode) {
     if (!d3.event.active) {
       this.restartSimulation_();
     }
@@ -1152,21 +1025,19 @@
   }
 
   /**
-   * @param {!GraphNode} d The dragged node.
-   * @private
+   * @param d The dragged node.
    */
-  onDrag_(d) {
+  private onDrag_(d: GraphNode) {
     d.fx = d3.event.x;
     d.fy = d3.event.y;
   }
 
   /**
-   * @param {!GraphNode} d The dragged node.
-   * @private
+   * @param d The dragged node.
    */
-  onDragEnd_(d) {
+  private onDragEnd_(d: GraphNode) {
     if (!d3.event.active) {
-      this.simulation_.alphaTarget(0);
+      this.simulation_!.alphaTarget(0);
     }
     // Leave the node pinned where it was dropped. Return it to free
     // positioning if it's dropped outside its designated area.
@@ -1180,36 +1051,23 @@
     d3.select(`#circle-${d.id}`).classed('pinned', d.fx != null);
   }
 
-  /**
-   * @param {!d3.ForceNode} d The node to position.
-   * @private
-   */
-  getTargetYPosition_(d) {
+  private getTargetYPosition_(d: GraphNode): number {
     return d.targetYPosition(this.height_);
   }
 
-  /**
-   * @param {!d3.ForceNode} d The node to position.
-   * @private
-   */
-  getTargetYPositionStrength_(d) {
+  private getTargetYPositionStrength_(d: GraphNode): number {
     return d.targetYPositionStrength;
   }
 
-  /**
-   * @param {!d3.ForceNode} d The node to position.
-   * @private
-   */
-  getManyBodyStrength_(d) {
+  private getManyBodyStrength_(d: GraphNode): number {
     return d.manyBodyStrength;
   }
 
   /**
-   * @param {number} graphWidth Width of the graph view (svg).
-   * @param {number} graphHeight Height of the graph view (svg).
-   * @private
+   * @param graphWidth Width of the graph view (svg).
+   * @param graphHeight Height of the graph view (svg).
    */
-  updateSeparators_(graphWidth, graphHeight) {
+  private updateSeparators_(graphWidth: number, graphHeight: number) {
     const separators = [
       ['Pages', 'Frame Tree', kPageNodesYRange],
       ['', 'Workers', graphHeight - kWorkerNodesYRange],
@@ -1218,10 +1076,10 @@
     const kAboveLabelOffset = -6;
     const kBelowLabelOffset = 14;
 
-    const groups = this.separatorGroup_.selectAll('g').data(separators);
+    const groups = this.separatorGroup_!.selectAll('g').data(separators);
     if (groups.enter()) {
       const group = groups.enter().append('g').attr(
-          'transform', d => `translate(0,${d[2]})`);
+          'transform', (d: Array<number|string>) => `translate(0,${d[2]})`);
       group.append('line')
           .attr('x1', 10)
           .attr('y1', 0)
@@ -1230,40 +1088,41 @@
           .attr('stroke', 'black')
           .attr('stroke-dasharray', '4');
 
-      group.each(function(d) {
+      group.each(function(d: unknown) {
         const parentGroup = d3.select(this);
-        if (d[0]) {
+        if ((d as Array<string|number>)[0]) {
           parentGroup.append('text')
               .attr('x', 20)
               .attr('y', kAboveLabelOffset)
               .attr('class', 'separator')
-              .text(d => d[0]);
+              .text(d => (d as Array<string|number>)[0]);
         }
-        if (d[1]) {
+        if ((d as Array<string|number>)[1]) {
           parentGroup.append('text')
               .attr('x', 20)
               .attr('y', kBelowLabelOffset)
               .attr('class', 'separator')
-              .text(d => d[1]);
+              .text(d => (d as Array<string|number>)[1]);
         }
       });
     }
 
-    groups.attr('transform', d => `translate(0,${d[2]})`);
+    groups.attr('transform', (d: unknown) => {
+      const value = (d as Array<string|number>)[2];
+      return `translate(0,${value})`;
+    });
     groups.selectAll('line').attr('x2', graphWidth - 10);
   }
 
-  /** @private */
-  restartSimulation_() {
+  private restartSimulation_() {
     // Restart the simulation.
-    this.simulation_.alphaTarget(0.3).restart();
+    this.simulation_!.alphaTarget(0.3).restart();
   }
 
   /**
    * Resizes and restarts the animation after a size change.
-   * @private
    */
-  onResize_() {
+  private onResize_() {
     this.width_ = this.svg_.clientWidth;
     this.height_ = this.svg_.clientHeight;
 
@@ -1271,12 +1130,13 @@
 
     // Reset both X and Y attractive forces, as they're cached.
     const xForce = d3.forceX().x(this.width_ / 2).strength(0.1);
-    const yForce = d3.forceY()
+    const yForce = (d3.forceY() as d3.ForceY<GraphNode>)
                        .y(this.getTargetYPosition_.bind(this))
                        .strength(this.getTargetYPositionStrength_.bind(this));
-    this.simulation_.force('x_pos', xForce);
-    this.simulation_.force('y_pos', yForce);
-    this.simulation_.force('y_bound', boundingForce(this.height_, this.width_));
+    this.simulation_!.force('x_pos', xForce);
+    this.simulation_!.force('y_pos', yForce);
+    this.simulation_!.force(
+        'y_bound', boundingForce(this.height_, this.width_));
 
     if (!this.wasResized_) {
       this.wasResized_ = true;
@@ -1287,20 +1147,19 @@
 
       // Allow the simulation to settle by running it for a bit.
       for (let i = 0; i < 200; ++i) {
-        this.simulation_.tick();
+        this.simulation_!.tick();
       }
     }
 
     this.restartSimulation_();
   }
 }
-/* @type {?Graph} */
-let graph = null;
+let graph: Graph|null = null;
 function onLoad() {
   graph =
-      new Graph(document.querySelector('svg'), document.querySelector('div'));
+      new Graph(document.querySelector('svg')!, document.querySelector('div')!);
 
-  graph.initialize();
+  graph!.initialize();
 }
 
 window.addEventListener('load', onLoad);
diff --git a/chrome/browser/resources/discards/graph_tab_template.html b/chrome/browser/resources/discards/graph_tab_template.html
index 7d782455..f6e2f39 100644
--- a/chrome/browser/resources/discards/graph_tab_template.html
+++ b/chrome/browser/resources/discards/graph_tab_template.html
@@ -1,4 +1,4 @@
 
-    <webview id="webView" src="${data_url}" on-contentload="onWebViewReady_"
+    <webview id="webView" src="{__data_url__}" on-contentload="onWebViewReady_"
         allowscaling>
     </webview>
diff --git a/chrome/browser/resources/discards/tsconfig_base.json b/chrome/browser/resources/discards/tsconfig_base.json
index 3e71f763..9199ab6f 100644
--- a/chrome/browser/resources/discards/tsconfig_base.json
+++ b/chrome/browser/resources/discards/tsconfig_base.json
@@ -2,8 +2,11 @@
   "extends": "../../../../tools/typescript/tsconfig_base.json",
   "compilerOptions": {
     "allowJs": true,
+    "allowUmdGlobalAccess": true,
     "noUncheckedIndexedAccess": false,
     "noUnusedLocals": false,
-    "strictPropertyInitialization": false
+    "strictPropertyInitialization": false,
+    "typeRoots": [ "../../../../third_party/node/node_modules/@types" ],
+    "types": [ "d3" ]
   }
 }
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 16feaa9..682e04f1 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -208,23 +208,8 @@
 
   manifest_excludes =
       [ "pdf_internal_plugin_wrapper.js" ] + print_preview_only_files
-  definitions = [
-    "//tools/typescript/definitions/chrome_event.d.ts",
-    "//tools/typescript/definitions/content_settings.d.ts",
-    "//tools/typescript/definitions/file_system.d.ts",
-    "//tools/typescript/definitions/metrics_private.d.ts",
-    "//tools/typescript/definitions/mime_handler_private.d.ts",
-    "//tools/typescript/definitions/pending.d.ts",
-    "//tools/typescript/definitions/resources_private.d.ts",
-    "//tools/typescript/definitions/runtime.d.ts",
-    "//tools/typescript/definitions/tabs.d.ts",
-    "//tools/typescript/definitions/windows.d.ts",
-    "source_capabilities.d.ts",
-  ]
 
-  if (enable_ink) {
-    definitions += [ "ink/drawing_canvas.d.ts" ]
-  }
+  definitions = ts_definitions
 
   deps = [
     "//third_party/polymer/v3_0:library",
diff --git a/chrome/browser/resources/pdf/manifest.json b/chrome/browser/resources/pdf/manifest.json
index dbe8dd5..dbc6ccd 100644
--- a/chrome/browser/resources/pdf/manifest.json
+++ b/chrome/browser/resources/pdf/manifest.json
@@ -9,6 +9,7 @@
   "incognito": "split",
   "permissions": [
     "chrome://resources/",
+    "chrome://webui-test/",
     "contentSettings",
     "metricsPrivate",
     "resourcesPrivate",
@@ -18,6 +19,6 @@
   "mime_types": [
     "application/pdf"
   ],
-  "content_security_policy": "script-src 'self' 'wasm-eval' blob: filesystem: chrome://resources; object-src * blob: externalfile: file: filesystem: data:",
+  "content_security_policy": "script-src 'self' 'wasm-eval' blob: filesystem: chrome://resources chrome://webui-test; object-src * blob: externalfile: file: filesystem: data:",
   "mime_types_handler": "index.html"
 }
diff --git a/chrome/browser/resources/pdf/pdf.gni b/chrome/browser/resources/pdf/pdf.gni
index 22f1298d..f8ca4e0 100644
--- a/chrome/browser/resources/pdf/pdf.gni
+++ b/chrome/browser/resources/pdf/pdf.gni
@@ -154,6 +154,27 @@
 print_preview_only_files =
     print_preview_ts_files + print_preview_html_wrapper_files
 
+ts_definitions = [
+  # Using an absolute path because this variable is also consumed by
+  # chrome/test/data/pdf:build_ts.
+  "//chrome/browser/resources/pdf/source_capabilities.d.ts",
+
+  "//tools/typescript/definitions/chrome_event.d.ts",
+  "//tools/typescript/definitions/content_settings.d.ts",
+  "//tools/typescript/definitions/file_system.d.ts",
+  "//tools/typescript/definitions/metrics_private.d.ts",
+  "//tools/typescript/definitions/mime_handler_private.d.ts",
+  "//tools/typescript/definitions/pending.d.ts",
+  "//tools/typescript/definitions/resources_private.d.ts",
+  "//tools/typescript/definitions/runtime.d.ts",
+  "//tools/typescript/definitions/tabs.d.ts",
+  "//tools/typescript/definitions/windows.d.ts",
+]
+
+if (enable_ink) {
+  ts_definitions += [ "//chrome/browser/resources/pdf/ink/drawing_canvas.d.ts" ]
+}
+
 # Print Preview's .grdp file needs all the shared + Print Preview specific files.
 print_preview_grdp_ts_files =
     shared_ts_files + shared_html_wrapper_files + shared_css_wrapper_files +
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS b/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc b/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc
index 133d509..22f17a0 100644
--- a/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc
+++ b/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc
@@ -28,7 +28,6 @@
 #include "ui/gfx/image/canvas_image_source.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
-#include "ui/views/image_model_utils.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/search/contextual_search_policy_handler_android.cc b/chrome/browser/search/contextual_search_policy_handler_android.cc
index 25c3ec1..fa6e945 100644
--- a/chrome/browser/search/contextual_search_policy_handler_android.cc
+++ b/chrome/browser/search/contextual_search_policy_handler_android.cc
@@ -22,13 +22,14 @@
 void ContextualSearchPolicyHandlerAndroid::ApplyPolicySettings(
     const PolicyMap& policies,
     PrefValueMap* prefs) {
-  const base::Value* value = policies.GetValue(policy_name());
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::BOOLEAN);
   // From a Contextual Search preference point of view, "false" means the
   // feature is turned off completely. "" means the feature is uninitialized and
   // an opt-in screen is presented to the user, after which the preference is
   // either "true" or "false", depending on their choice. Here a false policy
   // explicitly disables Contextual Search.
-  if (value && value->is_bool() && !value->GetBool()) {
+  if (value && !value->GetBool()) {
     prefs->SetString(prefs::kContextualSearchEnabled,
                      prefs::kContextualSearchDisabledValue);
   }
diff --git a/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc b/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc
index 3e8d184..c32d2fa 100644
--- a/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc
+++ b/chrome/browser/search/ntp_custom_background_enabled_policy_handler.cc
@@ -21,8 +21,9 @@
 void NtpCustomBackgroundEnabledPolicyHandler::ApplyPolicySettings(
     const policy::PolicyMap& policies,
     PrefValueMap* prefs) {
-  const base::Value* value = policies.GetValue(policy_name());
-  if (value && value->is_bool() && !value->GetBool()) {
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::BOOLEAN);
+  if (value && !value->GetBool()) {
     prefs->SetValue(prefs::kNtpCustomBackgroundDict,
                     base::Value(base::Value::Type::DICTIONARY));
   }
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
new file mode 100644
index 0000000..da757858
--- /dev/null
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
@@ -0,0 +1,39 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h"
+
+#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
+
+namespace segmentation_platform {
+
+ModelProviderFactoryImpl::ModelProviderFactoryImpl(
+    optimization_guide::OptimizationGuideModelProvider*
+        optimization_guide_provider,
+    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
+    : optimization_guide_provider_(optimization_guide_provider),
+      background_task_runner_(background_task_runner) {}
+
+ModelProviderFactoryImpl::~ModelProviderFactoryImpl() = default;
+
+std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateProvider(
+    optimization_guide::proto::OptimizationTarget optimization_target) {
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+  return std::make_unique<OptimizationGuideSegmentationModelProvider>(
+      optimization_guide_provider_, background_task_runner_,
+      optimization_target);
+#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+
+  NOTREACHED();
+  return nullptr;
+}
+
+std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateDefaultProvider(
+    optimization_guide::proto::OptimizationTarget optimization_target) {
+  return nullptr;
+}
+
+}  // namespace segmentation_platform
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.h b/chrome/browser/segmentation_platform/model_provider_factory_impl.h
new file mode 100644
index 0000000..b108b26b
--- /dev/null
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl.h
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
+#define CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/task/sequenced_task_runner.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
+
+namespace optimization_guide {
+class OptimizationGuideModelProvider;
+}
+
+namespace segmentation_platform {
+
+class ModelProviderFactoryImpl : public ModelProviderFactory {
+ public:
+  ModelProviderFactoryImpl(
+      optimization_guide::OptimizationGuideModelProvider*
+          optimization_guide_provider,
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner);
+
+  ~ModelProviderFactoryImpl() override;
+
+  ModelProviderFactoryImpl(ModelProviderFactoryImpl&) = delete;
+  ModelProviderFactoryImpl& operator=(ModelProviderFactoryImpl&) = delete;
+
+  // ModelProviderFactory impl:
+  std::unique_ptr<ModelProvider> CreateProvider(
+      optimization_guide::proto::OptimizationTarget optimization_target)
+      override;
+  std::unique_ptr<ModelProvider> CreateDefaultProvider(
+      optimization_guide::proto::OptimizationTarget optimization_target)
+      override;
+
+ private:
+  raw_ptr<optimization_guide::OptimizationGuideModelProvider>
+      optimization_guide_provider_;
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // CHROME_BROWSER_SEGMENTATION_PLATFORM_MODEL_PROVIDER_FACTORY_IMPL_H_
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
new file mode 100644
index 0000000..3df4a81
--- /dev/null
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h"
+
+#include "base/test/task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
+#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace segmentation_platform {
+
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+class ModelProviderFactoryImplTest : public testing::Test {
+ public:
+  ModelProviderFactoryImplTest() = default;
+  ~ModelProviderFactoryImplTest() override = default;
+
+  void SetUp() override {
+    task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+    model_provider_ = std::make_unique<
+        optimization_guide::TestOptimizationGuideModelProvider>();
+    provider_factory_ = std::make_unique<ModelProviderFactoryImpl>(
+        model_provider_.get(), task_runner_);
+  }
+
+  void TearDown() override {
+    task_runner_->RunPendingTasks();
+    provider_factory_.reset();
+    model_provider_.reset();
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider>
+      model_provider_;
+
+  std::unique_ptr<ModelProviderFactoryImpl> provider_factory_;
+};
+
+TEST_F(ModelProviderFactoryImplTest, ProviderCreated) {
+  EXPECT_TRUE(provider_factory_->CreateProvider(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+  EXPECT_TRUE(provider_factory_->CreateProvider(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+}
+#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+
+}  // namespace segmentation_platform
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
index 07f91141..7b04c284 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/segmentation_platform/model_provider_factory_impl.h"
 #include "chrome/browser/segmentation_platform/segmentation_platform_config.h"
 #include "chrome/browser/segmentation_platform/segmentation_platform_profile_observer.h"
 #include "chrome/browser/segmentation_platform/ukm_database_client.h"
@@ -23,6 +24,7 @@
 #include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/features.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 
@@ -81,8 +83,11 @@
       profile->GetDefaultStoragePartition()->GetProtoDatabaseProvider();
   base::DefaultClock* clock = base::DefaultClock::GetInstance();
 
+  auto model_provider_factory = std::make_unique<ModelProviderFactoryImpl>(
+      optimization_guide, task_runner);
+
   auto* service = new SegmentationPlatformServiceImpl(
-      optimization_guide, db_provider, storage_dir,
+      std::move(model_provider_factory), db_provider, storage_dir,
       UkmDatabaseClient::GetInstance().GetUkmDataManager(), profile->GetPrefs(),
       history_service, task_runner, clock, GetSegmentationPlatformConfig());
 
diff --git a/chrome/browser/sessions/restore_on_startup_policy_handler.cc b/chrome/browser/sessions/restore_on_startup_policy_handler.cc
index 01b12cc..f473cc2 100644
--- a/chrome/browser/sessions/restore_on_startup_policy_handler.cc
+++ b/chrome/browser/sessions/restore_on_startup_policy_handler.cc
@@ -27,10 +27,8 @@
     const PolicyMap& policies,
     PrefValueMap* prefs) {
   const base::Value* restore_on_startup_value =
-      policies.GetValue(policy_name());
+      policies.GetValue(policy_name(), base::Value::Type::INTEGER);
   if (restore_on_startup_value) {
-    if (!restore_on_startup_value->is_int())
-      return;
     prefs->SetInteger(prefs::kRestoreOnStartup,
                       restore_on_startup_value->GetInt());
   }
@@ -42,10 +40,10 @@
   if (!TypeCheckingPolicyHandler::CheckPolicySettings(policies, errors))
     return false;
 
-  const base::Value* restore_policy = policies.GetValue(key::kRestoreOnStartup);
+  const base::Value* restore_policy =
+      policies.GetValue(key::kRestoreOnStartup, base::Value::Type::INTEGER);
 
   if (restore_policy) {
-    CHECK(restore_policy->is_int());  // Passed type check.
     switch (restore_policy->GetInt()) {
       case 0:  // Deprecated kPrefValueHomePage.
         errors->AddError(policy_name(), IDS_POLICY_VALUE_DEPRECATED);
@@ -55,10 +53,9 @@
         // If the "restore last session" policy is set, session cookies are
         // treated as permanent cookies and site data needed to restore the
         // session is not cleared so we have to warn the user in that case.
-        const base::Value* cookies_policy =
-            policies.GetValue(key::kCookiesSessionOnlyForUrls);
-        if (cookies_policy && cookies_policy->is_list() &&
-            !cookies_policy->GetListDeprecated().empty()) {
+        const base::Value* cookies_policy = policies.GetValue(
+            key::kCookiesSessionOnlyForUrls, base::Value::Type::LIST);
+        if (cookies_policy && !cookies_policy->GetListDeprecated().empty()) {
           errors->AddError(key::kCookiesSessionOnlyForUrls,
                            IDS_POLICY_OVERRIDDEN,
                            key::kRestoreOnStartup);
diff --git a/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc b/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc
index ad02483..1c104c7 100644
--- a/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc
+++ b/chrome/browser/spellchecker/spellcheck_language_blocklist_policy_handler.cc
@@ -59,13 +59,14 @@
     const policy::PolicyMap& policies,
     PrefValueMap* prefs) {
   // Ignore this policy if the SpellcheckEnabled policy disables spellcheck.
-  const base::Value* spellcheck_enabled_value =
-      policies.GetValue(policy::key::kSpellcheckEnabled);
-  if (spellcheck_enabled_value && spellcheck_enabled_value->GetBool() == false)
+  const base::Value* spellcheck_enabled_value = policies.GetValue(
+      policy::key::kSpellcheckEnabled, base::Value::Type::BOOLEAN);
+  if (spellcheck_enabled_value && !spellcheck_enabled_value->GetBool())
     return;
 
   // If this policy isn't set, don't modify spellcheck languages.
-  const base::Value* value = policies.GetValue(policy_name());
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::LIST);
   if (!value)
     return;
 
@@ -98,13 +99,14 @@
     std::vector<base::Value>* const blocklisted,
     std::vector<std::string>* const unknown,
     std::vector<std::string>* const duplicates) {
-  const base::Value* value = policies.GetValue(policy_name());
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::LIST);
   if (!value)
     return;
 
   // Build a lookup of force-enabled spellcheck languages to find duplicates.
-  const base::Value* forced_enabled_value =
-      policies.GetValue(policy::key::kSpellcheckLanguage);
+  const base::Value* forced_enabled_value = policies.GetValue(
+      policy::key::kSpellcheckLanguage, base::Value::Type::LIST);
   std::unordered_set<std::string> forced_languages_lookup;
   if (forced_enabled_value) {
     for (const auto& forced_language :
diff --git a/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc b/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
index 316f636..814a507 100644
--- a/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
+++ b/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
@@ -51,13 +51,14 @@
     const policy::PolicyMap& policies,
     PrefValueMap* prefs) {
   // Ignore this policy if the SpellcheckEnabled policy disables spellcheck.
-  const base::Value* spellcheck_enabled_value =
-      policies.GetValue(policy::key::kSpellcheckEnabled);
-  if (spellcheck_enabled_value && spellcheck_enabled_value->GetBool() == false)
+  const base::Value* spellcheck_enabled_value = policies.GetValue(
+      policy::key::kSpellcheckEnabled, base::Value::Type::BOOLEAN);
+  if (spellcheck_enabled_value && !spellcheck_enabled_value->GetBool())
     return;
 
   // If this policy isn't set, don't modify spellcheck languages.
-  const base::Value* value = policies.GetValue(policy_name());
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::LIST);
   if (!value)
     return;
 
@@ -82,7 +83,8 @@
     const policy::PolicyMap& policies,
     std::vector<base::Value>* const forced,
     std::vector<std::string>* const unknown) {
-  const base::Value* value = policies.GetValue(policy_name());
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::LIST);
   if (!value)
     return;
 
diff --git a/chrome/browser/ssl/https_only_mode_browsertest.cc b/chrome/browser/ssl/https_only_mode_browsertest.cc
index cf332dc..45209d7 100644
--- a/chrome/browser/ssl/https_only_mode_browsertest.cc
+++ b/chrome/browser/ssl/https_only_mode_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
 #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/https_only_mode_navigation_throttle.h"
@@ -821,6 +822,96 @@
       security_interstitials::MetricsHelper::Decision::DONT_PROCEED, 1);
 }
 
+// Tests that if a user allowlists a host and then does not visit it again for
+// seven days (the expiration period), then the interstitial will be shown again
+// the next time they visit the host.
+IN_PROC_BROWSER_TEST_F(HttpsOnlyModeBrowserTest, AllowlistEntryExpires) {
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
+  content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
+
+  // Set a testing clock on the StatefulSSLHostStateDelegate, keeping a pointer
+  // to the clock object around so the test can manipulate time. `chrome_state`
+  // takes ownership of `clock`.
+  auto clock = std::make_unique<base::SimpleTestClock>();
+  auto* clock_ptr = clock.get();
+  StatefulSSLHostStateDelegate* chrome_state =
+      static_cast<StatefulSSLHostStateDelegate*>(state);
+  chrome_state->SetClockForTesting(std::move(clock));
+
+  // Start the clock at standard system time.
+  clock_ptr->SetNow(base::Time::NowFromSystemTime());
+
+  // Visit a host that doesn't support HTTPS for the first time, and click
+  // through the HTTPS-First Mode interstitial to allowlist the host.
+  GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html");
+  EXPECT_FALSE(content::NavigateToURL(contents, http_url));
+  EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(
+      contents));
+  ProceedThroughInterstitial(contents);
+  EXPECT_EQ(http_url, contents->GetLastCommittedURL());
+  EXPECT_TRUE(state->IsHttpAllowedForHost(http_url.host(), contents));
+
+  // Simulate the clock advancing by eight days, which is past the expiration
+  // point.
+  clock_ptr->Advance(base::Days(8));
+
+  // The host should no longer be allowlisted, and the interstitial should
+  // trigger again.
+  EXPECT_FALSE(state->IsHttpAllowedForHost(http_url.host(), contents));
+  EXPECT_FALSE(content::NavigateToURL(contents, http_url));
+  EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(
+      contents));
+}
+
+// Tests that re-visiting an allowlisted host bumps the expiration time to a new
+// seven days in the future from now.
+IN_PROC_BROWSER_TEST_F(HttpsOnlyModeBrowserTest, RevisitingBumpsExpiration) {
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
+  content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
+
+  // Set a testing clock on the StatefulSSLHostStateDelegate, keeping a pointer
+  // to the clock object around so the test can manipulate time. `chrome_state`
+  // takes ownership of `clock`.
+  auto clock = std::make_unique<base::SimpleTestClock>();
+  auto* clock_ptr = clock.get();
+  StatefulSSLHostStateDelegate* chrome_state =
+      static_cast<StatefulSSLHostStateDelegate*>(state);
+  chrome_state->SetClockForTesting(std::move(clock));
+
+  // Start the clock at standard system time.
+  clock_ptr->SetNow(base::Time::NowFromSystemTime());
+
+  // Visit a host that doesn't support HTTPS for the first time, and click
+  // through the HTTPS-First Mode interstitial to allowlist the host.
+  GURL http_url = http_server()->GetURL("bad-https.test", "/simple.html");
+  EXPECT_FALSE(content::NavigateToURL(contents, http_url));
+  EXPECT_TRUE(chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(
+      contents));
+  ProceedThroughInterstitial(contents);
+  EXPECT_EQ(http_url, contents->GetLastCommittedURL());
+  EXPECT_TRUE(state->IsHttpAllowedForHost(http_url.host(), contents));
+
+  // Simulate the clock advancing by five days.
+  clock_ptr->Advance(base::Days(5));
+
+  // Navigate to the host again; this will reset the allowlist expiration to
+  // now + 7 days.
+  EXPECT_TRUE(content::NavigateToURL(contents, http_url));
+
+  // Simulate the clock advancing another five days. This will be _after_ the
+  // initial expiration date of the allowlist entry, but _before_ the bumped
+  // expiration date from the second navigation.
+  clock_ptr->Advance(base::Days(5));
+  EXPECT_TRUE(content::NavigateToURL(contents, http_url));
+  EXPECT_FALSE(
+      chrome_browser_interstitials::IsShowingHttpsFirstModeInterstitial(
+          contents));
+}
+
 // A simple test fixture that ensures the kHttpsOnlyMode feature is enabled and
 // constructs a HistogramTester (so that it gets initialized before browser
 // startup). Used for testing pref tracking logic.
diff --git a/chrome/browser/ssl/https_only_mode_policy_handler.cc b/chrome/browser/ssl/https_only_mode_policy_handler.cc
index eee04af..e209596b 100644
--- a/chrome/browser/ssl/https_only_mode_policy_handler.cc
+++ b/chrome/browser/ssl/https_only_mode_policy_handler.cc
@@ -19,7 +19,8 @@
 
 void HttpsOnlyModePolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
                                                      PrefValueMap* prefs) {
-  const base::Value* value = policies.GetValue(key::kHttpsOnlyMode);
+  const base::Value* value =
+      policies.GetValue(key::kHttpsOnlyMode, base::Value::Type::STRING);
   if (value && value->GetString() == "disallowed") {
     // Only apply the policy to the pref if it is set to "disallowed".
     prefs->SetBoolean(prefs::kHttpsOnlyModeEnabled, false);
diff --git a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
index b7ed0d2..0a35279 100644
--- a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
+++ b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
@@ -118,6 +118,13 @@
     // StatefulSSLHostStateDelegate can be null during tests.
     if (state && state->IsHttpAllowedForHost(
                      tentative_resource_request.url.host(), web_contents)) {
+      // Renew the allowlist expiration for this host as the user is still
+      // actively using it. This means that the allowlist entry will stay valid
+      // until the user stops visiting this host for the entire expiration
+      // period (one week).
+      state->AllowHttpForHost(tentative_resource_request.url.host(),
+                              web_contents);
+
       std::move(callback).Run({});
       return;
     }
diff --git a/chrome/browser/ssl/secure_origin_policy_handler.cc b/chrome/browser/ssl/secure_origin_policy_handler.cc
index d0d2206..2ae4f4f8 100644
--- a/chrome/browser/ssl/secure_origin_policy_handler.cc
+++ b/chrome/browser/ssl/secure_origin_policy_handler.cc
@@ -28,7 +28,8 @@
 
 void SecureOriginPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
                                                     PrefValueMap* prefs) {
-  const base::Value* value = policies.GetValue(policy_name());
+  const base::Value* value =
+      policies.GetValue(policy_name(), base::Value::Type::LIST);
   if (!value)
     return;
 
diff --git a/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc b/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc
index dbfe822..ddd20c7 100644
--- a/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_settings_service_unittest.cc
@@ -111,9 +111,8 @@
       EXPECT_TRUE(expected_value);
     }
 
-    std::unique_ptr<base::Value> value =
-        base::JSONReader::ReadDeprecated(supervised_user_setting.value());
-    EXPECT_TRUE(expected_value->Equals(value.get()));
+    EXPECT_EQ(*expected_value,
+              base::JSONReader::Read(supervised_user_setting.value()));
   }
 
   void OnNewSettingsAvailable(const base::DictionaryValue* settings) {
@@ -207,7 +206,7 @@
   ASSERT_TRUE(value);
   const base::DictionaryValue* dict_value = nullptr;
   ASSERT_TRUE(value->GetAsDictionary(&dict_value));
-  EXPECT_TRUE(dict_value->Equals(&dict));
+  EXPECT_EQ(*dict_value, dict);
 }
 
 TEST_F(SupervisedUserSettingsServiceTest, NotifyForWebsiteApprovals) {
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
index 92dbf5f..d67a758 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
@@ -16,7 +16,6 @@
 import org.chromium.base.Log;
 import org.chromium.base.ObserverList;
 import org.chromium.base.TraceEvent;
-import org.chromium.base.annotations.DoNotClassMerge;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.tab.Tab;
@@ -38,11 +37,7 @@
 
 /**
  * Data which is core to the app and must be retrieved as quickly as possible on startup.
- *
- * This class should not be merged because it is being used as a key in a Map
- * in PersistedTabDataConfiguration.java.
  */
-@DoNotClassMerge
 public class CriticalPersistedTabData extends PersistedTabData {
     private static final String TAG = "CriticalPTD";
     private static final Class<CriticalPersistedTabData> USER_DATA_KEY =
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java
index 1f90b780..332f880 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/MockPersistedTabData.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.tab.state;
 
 import org.chromium.base.Callback;
-import org.chromium.base.annotations.DoNotClassMerge;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.tab.Tab;
 
@@ -13,11 +12,7 @@
 
 /**
  * MockPersistedTabData object used for testing
- *
- * This class should not be merged because it is being used as a key in a Map
- * in PersistedTabDataConfiguration.java.
  */
-@DoNotClassMerge
 public class MockPersistedTabData extends PersistedTabData {
     private int mField;
 
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
index 2b28f0d..d342812 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
@@ -14,7 +14,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
 import org.chromium.base.Log;
-import org.chromium.base.annotations.DoNotClassMerge;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
@@ -56,11 +55,7 @@
 
 /**
  * {@link PersistedTabData} for Shopping related websites
- *
- * This class should not be merged because it is being used as a key in a Map
- * in PersistedTabDataConfiguration.java.
  */
-@DoNotClassMerge
 public class ShoppingPersistedTabData extends PersistedTabData {
     private static final String TAG = "SPTD";
     private static final String STALE_TAB_THRESHOLD_SECONDS_PARAM =
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java
index 76e105d3..b82541dc 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/StorePersistedTabData.java
@@ -14,7 +14,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
-import org.chromium.base.annotations.DoNotClassMerge;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -29,11 +28,7 @@
 /**
  * {@link PersistedTabData} for Store websites with opening/closing hours.
  * TODO(crbug.com/1199134) Add tests for StorePersistedTabData
- *
- * This class should not be merged because it is being used as a key in a Map
- * in PersistedTabDataConfiguration.java.
  */
-@DoNotClassMerge
 public class StorePersistedTabData extends PersistedTabData {
     private static final String MISSING_STRING = "missing";
     private static final String COLON_STRING = ":";
diff --git a/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc
index cc08d92..ddedbe5 100644
--- a/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc
+++ b/chrome/browser/task_manager/providers/web_contents/devtools_tag_browsertest.cc
@@ -63,7 +63,7 @@
 
 // Tests that opening a DevToolsWindow will result in tagging its main
 // WebContents and that tag will be recorded by the TagsManager.
-IN_PROC_BROWSER_TEST_F(DevToolsTagTest, TagsManagerRecordsATag) {
+IN_PROC_BROWSER_TEST_F(DevToolsTagTest, DISABLED_TagsManagerRecordsATag) {
   // Browser tests start with a single tab.
   EXPECT_EQ(1U, tags_manager()->tracked_tags().size());
 
diff --git a/chrome/browser/touch_to_fill/android/BUILD.gn b/chrome/browser/touch_to_fill/android/BUILD.gn
index a415ebb..079a203 100644
--- a/chrome/browser/touch_to_fill/android/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/BUILD.gn
@@ -62,6 +62,7 @@
     "//chrome/browser/flags:java",
     "//chrome/browser/touch_to_fill/android:public_java",
     "//chrome/browser/touch_to_fill/android/internal:java",
+    "//chrome/browser/touch_to_fill/android/internal:resource_provider_public_impl_java",
     "//chrome/browser/ui/android/favicon:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/browser_ui/bottomsheet/android:java",
@@ -95,6 +96,7 @@
     "//chrome/android:chrome_test_util_java",
     "//chrome/browser/flags:java",
     "//chrome/browser/touch_to_fill/android/internal:java",
+    "//chrome/browser/touch_to_fill/android/internal:resource_provider_public_impl_java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/browser_ui/bottomsheet/android:java",
     "//components/browser_ui/bottomsheet/android/test:java",
diff --git a/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
index bb59c32..a06ec37 100644
--- a/chrome/browser/touch_to_fill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -22,6 +22,7 @@
     "//components/favicon/android:java",
     "//components/url_formatter/android:url_formatter_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//ui/android:ui_java",
     "//url:gurl_java",
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
index c717b4f..0b73a70 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
@@ -14,11 +14,11 @@
     android:orientation="vertical">
 
     <ImageView
+        android:id="@+id/touch_to_fill_sheet_header_image"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
-        android:importantForAccessibility="no"
-        app:srcCompat="@drawable/touch_to_fill_header_image" />
+        android:importantForAccessibility="no" />
 
     <org.chromium.ui.widget.TextViewWithLeading
         android:id="@+id/touch_to_fill_sheet_title"
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
index c46ec6ad..339e955 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
@@ -10,6 +10,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL;
@@ -69,6 +70,8 @@
         assert credentials != null;
         mModel.set(ON_CLICK_MANAGE, this::onManagePasswordSelected);
 
+        TouchToFillResourceProvider resourceProvider = new TouchToFillResourceProviderImpl();
+
         ListModel<ListItem> sheetItems = mModel.get(SHEET_ITEMS);
         sheetItems.clear();
 
@@ -83,6 +86,7 @@
                                         url, SchemeDisplay.OMIT_HTTP_AND_HTTPS))
                         .with(ORIGIN_SECURE, isOriginSecure)
                         .with(SHOW_SUBMIT_SUBTITLE, show_submit_subtitle)
+                        .with(IMAGE_DRAWABLE_ID, resourceProvider.getHeaderImageDrawableId())
                         .build()));
 
         mCredentials = credentials;
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
index 3995ac56..d7de24f 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
@@ -93,9 +93,11 @@
                 new PropertyModel.ReadableObjectPropertyKey<>("formatted_url");
         static final PropertyModel.ReadableBooleanPropertyKey ORIGIN_SECURE =
                 new PropertyModel.ReadableBooleanPropertyKey("origin_secure");
+        static final PropertyModel.ReadableIntPropertyKey IMAGE_DRAWABLE_ID =
+                new PropertyModel.ReadableIntPropertyKey("image_drawable_id");
 
-        static final PropertyKey[] ALL_KEYS = {
-                SHOW_SUBMIT_SUBTITLE, SINGLE_CREDENTIAL, FORMATTED_URL, ORIGIN_SECURE};
+        static final PropertyKey[] ALL_KEYS = {SHOW_SUBMIT_SUBTITLE, SINGLE_CREDENTIAL,
+                FORMATTED_URL, ORIGIN_SECURE, IMAGE_DRAWABLE_ID};
 
         private HeaderProperties() {}
     }
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
index 5a8d8811..1c00d12 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
@@ -11,6 +11,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL;
@@ -27,6 +28,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.StringRes;
+import androidx.appcompat.content.res.AppCompatResources;
 
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties;
@@ -233,12 +235,16 @@
      */
     private static void bindHeaderView(PropertyModel model, View view, PropertyKey key) {
         if (key == SHOW_SUBMIT_SUBTITLE || key == SINGLE_CREDENTIAL || key == FORMATTED_URL
-                || key == ORIGIN_SECURE) {
+                || key == ORIGIN_SECURE || key == IMAGE_DRAWABLE_ID) {
             TextView sheetTitleText = view.findViewById(R.id.touch_to_fill_sheet_title);
             sheetTitleText.setText(getTitle(model, view.getContext()));
 
             TextView sheetSubtitleText = view.findViewById(R.id.touch_to_fill_sheet_subtitle);
             sheetSubtitleText.setText(getSubtitle(model, view.getContext()));
+
+            ImageView sheetHeaderImage = view.findViewById(R.id.touch_to_fill_sheet_header_image);
+            sheetHeaderImage.setImageDrawable(AppCompatResources.getDrawable(
+                    view.getContext(), model.get(IMAGE_DRAWABLE_ID)));
         } else {
             assert false : "Unhandled update to property:" + key;
         }
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
index 4cd1e3b..169208f 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
@@ -18,6 +18,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL;
@@ -89,6 +90,7 @@
     private PropertyModel mModel;
     private TouchToFillView mTouchToFillView;
     private BottomSheetController mBottomSheetController;
+    TouchToFillResourceProvider mResourceProvider;
 
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@@ -100,6 +102,7 @@
         mBottomSheetController = mActivityTestRule.getActivity()
                                          .getRootUiCoordinatorForTesting()
                                          .getBottomSheetController();
+        mResourceProvider = new TouchToFillResourceProviderImpl();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel = TouchToFillProperties.createDefaultModel(mDismissHandler);
             mTouchToFillView = new TouchToFillView(getActivity(), mBottomSheetController);
@@ -134,6 +137,8 @@
                                     .with(SINGLE_CREDENTIAL, true)
                                     .with(FORMATTED_URL, "www.example.org")
                                     .with(ORIGIN_SECURE, true)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
@@ -158,6 +163,8 @@
                                     .with(SINGLE_CREDENTIAL, false)
                                     .with(FORMATTED_URL, "www.example.org")
                                     .with(ORIGIN_SECURE, true)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
@@ -180,6 +187,8 @@
                                     .with(SINGLE_CREDENTIAL, true)
                                     .with(FORMATTED_URL, "www.example.org")
                                     .with(ORIGIN_SECURE, true)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
@@ -202,6 +211,8 @@
                                     .with(SINGLE_CREDENTIAL, false)
                                     .with(FORMATTED_URL, "www.example.org")
                                     .with(ORIGIN_SECURE, true)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
@@ -222,6 +233,8 @@
                             new PropertyModel.Builder(HeaderProperties.ALL_KEYS)
                                     .with(FORMATTED_URL, "www.example.org")
                                     .with(ORIGIN_SECURE, true)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
@@ -241,6 +254,8 @@
                             new PropertyModel.Builder(HeaderProperties.ALL_KEYS)
                                     .with(FORMATTED_URL, "m.example.org")
                                     .with(ORIGIN_SECURE, false)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
@@ -262,6 +277,8 @@
                                     .with(SHOW_SUBMIT_SUBTITLE, true)
                                     .with(FORMATTED_URL, "m.example.org")
                                     .with(ORIGIN_SECURE, true)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
@@ -283,6 +300,8 @@
                                     .with(SHOW_SUBMIT_SUBTITLE, true)
                                     .with(FORMATTED_URL, "m.example.org")
                                     .with(ORIGIN_SECURE, false)
+                                    .with(IMAGE_DRAWABLE_ID,
+                                            mResourceProvider.getHeaderImageDrawableId())
                                     .build()));
             mModel.set(VISIBLE, true);
         });
diff --git a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
index f88d290..365bea7 100644
--- a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
+++ b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
@@ -22,6 +22,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SHOW_SUBMIT_SUBTITLE;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.SINGLE_CREDENTIAL;
@@ -155,6 +156,8 @@
         assertThat(itemList.get(2).model.get(CREDENTIAL), is(CARL));
         assertNotNull(itemList.get(2).model.get(ON_CLICK_LISTENER));
         assertThat(itemList.get(2).model.get(FORMATTED_ORIGIN), is(format(CARL.getOriginUrl())));
+        assertThat(itemList.get(0).model.get(IMAGE_DRAWABLE_ID),
+                is(R.drawable.touch_to_fill_header_image));
     }
 
     @Test
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 936021a..ad7eb75 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2957,6 +2957,8 @@
       "//ash/components/drivefs/mojom:mojom",
       "//ash/components/login/auth",
       "//ash/components/login/session",
+      "//ash/components/multidevice",
+      "//ash/components/multidevice/logging",
       "//ash/components/peripheral_notification",
       "//ash/components/phonehub",
       "//ash/components/phonehub:debug",
@@ -3052,8 +3054,6 @@
       "//chromeos/assistant:buildflags",
       "//chromeos/components/local_search_service/public/cpp",
       "//chromeos/components/local_search_service/public/mojom",
-      "//chromeos/components/multidevice",
-      "//chromeos/components/multidevice/logging",
       "//chromeos/components/onc",
       "//chromeos/components/quick_answers",
       "//chromeos/components/quick_answers/public/cpp:cpp",
@@ -4163,8 +4163,6 @@
       "views/device_chooser_content_view.h",
       "views/devtools_process_observer.cc",
       "views/devtools_process_observer.h",
-      "views/download/bubble/download_bubble_controller.cc",
-      "views/download/bubble/download_bubble_controller.h",
       "views/download/bubble/download_bubble_row_list_view.cc",
       "views/download/bubble/download_bubble_row_list_view.h",
       "views/download/bubble/download_bubble_row_view.cc",
@@ -5149,6 +5147,7 @@
       "//extensions/components/native_app_window",
       "//extensions/strings",
       "//ui/base/cursor",
+      "//ui/color:color",
     ]
     allow_circular_includes_from += [
       "//chrome/browser/apps/platform_apps",
diff --git a/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java b/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java
index 7ad1fce..2320fefd 100644
--- a/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java
+++ b/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconUtils.java
@@ -9,6 +9,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.Nullable;
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
 
@@ -80,7 +81,7 @@
      * @return A {@link Drawable} to be displayed as the favicon.
      */
     public static Drawable getIconDrawableWithoutFilter(@Nullable Bitmap icon, GURL url,
-            int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources,
+            @ColorInt int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources,
             int iconSize) {
         return getIconDrawableWithoutFilter(
                 icon, url.getSpec(), fallbackColor, iconGenerator, resources, iconSize);
@@ -88,7 +89,7 @@
 
     @Deprecated // Use GURL variant instead.
     public static Drawable getIconDrawableWithoutFilter(@Nullable Bitmap icon, String url,
-            int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources,
+            @ColorInt int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources,
             int iconSize) {
         if (icon == null) {
             iconGenerator.setBackgroundColor(fallbackColor);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java
index 4647248..ecc13975 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java
@@ -31,7 +31,7 @@
     }
 
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         long createAutocompleteClassifier(Profile profile);
         void deleteAutocompleteClassifier(long chromeAutocompleteSchemeClassifier);
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index df8e396d..1dec07d 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
+import android.util.LruCache;
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.ColorRes;
@@ -42,6 +43,7 @@
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification;
+import org.chromium.components.omnibox.AutocompleteSchemeClassifier;
 import org.chromium.components.omnibox.OmniboxUrlEmphasizer;
 import org.chromium.components.omnibox.SecurityStatusIcon;
 import org.chromium.components.prefs.PrefService;
@@ -54,11 +56,59 @@
 import org.chromium.url.URI;
 
 import java.net.URISyntaxException;
+import java.util.Objects;
 
 /**
  * Provides a way of accessing toolbar data and state.
  */
 public class LocationBarModel implements ToolbarDataProvider, LocationBarDataProvider {
+    private static final int LRU_CACHE_SIZE = 10;
+    static class SpannableDisplayTextCacheKey {
+        @NonNull
+        private final String mUrl;
+        @NonNull
+        private final String mDisplayText;
+        private final int mSecurityLevel;
+        private final int mNonEmphasizedColor;
+        private final int mEmphasizedColor;
+        private final int mDangerColor;
+        private final int mSecureColor;
+
+        private SpannableDisplayTextCacheKey(@NonNull String url, @NonNull String displayText,
+                int securityLevel, int nonEmphasizedColor, int emphasizedColor, int dangerColor,
+                int secureColor) {
+            mUrl = url;
+            mDisplayText = displayText;
+            mSecurityLevel = securityLevel;
+            mNonEmphasizedColor = nonEmphasizedColor;
+            mEmphasizedColor = emphasizedColor;
+            mDangerColor = dangerColor;
+            mSecureColor = secureColor;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            SpannableDisplayTextCacheKey that = (SpannableDisplayTextCacheKey) o;
+            return mSecurityLevel == that.mSecurityLevel
+                    && mNonEmphasizedColor == that.mNonEmphasizedColor
+                    && mEmphasizedColor == that.mEmphasizedColor
+                    && mDangerColor == that.mDangerColor && mSecureColor == that.mSecureColor
+                    && mUrl.equals(that.mUrl) && mDisplayText.equals(that.mDisplayText);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mUrl, mDisplayText, mSecurityLevel, mNonEmphasizedColor,
+                    mEmphasizedColor, mDangerColor, mSecureColor);
+        }
+    }
+
     /**
      * Formats the given URL to the original one of a distillation.
      */
@@ -100,6 +150,19 @@
     private final @NonNull ProfileProvider mProfileProvider;
     private final @NonNull OfflineStatus mOfflineStatus;
     private final SearchEngineLogoUtils mSearchEngineLogoUtils;
+    // Always null if optimizations are disabled. Otherwise, non-null and unchanging following
+    // native init. Always tied to the mLastUsedNonOTRProfile which is safe because no underlying
+    // services have an incognito-specific instance.
+    @Nullable
+    private AutocompleteSchemeClassifier mChromeAutocompleteSchemeClassifier;
+    // Non-null and unchanging following native init. The last used non-OTR (or regular) profile
+    // can't change after this point because we don't support multi-profile on Android.
+    @Nullable
+    private Profile mLastUsedNonOTRProfile;
+    @Nullable
+    private LruCache<SpannableDisplayTextCacheKey, SpannableStringBuilder>
+            mSpannableDisplayTextCache;
+    private boolean mOptimizationsEnabled;
 
     private Tab mTab;
     private int mPrimaryColor;
@@ -141,6 +204,15 @@
      * Handle any initialization that must occur after native has been initialized.
      */
     public void initializeWithNative() {
+        mOptimizationsEnabled =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.LOCATION_BAR_MODEL_OPTIMIZATIONS);
+        mLastUsedNonOTRProfile = Profile.getLastUsedRegularProfile();
+        if (mOptimizationsEnabled) {
+            mSpannableDisplayTextCache = new LruCache<>(LRU_CACHE_SIZE);
+            mChromeAutocompleteSchemeClassifier =
+                    new ChromeAutocompleteSchemeClassifier(getProfile());
+        }
+
         mNativeLocationBarModelAndroid = LocationBarModelJni.get().init(LocationBarModel.this);
     }
 
@@ -148,6 +220,10 @@
      * Destroys the native LocationBarModel.
      */
     public void destroy() {
+        if (mChromeAutocompleteSchemeClassifier != null) {
+            mChromeAutocompleteSchemeClassifier.destroy();
+            mChromeAutocompleteSchemeClassifier = null;
+        }
         if (mNativeLocationBarModelAndroid == 0) return;
         LocationBarModelJni.get().destroy(mNativeLocationBarModelAndroid, LocationBarModel.this);
         mNativeLocationBarModelAndroid = 0;
@@ -207,19 +283,19 @@
     }
 
     @Override
+    // TODO(https://crbug.com/1305374): migrate to GURL.
     public String getCurrentUrl() {
         // Provide NTP url instead of most recent tab url for searches in overview mode (when Start
-        // Surface is enabled). .
+        // Surface is enabled).
+        String url = getTab() != null && getTab().isInitialized()
+                ? getTab().getUrl().getSpec().trim()
+                : "";
         if (isInOverviewAndShowingOmnibox()
-                || StartSurfaceConfiguration.shouldHandleAsNtp(getTab())) {
+                || StartSurfaceConfiguration.shouldHandleAsNtp(getTab(), url)) {
             return UrlConstants.NTP_URL;
         }
 
-        // TODO(yusufo) : Consider using this for all calls from getTab() for accessing url.
-        if (!hasTab() || !getTab().isInitialized()) return "";
-
-        // Tab.getUrl() returns empty string if it does not have a URL.
-        return getTab().getUrl().getSpec().trim();
+        return url;
     }
 
     public void notifyUrlChanged() {
@@ -250,11 +326,11 @@
         // Part of scroll jank investigation http://crbug.com/905461. Will remove TraceEvent after
         // the investigation is complete.
         try (TraceEvent te = TraceEvent.scoped("LocationBarModel.getUrlBarData")) {
-            if (!hasTab() || StartSurfaceConfiguration.shouldHandleAsNtp(getTab())) {
+            String url = getCurrentUrl();
+            if (!hasTab() || StartSurfaceConfiguration.shouldHandleAsNtp(getTab(), url)) {
                 return UrlBarData.EMPTY;
             }
 
-            String url = getCurrentUrl();
             if (!UrlBarData.shouldShowUrl(url, isIncognito())) {
                 return UrlBarData.EMPTY;
             }
@@ -299,7 +375,6 @@
 
     private UrlBarData buildUrlBarData(String url, String displayText, String editingText) {
         SpannableStringBuilder spannableDisplayText = new SpannableStringBuilder(displayText);
-
         if (mNativeLocationBarModelAndroid != 0 && spannableDisplayText.length() > 0
                 && shouldEmphasizeUrl()) {
             boolean isInternalPage = false;
@@ -309,8 +384,6 @@
                 // Ignore as this only is for applying color
             }
 
-            ChromeAutocompleteSchemeClassifier chromeAutocompleteSchemeClassifier =
-                    new ChromeAutocompleteSchemeClassifier(getProfile());
             final @BrandedColorScheme int brandedColorScheme =
                     OmniboxResourceProvider.getBrandedColorScheme(
                             mContext, isIncognito(), getPrimaryColor());
@@ -323,13 +396,32 @@
                     OmniboxResourceProvider.getUrlBarDangerColor(mContext, brandedColorScheme);
             final @ColorInt int secureColor =
                     OmniboxResourceProvider.getUrlBarSecureColor(mContext, brandedColorScheme);
-            OmniboxUrlEmphasizer.emphasizeUrl(spannableDisplayText,
-                    chromeAutocompleteSchemeClassifier, getSecurityLevel(), isInternalPage,
-                    shouldEmphasizeHttpsScheme(), nonEmphasizedColor, emphasizedColor, dangerColor,
-                    secureColor);
-            chromeAutocompleteSchemeClassifier.destroy();
-        }
 
+            AutocompleteSchemeClassifier autocompleteSchemeClassifier;
+            SpannableDisplayTextCacheKey cacheKey =
+                    new SpannableDisplayTextCacheKey(url, displayText, getSecurityLevel(),
+                            nonEmphasizedColor, emphasizedColor, dangerColor, secureColor);
+            SpannableStringBuilder cachedSpannableDisplayText = null;
+            if (mOptimizationsEnabled) {
+                autocompleteSchemeClassifier = mChromeAutocompleteSchemeClassifier;
+                cachedSpannableDisplayText = mSpannableDisplayTextCache.get(cacheKey);
+            } else {
+                autocompleteSchemeClassifier = new ChromeAutocompleteSchemeClassifier(getProfile());
+            }
+
+            if (cachedSpannableDisplayText != null) {
+                return UrlBarData.forUrlAndText(url, cachedSpannableDisplayText, editingText);
+            } else {
+                OmniboxUrlEmphasizer.emphasizeUrl(spannableDisplayText,
+                        autocompleteSchemeClassifier, getSecurityLevel(), isInternalPage,
+                        shouldEmphasizeHttpsScheme(), nonEmphasizedColor, emphasizedColor,
+                        dangerColor, secureColor);
+                if (mOptimizationsEnabled) {
+                    mSpannableDisplayTextCache.put(cacheKey, spannableDisplayText);
+                }
+            }
+            if (!mOptimizationsEnabled) autocompleteSchemeClassifier.destroy();
+        }
         return UrlBarData.forUrlAndText(url, spannableDisplayText, editingText);
     }
 
@@ -344,6 +436,11 @@
         return TrustedCdn.getPublisherUrl(mTab) == null;
     }
 
+    @VisibleForTesting
+    LruCache<SpannableDisplayTextCacheKey, SpannableStringBuilder> getCacheForTesting() {
+        return mSpannableDisplayTextCache;
+    }
+
     /**
      * @return Whether the light security theme should be used.
      */
@@ -398,7 +495,6 @@
 
     @Override
     public Profile getProfile() {
-        Profile lastUsedRegularProfile = Profile.getLastUsedRegularProfile();
         if (mIsIncognito) {
             WindowAndroid windowAndroid = (mTab != null) ? mTab.getWindowAndroid() : null;
             // If the mTab belongs to a CustomTabActivity then we return the non-primary OTR profile
@@ -408,11 +504,11 @@
 
             // When in overview mode with no open tabs, there has not been created an
             // OTR profile yet.
-            assert lastUsedRegularProfile.hasPrimaryOTRProfile() || isInOverviewAndShowingOmnibox();
+            assert mLastUsedNonOTRProfile.hasPrimaryOTRProfile() || isInOverviewAndShowingOmnibox();
             // Return the primary OTR profile.
-            return lastUsedRegularProfile.getPrimaryOTRProfile(/*createIfNeeded=*/true);
+            return mLastUsedNonOTRProfile.getPrimaryOTRProfile(/*createIfNeeded=*/true);
         }
-        return lastUsedRegularProfile;
+        return mLastUsedNonOTRProfile;
     }
 
     public void setLayoutStateProvider(LayoutStateProvider layoutStateProvider) {
diff --git a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml
index 36852cc1..ed01b838 100644
--- a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml
+++ b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_data_sharing_consent_item.xml
@@ -12,7 +12,7 @@
     android:layout_marginEnd="@dimen/account_selection_sheet_horizontal_margin"
     android:layout_marginStart="@dimen/account_selection_sheet_horizontal_margin"
     android:layout_marginBottom="@dimen/account_selection_sheet_item_padding"
-    android:layout_marginTop="@dimen/account_selection_sheet_item_padding"
+    android:layout_marginTop="8dp"
     android:gravity="start"
     android:textAppearance="@style/TextAppearance.TextSmall.Secondary"
     android:minHeight="@dimen/account_selection_sheet_title_height"/>
diff --git a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml
index 1a93a0e..52f3041 100644
--- a/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml
+++ b/chrome/browser/ui/android/webid/internal/java/res/layout/account_selection_header_item.xml
@@ -35,6 +35,8 @@
           android:layout_height="wrap_content"
           android:layout_weight="1"
           android:layout_gravity="center_vertical"
+          android:paddingTop="12dp"
+          android:paddingBottom="12dp"
           android:focusable="true"
           android:focusableInTouchMode="true"
           android:screenReaderFocusable="true"
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index 9de15dab..8da415b 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -310,8 +310,6 @@
 
 void AppListClientImpl::ViewClosing() {
   display_id_ = display::kInvalidDisplayId;
-  if (search_controller_)
-    search_controller_->ViewClosing();
 }
 
 void AppListClientImpl::ViewShown(int64_t display_id) {
@@ -396,6 +394,8 @@
       !ash::features::IsProductivityLauncherEnabled()) {
     search_controller_->StartSearch(std::u16string());
   }
+  if (!visible && search_controller_)
+    search_controller_->ViewClosing();
 }
 
 void AppListClientImpl::OnAppListVisibilityChanged(bool visible) {
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
index 9038075..560b824 100644
--- a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
@@ -122,20 +122,6 @@
   return {text_item};
 }
 
-std::vector<TextItem> AddBoldTags(std::vector<TextItem> text_vector) {
-  std::vector<TextItem> bolded_vector;
-  for (const auto& old_text : text_vector) {
-    auto new_text = old_text;
-    if (old_text.GetType() == TextType::kString) {
-      auto tags = old_text.GetTextTags();
-      tags.push_back(Tag(Tag::MATCH, 0, old_text.GetText().length()));
-      new_text.SetTextTags(tags);
-    }
-    bolded_vector.push_back(new_text);
-  }
-  return bolded_vector;
-}
-
 std::u16string ComputeAccessibleName(
     const std::vector<std::vector<TextItem>>& text_vectors) {
   std::vector<std::u16string> text;
@@ -217,12 +203,12 @@
     std::vector<TextItem> contents_vector =
         MatchFieldsToTextVector(match_.contents, match_.contents_class);
     if (match_.description.empty()) {
-      SetTitleTextVector(contents_vector);
-      SetDetailsTextVector({CreateStringTextItem(query_)});
-    } else {
-      SetTitleTextVector(MatchFieldsToTextVector(match_.description,
-                                                 match_.description_class));
+      SetTitleTextVector({CreateStringTextItem(query_)});
       SetDetailsTextVector(contents_vector);
+    } else {
+      SetTitleTextVector(contents_vector);
+      SetDetailsTextVector(MatchFieldsToTextVector(match_.description,
+                                                   match_.description_class));
     }
   } else if (IsWeatherResult()) {
     const auto& second_line = match_.answer->second_line();
@@ -246,18 +232,10 @@
     auto second_vector = ImageLineToTextVector(second_line);
     AppendAdditionalText(second_line, second_vector);
 
-    if (IsDictionaryResult()) {
-      SetTitleTextVector(first_vector);
-      SetDetailsTextVector(second_vector);
-    } else {
-      SetTitleTextVector(second_vector);
-      SetDetailsTextVector(first_vector);
-    }
+    SetTitleTextVector(first_vector);
+    SetDetailsTextVector(second_vector);
   }
 
-  // Bold the title field.
-  SetTitleTextVector(AddBoldTags(title_text_vector()));
-
   std::u16string accessible_name = ComputeAccessibleName(
       {big_title_text_vector(), title_text_vector(), details_text_vector()});
   SetAccessibleName(accessible_name);
@@ -315,11 +293,6 @@
   return match_.type == AutocompleteMatchType::CALCULATOR;
 }
 
-bool OmniboxAnswerResult::IsDictionaryResult() const {
-  return match_.answer.has_value() &&
-         match_.answer->type() == SuggestionAnswer::ANSWER_TYPE_DICTIONARY;
-}
-
 bool OmniboxAnswerResult::IsWeatherResult() const {
   return match_.answer.has_value() &&
          match_.answer->type() == SuggestionAnswer::ANSWER_TYPE_WEATHER;
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.h b/chrome/browser/ui/app_list/search/omnibox_answer_result.h
index 623ce49b..a121accc 100644
--- a/chrome/browser/ui/app_list/search/omnibox_answer_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.h
@@ -49,7 +49,6 @@
   void FetchImage(const GURL& url);
 
   bool IsCalculatorResult() const;
-  bool IsDictionaryResult() const;
   bool IsWeatherResult() const;
 
   Profile* profile_;
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc
index c81d397..47ae7f0 100644
--- a/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc
@@ -108,15 +108,13 @@
   ASSERT_EQ(result.title_text_vector().size(), 1);
   const auto& title = result.title_text_vector()[0];
   ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(title.GetText(), u"4");
-  EXPECT_THAT(title.GetTextTags(),
-              testing::UnorderedElementsAre(TagEquals(
-                  Tag(Tag::Style::MATCH, 0, title.GetText().length()))));
+  EXPECT_EQ(title.GetText(), u"2+2");
+  EXPECT_TRUE(title.GetTextTags().empty());
 
   ASSERT_EQ(result.details_text_vector().size(), 1);
   const auto& details = result.details_text_vector()[0];
   ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(details.GetText(), u"2+2");
+  EXPECT_EQ(details.GetText(), u"4");
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
@@ -133,15 +131,13 @@
   ASSERT_EQ(result.title_text_vector().size(), 1);
   const auto& title = result.title_text_vector()[0];
   ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(title.GetText(), u"4");
-  EXPECT_THAT(title.GetTextTags(),
-              testing::UnorderedElementsAre(TagEquals(
-                  Tag(Tag::Style::MATCH, 0, title.GetText().length()))));
+  EXPECT_EQ(title.GetText(), u"2+2");
+  EXPECT_TRUE(title.GetTextTags().empty());
 
   ASSERT_EQ(result.details_text_vector().size(), 1);
   const auto& details = result.details_text_vector()[0];
   ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(details.GetText(), u"2+2");
+  EXPECT_EQ(details.GetText(), u"4");
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
@@ -185,9 +181,7 @@
   // Suggest server, this should be updated to display the weather description
   // instead of |match.contents|.
   EXPECT_EQ(title.GetText(), u"contents");
-  EXPECT_THAT(title.GetTextTags(),
-              testing::UnorderedElementsAre(TagEquals(
-                  Tag(Tag::Style::MATCH, 0, title.GetText().length()))));
+  EXPECT_TRUE(title.GetTextTags().empty());
 
   ASSERT_EQ(result.details_text_vector().size(), 1);
   const auto& details = result.details_text_vector()[0];
@@ -196,71 +190,6 @@
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
-TEST_F(OmniboxAnswerResultTest, DictionaryResult) {
-  // This comes from SuggestionAnswer::AnswerType::ANSWER_TYPE_DICTIONARY.
-  const std::u16string kDictionaryType = u"1";
-
-  SuggestionAnswer answer;
-  std::string json =
-      "{ \"l\": ["
-      "  { \"il\": { \"t\": [{ \"t\": \"text one\", \"tt\": 8 }], "
-      "              \"at\": { \"t\": \"additional one\", \"tt\": 42 } } }, "
-      "  { \"il\": { \"t\": [{ \"t\": \"text two\", \"tt\": 8 }], "
-      "              \"at\": { \"t\": \"additional two\", \"tt\": 42 } } } "
-      "] }";
-  absl::optional<base::Value> value = base::JSONReader::Read(json);
-  ASSERT_TRUE(value && value->is_dict());
-  ASSERT_TRUE(SuggestionAnswer::ParseAnswer(value->GetDict(), kDictionaryType,
-                                            &answer));
-
-  AutocompleteMatch match;
-  match.answer = answer;
-  match.contents = u"contents";
-  match.description = u"description";
-
-  OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"query");
-  EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kAnswerCard);
-  EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
-  EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_ANSWER);
-
-  // All title fields should have the MATCH tag, and there should be a space
-  // delimiter added between each field.
-  const auto& title = result.title_text_vector();
-  ASSERT_EQ(title.size(), 3);
-  ASSERT_EQ(title[0].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(title[0].GetText(), u"contents");
-  size_t length = title[0].GetText().length();
-  EXPECT_THAT(title[0].GetTextTags(), testing::UnorderedElementsAre(TagEquals(
-                                          Tag(Tag::Style::MATCH, 0, length))));
-
-  ASSERT_EQ(title[1].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(title[1].GetText(), u" ");
-  length = title[1].GetText().length();
-  EXPECT_THAT(title[1].GetTextTags(), testing::UnorderedElementsAre(TagEquals(
-                                          Tag(Tag::Style::MATCH, 0, length))));
-
-  ASSERT_EQ(title[2].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(title[2].GetText(), u"additional one");
-  length = title[2].GetText().length();
-  EXPECT_THAT(title[2].GetTextTags(), testing::UnorderedElementsAre(TagEquals(
-                                          Tag(Tag::Style::MATCH, 0, length))));
-
-  // Details text should not have tags.
-  const auto& details = result.details_text_vector();
-  ASSERT_EQ(details.size(), 3);
-  ASSERT_EQ(details[0].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(details[0].GetText(), u"text two");
-  EXPECT_TRUE(details[0].GetTextTags().empty());
-
-  ASSERT_EQ(details[1].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(details[1].GetText(), u" ");
-  EXPECT_TRUE(details[1].GetTextTags().empty());
-
-  ASSERT_EQ(details[2].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(details[2].GetText(), u"additional two");
-  EXPECT_TRUE(details[2].GetTextTags().empty());
-}
-
 TEST_F(OmniboxAnswerResultTest, AnswerResult) {
   // This comes from SuggestionAnswer::AnswerType::ANSWER_TYPE_FINANCE.
   const std::u16string kWeatherType = u"2";
@@ -290,47 +219,41 @@
   EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
   EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_ANSWER);
 
-  // All title fields should have the MATCH tag, and there should be a space
-  // delimiter added between each field. Additionally, the NEGATIVE text type
-  // should have RED tags, and the POSITIVE text type should have GREEN tags.
   const auto& title = result.title_text_vector();
   ASSERT_EQ(title.size(), 3);
   ASSERT_EQ(title[0].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(title[0].GetText(), u"text two");
-  size_t length = title[0].GetText().length();
-  EXPECT_THAT(title[0].GetTextTags(),
-              testing::UnorderedElementsAre(
-                  TagEquals(Tag(Tag::Style::MATCH, 0, length)),
-                  TagEquals(Tag(Tag::Style::RED, 0, length))));
+  EXPECT_EQ(title[0].GetText(), u"contents");
+  EXPECT_TRUE(title[0].GetTextTags().empty());
 
   ASSERT_EQ(title[1].GetType(), ash::SearchResultTextItemType::kString);
   EXPECT_EQ(title[1].GetText(), u" ");
-  length = title[1].GetText().length();
-  EXPECT_THAT(title[1].GetTextTags(), testing::UnorderedElementsAre(TagEquals(
-                                          Tag(Tag::Style::MATCH, 0, length))));
+  EXPECT_TRUE(title[1].GetTextTags().empty());
 
   ASSERT_EQ(title[2].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(title[2].GetText(), u"additional two");
-  length = title[2].GetText().length();
-  EXPECT_THAT(title[2].GetTextTags(),
-              testing::UnorderedElementsAre(
-                  TagEquals(Tag(Tag::Style::MATCH, 0, length)),
-                  TagEquals(Tag(Tag::Style::GREEN, 0, length))));
+  EXPECT_EQ(title[2].GetText(), u"additional one");
+  EXPECT_TRUE(title[2].GetTextTags().empty());
 
-  // Details text should not have tags.
+  // The NEGATIVE text type should have RED tags, and the POSITIVE text type
+  // should have GREEN tags.
   const auto& details = result.details_text_vector();
   ASSERT_EQ(details.size(), 3);
   ASSERT_EQ(details[0].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(details[0].GetText(), u"contents");
-  EXPECT_TRUE(details[0].GetTextTags().empty());
+  EXPECT_EQ(details[0].GetText(), u"text two");
+  size_t length = details[0].GetText().length();
+  EXPECT_THAT(details[0].GetTextTags(), testing::UnorderedElementsAre(TagEquals(
+                                            Tag(Tag::Style::RED, 0, length))));
 
   ASSERT_EQ(details[1].GetType(), ash::SearchResultTextItemType::kString);
   EXPECT_EQ(details[1].GetText(), u" ");
+  length = details[1].GetText().length();
   EXPECT_TRUE(details[1].GetTextTags().empty());
 
   ASSERT_EQ(details[2].GetType(), ash::SearchResultTextItemType::kString);
-  EXPECT_EQ(details[2].GetText(), u"additional one");
-  EXPECT_TRUE(details[2].GetTextTags().empty());
+  EXPECT_EQ(details[2].GetText(), u"additional two");
+  length = details[2].GetText().length();
+  EXPECT_THAT(details[2].GetTextTags(),
+              testing::UnorderedElementsAre(
+                  TagEquals(Tag(Tag::Style::GREEN, 0, length))));
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc b/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc
index 2e857963..30177d6 100644
--- a/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc
+++ b/chrome/browser/ui/app_list/search/ranking/query_highlighter.cc
@@ -44,17 +44,16 @@
   DCHECK(it != results.end());
 
   for (const auto& result : it->second) {
-    // Don't perform query highlighting on answer cards.
-    if (result->display_type() == ChromeSearchResult::DisplayType::kAnswerCard)
-      continue;
-
     TextVector title_vector = result->title_text_vector();
     SetMatchTags(last_query_, title_vector);
     result->SetTitleTextVector(title_vector);
 
-    TextVector details_vector = result->details_text_vector();
-    SetMatchTags(last_query_, details_vector);
-    result->SetDetailsTextVector(details_vector);
+    if (result->display_type() !=
+        ChromeSearchResult::DisplayType::kAnswerCard) {
+      TextVector details_vector = result->details_text_vector();
+      SetMatchTags(last_query_, details_vector);
+      result->SetDetailsTextVector(details_vector);
+    }
   }
 }
 
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 5eb2c7af..6900baa 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -110,6 +110,32 @@
   return std::make_unique<ChromeCaptureModeDelegate>();
 }
 
+ash::AccessibilityDelegate* ChromeShellDelegate::CreateAccessibilityDelegate() {
+  return new ChromeAccessibilityDelegate;
+}
+
+std::unique_ptr<ash::BackGestureContextualNudgeDelegate>
+ChromeShellDelegate::CreateBackGestureContextualNudgeDelegate(
+    ash::BackGestureContextualNudgeController* controller) {
+  return std::make_unique<BackGestureContextualNudgeDelegate>(controller);
+}
+
+std::unique_ptr<ash::NearbyShareDelegate>
+ChromeShellDelegate::CreateNearbyShareDelegate(
+    ash::NearbyShareController* controller) const {
+  return std::make_unique<NearbyShareDelegateImpl>(controller);
+}
+
+std::unique_ptr<ash::DesksTemplatesDelegate>
+ChromeShellDelegate::CreateDesksTemplatesDelegate() const {
+  return std::make_unique<ChromeDesksTemplatesDelegate>();
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+ChromeShellDelegate::GetGeolocationSharedURLLoaderFactory() const {
+  return g_browser_process->shared_url_loader_factory();
+}
+
 void ChromeShellDelegate::OpenKeyboardShortcutHelpPage() const {
   ash::NewWindowDelegate::GetPrimary()->OpenUrl(
       GURL(kKeyboardShortcutHelpPageUrl),
@@ -193,27 +219,6 @@
   return &content::GetMediaSessionService();
 }
 
-ash::AccessibilityDelegate* ChromeShellDelegate::CreateAccessibilityDelegate() {
-  return new ChromeAccessibilityDelegate;
-}
-
-std::unique_ptr<ash::BackGestureContextualNudgeDelegate>
-ChromeShellDelegate::CreateBackGestureContextualNudgeDelegate(
-    ash::BackGestureContextualNudgeController* controller) {
-  return std::make_unique<BackGestureContextualNudgeDelegate>(controller);
-}
-
-std::unique_ptr<ash::NearbyShareDelegate>
-ChromeShellDelegate::CreateNearbyShareDelegate(
-    ash::NearbyShareController* controller) const {
-  return std::make_unique<NearbyShareDelegateImpl>(controller);
-}
-
-std::unique_ptr<ash::DesksTemplatesDelegate>
-ChromeShellDelegate::CreateDesksTemplatesDelegate() const {
-  return std::make_unique<ChromeDesksTemplatesDelegate>();
-}
-
 bool ChromeShellDelegate::IsSessionRestoreInProgress() const {
   Profile* profile = ProfileManager::GetActiveUserProfile();
   return SessionRestore::IsRestoring(profile);
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 1ff456f..57cb4704 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -32,6 +32,8 @@
       ash::NearbyShareController* controller) const override;
   std::unique_ptr<ash::DesksTemplatesDelegate> CreateDesksTemplatesDelegate()
       const override;
+  scoped_refptr<network::SharedURLLoaderFactory>
+  GetGeolocationSharedURLLoaderFactory() const override;
   void OpenKeyboardShortcutHelpPage() const override;
   bool CanGoBack(gfx::NativeWindow window) const override;
   void SetTabScrubberChromeOSEnabled(bool enabled) override;
diff --git a/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc b/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc
index ebb045e..1d69a526 100644
--- a/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc
+++ b/chrome/browser/ui/ash/desks_templates/chrome_desks_templates_delegate.cc
@@ -49,11 +49,11 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/paint_vector_icon.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "url/gurl.h"
 
 namespace {
diff --git a/chrome/browser/ui/ash/network/tether_notification_presenter.cc b/chrome/browser/ui/ash/network/tether_notification_presenter.cc
index 0f26818..f3528974 100644
--- a/chrome/browser/ui/ash/network/tether_notification_presenter.cc
+++ b/chrome/browser/ui/ash/network/tether_notification_presenter.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/public/cpp/network_icon_image_source.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "base/bind.h"
@@ -20,7 +21,6 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/network/network_connect.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
diff --git a/chrome/browser/ui/ash/network/tether_notification_presenter.h b/chrome/browser/ui/ash/network/tether_notification_presenter.h
index b3eabe4..50cb44f 100644
--- a/chrome/browser/ui/ash/network/tether_notification_presenter.h
+++ b/chrome/browser/ui/ash/network/tether_notification_presenter.h
@@ -8,10 +8,10 @@
 #include <memory>
 #include <string>
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/tether/notification_presenter.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "chromeos/network/network_state.h"
 #include "ui/message_center/public/cpp/notification.h"
 
diff --git a/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc b/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc
index 47311ca..e33b3b34 100644
--- a/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc
+++ b/chrome/browser/ui/ash/network/tether_notification_presenter_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/observer_list.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -14,7 +15,6 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "chromeos/network/network_connect.h"
 
 namespace {
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index 9d7bbb9..a65fc5d6 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -3120,18 +3120,29 @@
 };
 
 IN_PROC_BROWSER_TEST_F(FilesSystemWebAppPinnedTest, EnterpriseMigration) {
-  // Setup: the customer pins Files Chrome App (hhaomji... app ID).
+  // Setup: the customer pins Files Chrome App (ID:hhaomji...).
   base::DictionaryValue entry;
   entry.SetKey(ChromeShelfPrefs::kPinnedAppsPrefAppIDKey,
                base::Value(file_manager::kFileManagerAppId));
   base::ListValue policy_value;
   policy_value.Append(std::move(entry));
   profile()->GetPrefs()->Set(prefs::kPolicyPinnedLauncherApps, policy_value);
-  WaitForSystemAppsSynchronized();
 
-  // Expected results: fkiggjm... is pinned.
-  ash::ShelfID shelf_id(file_manager::kFileManagerSwaAppId);
-  EXPECT_TRUE(ChromeShelfController::instance()->IsPinned(shelf_id));
+  // Ensure shelf is updated.
+  WaitForSystemAppsSynchronized();
+  web_app::WebAppProvider::GetForTest(browser()->profile())
+      ->install_manager()
+      .NotifyWebAppInstalledWithOsHooks(file_manager::kFileManagerSwaAppId);
+  apps::AppServiceProxyFactory::GetForProfile(profile())
+      ->FlushMojoCallsForTesting();
+
+  // Expected results: Files SWA App (ID:fkiggjm...) is force-pinned.
+  ash::ShelfID swa_shelf_id(file_manager::kFileManagerSwaAppId);
+  EXPECT_TRUE(ChromeShelfController::instance()->IsPinned(swa_shelf_id));
+  ash::ShelfID extension_shelf_id(file_manager::kFileManagerAppId);
+  EXPECT_FALSE(ChromeShelfController::instance()->IsPinned(extension_shelf_id));
+  EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
+            GetPinnableForAppID(file_manager::kFileManagerSwaAppId, profile()));
 }
 
 INSTANTIATE_TEST_SUITE_P(All, PerDeskShelfAppBrowserTest, ::testing::Bool());
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
index 1e2158fa..f392a82 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
@@ -62,16 +62,18 @@
 // Chrome App.
 std::string GetPolicyValueFromAppId(const std::string& app_id,
                                     Profile* profile) {
-  // Handle Web App ids
+  // Handle App Service apps (eg. web apps).
   //
-  // WebAppProvider is absent in some cases e.g. Arc++ Kiosk Mode.
-  if (auto* provider = web_app::WebAppProvider::GetDeprecated(profile)) {
-    std::map<web_app::AppId, GURL> installed_apps =
-        provider->registrar().GetExternallyInstalledApps(
-            web_app::ExternalInstallSource::kExternalPolicy);
-    auto it = installed_apps.find(app_id);
-    if (it != installed_apps.end())
-      return it->second.spec();
+  // App Service is absent in some cases e.g. Arc++ Kiosk Mode.
+  if (apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
+    std::string policy_id;
+    apps::AppServiceProxyFactory::GetForProfile(profile)
+        ->AppRegistryCache()
+        .ForOneApp(app_id, [&policy_id](const apps::AppUpdate& update) {
+          policy_id = update.PolicyId();
+        });
+    if (!policy_id.empty())
+      return policy_id;
   }
 
   // Handle Arc++ ids
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
index e4a5da8..5adbc8b 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
@@ -250,25 +250,6 @@
       continue;
     }
 
-    // Handle Chrome App ids
-    if (crx_file::id_util::IdIsValid(*policy_entry)) {
-      if (*policy_entry == file_manager::kFileManagerAppId &&
-          chromeos::features::IsFileManagerSwaEnabled()) {
-        absl::optional<std::string> files_app_id =
-            web_app::GetAppIdForSystemWebApp(
-                helper->profile(), web_app::SystemAppType::FILE_MANAGER);
-        if (files_app_id) {
-          result.emplace_back(*files_app_id);
-        } else {
-          // Fall-back to the policy_entry if we cannot fetch one for Files app.
-          result.emplace_back(*policy_entry);
-        }
-      } else {
-        result.emplace_back(*policy_entry);
-      }
-      continue;
-    }
-
     // URLs provided through policy might not match exactly (eg. missing
     // trailing slash), so check the normalized version of valid URLs too.
     std::vector<std::string> policy_entries_to_check{*policy_entry};
@@ -281,6 +262,7 @@
     // Handle App Service policy IDs (currently Web Apps only)
     if (apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(
             helper->profile())) {
+      size_t initial_result_size = result.size();
       apps::AppServiceProxyFactory::GetForProfile(helper->profile())
           ->AppRegistryCache()
           .ForEachApp([&result, &policy_entries_to_check](
@@ -290,6 +272,15 @@
               result.emplace_back(update.AppId());
             }
           });
+      if (result.size() > initial_result_size) {
+        continue;
+      }
+    }
+
+    // Handle Chrome App ids
+    if (crx_file::id_util::IdIsValid(*policy_entry)) {
+      result.emplace_back(*policy_entry);
+      continue;
     }
 
     // Handle Arc++ App ids
diff --git a/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc
index ee7accd..fc7e54f 100644
--- a/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.cc
@@ -156,7 +156,11 @@
       result = VirtualCardEnrollmentBubbleResult::
           VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS;
       break;
-    default:
+    case PaymentsBubbleClosedReason::kCancelled:
+      result = VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED;
+      break;
+    case PaymentsBubbleClosedReason::kUnknown:
       NOTREACHED();
       result = VirtualCardEnrollmentBubbleResult::
           VIRTUAL_CARD_ENROLLMENT_BUBBLE_RESULT_UNKNOWN;
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils.cc b/chrome/browser/ui/bookmarks/bookmark_utils.cc
index 9dd0ce8f..7c41c782 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils.cc
@@ -40,6 +40,7 @@
 #if defined(TOOLKIT_VIEWS)
 #include "chrome/grit/theme_resources.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/gfx/canvas.h"
@@ -48,7 +49,6 @@
 #include "ui/gfx/image/image_skia_source.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/scoped_canvas.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/resources/grit/ui_resources.h"
 #endif
 
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 3284c6c..ace0c593 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1984,8 +1984,11 @@
   ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(
       protocol, url, GetProtocolHandlerSecurityLevel(requesting_frame));
 
-  if (!handler.IsValid())
-    return;
+  // The parameters's normalization process defined in the spec has been already
+  // applied in the WebContentImpl class, so at this point it shouldn't be
+  // possible to create an invalid handler.
+  // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
+  DCHECK(handler.IsValid());
 
   custom_handlers::ProtocolHandlerRegistry* registry =
       ProtocolHandlerRegistryFactory::GetForBrowserContext(context);
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index f9d291e..123291fe 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -811,7 +811,7 @@
     case IDC_SHOW_BETA_FORUM:
       ShowBetaForum(browser_);
       break;
-    case IDC_TOGGLE_COMMANDER:
+    case IDC_TOGGLE_QUICK_COMMANDS:
       ToggleCommander(browser_);
       break;
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -1119,7 +1119,7 @@
       IDC_SHOW_SAVE_LOCAL_CARD_SIGN_IN_PROMO_IF_APPLICABLE, true);
   command_updater_.UpdateCommandEnabled(IDC_CLOSE_SIGN_IN_PROMO, true);
   command_updater_.UpdateCommandEnabled(IDC_CARET_BROWSING_TOGGLE, true);
-  command_updater_.UpdateCommandEnabled(IDC_TOGGLE_COMMANDER,
+  command_updater_.UpdateCommandEnabled(IDC_TOGGLE_QUICK_COMMANDS,
                                         commander::IsEnabled());
   UpdateShowSyncState(true);
 
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 19e4df3..9220a35 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -472,7 +472,8 @@
   // Whether or not the shelf view is visible.
   virtual bool IsDownloadShelfVisible() const = 0;
 
-  // Returns the DownloadShelf.
+  // Returns the DownloadShelf. Returns null if download shelf is disabled. This
+  // can happen if the new download bubble UI is enabled.
   virtual DownloadShelf* GetDownloadShelf() = 0;
 
   // Shows the confirmation dialog box warning that the browser is closing with
diff --git a/chrome/browser/ui/cocoa/accelerators_cocoa.mm b/chrome/browser/ui/cocoa/accelerators_cocoa.mm
index 7f86487..809554c 100644
--- a/chrome/browser/ui/cocoa/accelerators_cocoa.mm
+++ b/chrome/browser/ui/cocoa/accelerators_cocoa.mm
@@ -138,7 +138,7 @@
 
   if (commander::IsEnabled()) {
     result = accelerators_.insert(
-        std::make_pair(IDC_TOGGLE_COMMANDER,
+        std::make_pair(IDC_TOGGLE_QUICK_COMMANDS,
                        ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN)));
     DCHECK(result.second);
   }
diff --git a/chrome/browser/ui/cocoa/main_menu_builder.mm b/chrome/browser/ui/cocoa/main_menu_builder.mm
index aff8f93a..a116531 100644
--- a/chrome/browser/ui/cocoa/main_menu_builder.mm
+++ b/chrome/browser/ui/cocoa/main_menu_builder.mm
@@ -286,8 +286,8 @@
                 Item(IDS_DISTILL_PAGE)
                     .command_id(IDC_DISTILL_PAGE)
                     .remove_if(!dom_distiller::IsDomDistillerEnabled()),
-                Item(IDS_TOGGLE_COMMANDER)
-                    .command_id(IDC_TOGGLE_COMMANDER)
+                Item(IDS_TOGGLE_QUICK_COMMANDS)
+                    .command_id(IDC_TOGGLE_QUICK_COMMANDS)
                     .remove_if(!commander::IsEnabled()),
 
                 Item().is_separator(),
diff --git a/chrome/browser/ui/cocoa/task_manager_mac.mm b/chrome/browser/ui/cocoa/task_manager_mac.mm
index 00c21be..0f55c08 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac.mm
@@ -33,7 +33,6 @@
 #include "ui/base/models/image_model.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_util_mac.h"
-#include "ui/views/image_model_utils.h"
 
 namespace {
 
@@ -715,8 +714,8 @@
 
 NSImage* TaskManagerMac::GetImageForRow(int row) {
   const NSSize kImageSize = NSMakeSize(16.0, 16.0);
-  NSImage* image = gfx::NSImageFromImageSkia(
-      views::GetImageSkiaFromImageModel(table_model_.GetIcon(row), nullptr));
+  NSImage* image =
+      gfx::NSImageFromImageSkia(table_model_.GetIcon(row).Rasterize(nullptr));
   if (image)
     image.size = kImageSize;
   else
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index cf2b8ef..d081979 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -173,6 +173,16 @@
   E_CPONLY(kColorPipWindowHangUpButtonForeground) \
   E_CPONLY(kColorPipWindowSkipAdButtonBackground) \
   E_CPONLY(kColorPipWindowSkipAdButtonBorder) \
+  /* PWA colors. */ \
+  E_CPONLY(kColorPwaBackground) \
+  E_CPONLY(kColorPwaMenuButtonIcon) \
+  E_CPONLY(kColorPwaSecurityChipForeground) \
+  E_CPONLY(kColorPwaSecurityChipForegroundDangerous) \
+  E_CPONLY(kColorPwaSecurityChipForegroundPolicyCert) \
+  E_CPONLY(kColorPwaSecurityChipForegroundSecure) \
+  E_CPONLY(kColorPwaTheme) \
+  E_CPONLY(kColorPwaToolbarBackground) \
+  E_CPONLY(kColorPwaToolbarForeground) \
   /* QR code colors. */ \
   E_CPONLY(kColorQrCodeBackground) \
   E_CPONLY(kColorQrCodeBorder) \
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index d9d86c06..c8705f7 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -90,7 +90,6 @@
 #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
 #include "third_party/blink/public/common/switches.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
-#include "ui/views/image_model_utils.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
@@ -504,8 +503,7 @@
       app_service_test().LoadAppIconBlocking(
           apps::mojom::AppType::kChromeApp, app_id_,
           extension_misc::EXTENSION_ICON_SMALL),
-      views::GetImageSkiaFromImageModel(
-          app_browser_->app_controller()->GetWindowAppIcon(), nullptr)));
+      app_browser_->app_controller()->GetWindowAppIcon().Rasterize(nullptr)));
 }
 #endif
 
diff --git a/chrome/browser/ui/intent_picker_tab_helper.cc b/chrome/browser/ui/intent_picker_tab_helper.cc
index 5bb52a2..70fb7296 100644
--- a/chrome/browser/ui/intent_picker_tab_helper.cc
+++ b/chrome/browser/ui/intent_picker_tab_helper.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -91,7 +92,7 @@
 
   tab_helper->should_show_icon_ = should_show_icon;
 
-  if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) {
+  if (apps::features::LinkCapturingUiUpdateEnabled()) {
     tab_helper->UpdateCollapsedState();
   }
 
diff --git a/chrome/browser/ui/startup/web_app_startup_utils.cc b/chrome/browser/ui/startup/web_app_startup_utils.cc
index 0570a70d..56be9f61 100644
--- a/chrome/browser/ui/startup/web_app_startup_utils.cc
+++ b/chrome/browser/ui/startup/web_app_startup_utils.cc
@@ -47,6 +47,7 @@
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
+#include "third_party/blink/public/common/security/protocol_handler_security_level.h"
 #include "url/gurl.h"
 
 namespace web_app {
@@ -172,11 +173,10 @@
       // has a wait for "on_registry_ready()", `potential_protocol` checks for
       // blink::IsValidCustomHandlerScheme() here to avoid loading the
       // WebAppProvider with a false positive.
-      bool unused_has_custom_scheme_prefix = false;
       if (potential_protocol.is_valid() &&
-          blink::IsValidCustomHandlerScheme(potential_protocol.scheme(),
-                                            /*allow_ext_prefix=*/false,
-                                            unused_has_custom_scheme_prefix)) {
+          blink::IsValidCustomHandlerScheme(
+              potential_protocol.scheme(),
+              blink::ProtocolHandlerSecurityLevel::kStrict)) {
         protocol_url = std::move(potential_protocol);
         break;
       }
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index b14b485a..0f0a8f0 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -238,7 +238,7 @@
   AddItemWithStringId(IDC_CREATE_SHORTCUT, IDS_ADD_TO_OS_LAUNCH_SURFACE);
   AddItemWithStringId(IDC_NAME_WINDOW, IDS_NAME_WINDOW);
   if (commander::IsEnabled())
-    AddItemWithStringId(IDC_TOGGLE_COMMANDER, IDS_TOGGLE_COMMANDER);
+    AddItemWithStringId(IDC_TOGGLE_QUICK_COMMANDS, IDS_TOGGLE_QUICK_COMMANDS);
 
   AddSeparator(ui::NORMAL_SEPARATOR);
   AddItemWithStringId(IDC_CLEAR_BROWSING_DATA, IDS_CLEAR_BROWSING_DATA);
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index 27564a1..6a99ee14 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -226,7 +226,7 @@
     // TODO(https://crbug.com/1016439): This is a temporary hotkey. Chrome OS
     // uses this for switching IMEs, but since this feature is only exposed via
     // command line flag at the moment, we'll exclude them entirely for now.
-    {ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, IDC_TOGGLE_COMMANDER},
+    {ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, IDC_TOGGLE_QUICK_COMMANDS},
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 #endif  // !BUILDFLAG(IS_MAC)
 #if BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index 9242d25..782cfa1 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -52,7 +52,6 @@
 #include "ui/views/controls/menu/menu_model_adapter.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/controls/webview/webview.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
@@ -183,8 +182,7 @@
     return ui::ImageModel();
 
   DCHECK(image.IsImage());
-  const gfx::ImageSkia image_skia =
-      views::GetImageSkiaFromImageModel(image, nullptr);
+  const gfx::ImageSkia image_skia = image.Rasterize(nullptr);
   return ui::ImageModel::FromImageSkia(
       apps::CreateStandardIconImage(image_skia));
 }
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc
index afcef475..1763373 100644
--- a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views_interactive_uitest.cc
@@ -156,6 +156,62 @@
     }
   }
 
+  void TestCloseBubbleForExpectedResultFromSource(
+      VirtualCardEnrollmentBubbleResult expected_result,
+      VirtualCardEnrollmentSource source) {
+    base::HistogramTester histogram_tester;
+    ShowBubbleAndWaitUntilShown(GetFieldsForSource(source), base::DoNothing(),
+                                base::DoNothing());
+
+    ASSERT_TRUE(GetBubbleViews());
+    ASSERT_TRUE(IsIconVisible());
+
+    views::Widget::ClosedReason closed_reason;
+    switch (expected_result) {
+      case VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_ACCEPTED:
+        closed_reason = views::Widget::ClosedReason::kAcceptButtonClicked;
+        break;
+      case VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_CLOSED:
+        closed_reason = views::Widget::ClosedReason::kCloseButtonClicked;
+        break;
+      case VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS:
+        closed_reason = views::Widget::ClosedReason::kLostFocus;
+        break;
+      case VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED:
+        closed_reason = views::Widget::ClosedReason::kCancelButtonClicked;
+        break;
+      case VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED:
+        // The VirtualCardEnrollBubble will be closed differently in the not
+        // interacted case, so no need to set |closed_reason| here.
+        break;
+      case VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_RESULT_UNKNOWN:
+        NOTREACHED();
+        break;
+    }
+
+    views::test::WidgetDestroyedWaiter destroyed_waiter(
+        GetBubbleViews()->GetWidget());
+
+    if (expected_result != VirtualCardEnrollmentBubbleResult::
+                               VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED) {
+      GetBubbleViews()->GetWidget()->CloseWithReason(closed_reason);
+    } else {
+      browser()->tab_strip_model()->CloseAllTabs();
+    }
+
+    destroyed_waiter.Wait();
+    histogram_tester.ExpectBucketCount(
+        "Autofill.VirtualCardEnrollBubble.Result." +
+            VirtualCardEnrollmentSourceToMetricSuffix(source) + ".FirstShow",
+        expected_result, 1);
+  }
+
  private:
   VirtualCardEnrollmentFields downstream_virtual_card_enrollment_fields_;
   VirtualCardEnrollmentFields upstream_virtual_card_enrollment_fields_;
@@ -195,119 +251,69 @@
   EXPECT_TRUE(IsIconVisible());
 }
 
-IN_PROC_BROWSER_TEST_F(VirtualCardEnrollBubbleViewsInteractiveUiTest,
-                       Metrics_BubbleLostFocus) {
-  base::HistogramTester histogram_tester;
-  std::string histogram_name;
-
-  for (VirtualCardEnrollmentSource virtual_card_enrollment_source :
-       {VirtualCardEnrollmentSource::kUpstream,
-        VirtualCardEnrollmentSource::kDownstream,
-        VirtualCardEnrollmentSource::kSettingsPage}) {
-    ShowBubbleAndWaitUntilShown(
-        GetFieldsForSource(virtual_card_enrollment_source), base::DoNothing(),
-        base::DoNothing());
-
-    ASSERT_TRUE(GetBubbleViews());
-    ASSERT_TRUE(IsIconVisible());
-
-    // Mock deactivation and lose focus.
-    views::test::WidgetDestroyedWaiter destroyed_waiter(
-        GetBubbleViews()->GetWidget());
-    GetBubbleViews()->GetWidget()->CloseWithReason(
-        views::Widget::ClosedReason::kLostFocus);
-    destroyed_waiter.Wait();
-
-    histogram_tester.ExpectBucketCount(
-        "Autofill.VirtualCardEnrollBubble.Result." +
-            VirtualCardEnrollmentSourceToMetricSuffix(
-                virtual_card_enrollment_source) +
-            ".FirstShow",
-        VirtualCardEnrollmentBubbleResult::
-            VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS,
-        1);
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(VirtualCardEnrollBubbleViewsInteractiveUiTest,
-                       Metrics_BubbleAccepted) {
-  base::HistogramTester histogram_tester;
-  std::string histogram_name;
-
-  for (VirtualCardEnrollmentSource virtual_card_enrollment_source :
-       {VirtualCardEnrollmentSource::kUpstream,
-        VirtualCardEnrollmentSource::kDownstream,
-        VirtualCardEnrollmentSource::kSettingsPage}) {
-    ShowBubbleAndWaitUntilShown(
-        GetFieldsForSource(virtual_card_enrollment_source), base::DoNothing(),
-        base::DoNothing());
-
-    ASSERT_TRUE(GetBubbleViews());
-    ASSERT_TRUE(IsIconVisible());
-
-    // Mock deactivation and accept.
-    views::test::WidgetDestroyedWaiter destroyed_waiter(
-        GetBubbleViews()->GetWidget());
-    GetBubbleViews()->GetWidget()->CloseWithReason(
-        views::Widget::ClosedReason::kAcceptButtonClicked);
-    destroyed_waiter.Wait();
-
-    histogram_tester.ExpectBucketCount(
-        "Autofill.VirtualCardEnrollBubble.Result." +
-            VirtualCardEnrollmentSourceToMetricSuffix(
-                virtual_card_enrollment_source) +
-            ".FirstShow",
-        VirtualCardEnrollmentBubbleResult::
-            VIRTUAL_CARD_ENROLLMENT_BUBBLE_ACCEPTED,
-        1);
-  }
-}
-
-class NotInteractedAndClosedMetricsTest
+class VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized
     : public VirtualCardEnrollBubbleViewsInteractiveUiTest,
       public testing::WithParamInterface<VirtualCardEnrollmentSource> {
  public:
-  NotInteractedAndClosedMetricsTest() = default;
-  ~NotInteractedAndClosedMetricsTest() override = default;
+  VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized() = default;
+  ~VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized() override =
+      default;
 };
 
 INSTANTIATE_TEST_SUITE_P(
     ,
-    NotInteractedAndClosedMetricsTest,
+    VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized,
     testing::Values(VirtualCardEnrollmentSource::kUpstream,
                     VirtualCardEnrollmentSource::kDownstream,
                     VirtualCardEnrollmentSource::kSettingsPage));
 
-IN_PROC_BROWSER_TEST_P(NotInteractedAndClosedMetricsTest,
-                       NotInteractedTest_AllSources) {
-  VirtualCardEnrollmentSource virtual_card_enrollment_source = GetParam();
-  base::HistogramTester histogram_tester;
-  std::string histogram_name;
-  ShowBubbleAndWaitUntilShown(
-      GetFieldsForSource(virtual_card_enrollment_source), base::DoNothing(),
-      base::DoNothing());
-
-  ASSERT_TRUE(GetBubbleViews());
-  ASSERT_TRUE(IsIconVisible());
-
-  // Mock browser being closed.
-  views::test::WidgetDestroyedWaiter destroyed_waiter(
-      GetBubbleViews()->GetWidget());
-  browser()->tab_strip_model()->CloseAllTabs();
-  destroyed_waiter.Wait();
-
-  histogram_tester.ExpectBucketCount(
-      "Autofill.VirtualCardEnrollBubble.Result." +
-          VirtualCardEnrollmentSourceToMetricSuffix(
-              virtual_card_enrollment_source) +
-          ".FirstShow",
+IN_PROC_BROWSER_TEST_P(
+    VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized,
+    Metrics_BubbleLostFocus) {
+  TestCloseBubbleForExpectedResultFromSource(
       VirtualCardEnrollmentBubbleResult::
-          VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED,
-      1);
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS,
+      GetParam());
 }
 
-IN_PROC_BROWSER_TEST_P(NotInteractedAndClosedMetricsTest,
-                       ShownAndLostFocusTest_AllSources) {
+IN_PROC_BROWSER_TEST_P(
+    VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized,
+    Metrics_BubbleAccepted) {
+  TestCloseBubbleForExpectedResultFromSource(
+      VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_ACCEPTED,
+      GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(
+    VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized,
+    Metrics_BubbleCancelled) {
+  TestCloseBubbleForExpectedResultFromSource(
+      VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED,
+      GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(
+    VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized,
+    Metrics_BubbleClosed) {
+  TestCloseBubbleForExpectedResultFromSource(
+      VirtualCardEnrollmentBubbleResult::VIRTUAL_CARD_ENROLLMENT_BUBBLE_CLOSED,
+      GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(
+    VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized,
+    Metrics_NotInteracted) {
+  TestCloseBubbleForExpectedResultFromSource(
+      VirtualCardEnrollmentBubbleResult::
+          VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED,
+      GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(
+    VirtualCardEnrollBubbleViewsInteractiveUiTestParameterized,
+    ShownAndLostFocusTest_AllSources) {
   base::HistogramTester histogram_tester;
   VirtualCardEnrollmentSource virtual_card_enrollment_source = GetParam();
   ShowBubbleAndWaitUntilShown(
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 5cda679..6c33509 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -128,7 +128,6 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/drag_utils.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/metrics.h"
 #include "ui/views/view_constants.h"
 #include "ui/views/widget/tooltip_manager.h"
@@ -1488,10 +1487,9 @@
         chrome::BookmarkFolderIconType::kNormal, ui::kColorMenuIcon);
   }
 
-  button_drag_utils::SetDragImage(
-      node->url(), node->GetTitle(),
-      views::GetImageSkiaFromImageModel(icon, GetColorProvider()), &press_pt,
-      data);
+  button_drag_utils::SetDragImage(node->url(), node->GetTitle(),
+                                  icon.Rasterize(GetColorProvider()), &press_pt,
+                                  data);
   WriteBookmarkDragData(node, data);
 }
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
index 22bf2e2..2b97a31 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.cc
@@ -40,7 +40,6 @@
 #include "ui/gfx/render_text.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/views/drag_utils.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/views/style/typography.h"
 #include "ui/views/style/typography_provider.h"
@@ -241,7 +240,7 @@
               (icon.IsEmpty() || (icon.IsImageGenerator() && !color_provider))
                   ? *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
                         IDR_DEFAULT_FAVICON)
-                  : views::GetImageSkiaFromImageModel(icon, color_provider),
+                  : icon.Rasterize(color_provider),
               count_),
           BookmarkDragImageSource::kBookmarkDragImageSize);
 
diff --git a/chrome/browser/ui/views/chrome_typography.cc b/chrome/browser/ui/views/chrome_typography.cc
index 3315559..2b5c02c 100644
--- a/chrome/browser/ui/views/chrome_typography.cc
+++ b/chrome/browser/ui/views/chrome_typography.cc
@@ -107,6 +107,7 @@
       details.size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(14);
       break;
     case CONTEXT_IPH_BUBBLE_BUTTON:
+    case CONTEXT_SIDE_PANEL_TITLE:
       details.size_delta = GetFontSizeDeltaIgnoringUserOrLocaleSettings(13);
       break;
   }
diff --git a/chrome/browser/ui/views/chrome_typography.h b/chrome/browser/ui/views/chrome_typography.h
index 6093df4..b5036de 100644
--- a/chrome/browser/ui/views/chrome_typography.h
+++ b/chrome/browser/ui/views/chrome_typography.h
@@ -64,7 +64,10 @@
   CONTEXT_IPH_BUBBLE_BODY,
 
   // Button label in the IPH bubble. Usually 13pt.
-  CONTEXT_IPH_BUBBLE_BUTTON
+  CONTEXT_IPH_BUBBLE_BUTTON,
+
+  // Title label in the browser side panel. Usually 13pt.
+  CONTEXT_SIDE_PANEL_TITLE,
 };
 
 enum ChromeTextStyle {
diff --git a/chrome/browser/ui/views/commander_frontend_views.cc b/chrome/browser/ui/views/commander_frontend_views.cc
index 074b8d5..c1fdf42 100644
--- a/chrome/browser/ui/views/commander_frontend_views.cc
+++ b/chrome/browser/ui/views/commander_frontend_views.cc
@@ -156,7 +156,7 @@
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.delegate = widget_delegate_.get();
-  params.name = "Commander";
+  params.name = "Quick Commands";
   params.parent = parent->GetWidget()->GetNativeView();
 // On Windows, this defaults to DesktopNativeWidgetAura, which has incorrect
 // parenting behavior for this widget.
@@ -287,8 +287,8 @@
   web_view->set_allow_accelerators(true);
   // Make the commander WebContents show up in the task manager.
   content::WebContents* web_contents = web_view->GetWebContents();
-  task_manager::WebContentsTags::CreateForToolContents(web_contents,
-                                                       IDS_COMMANDER_LABEL);
+  task_manager::WebContentsTags::CreateForToolContents(
+      web_contents, IDS_QUICK_COMMANDS_LABEL);
   web_view->LoadInitialURL(GURL(chrome::kChromeUICommanderURL));
   return web_view;
 }
diff --git a/chrome/browser/ui/views/commander_frontend_views_browsertest.cc b/chrome/browser/ui/views/commander_frontend_views_browsertest.cc
index 25ca754..ce979f3 100644
--- a/chrome/browser/ui/views/commander_frontend_views_browsertest.cc
+++ b/chrome/browser/ui/views/commander_frontend_views_browsertest.cc
@@ -105,7 +105,7 @@
   }
 
   void OnWidgetShown(views::Widget* widget) {
-    if (widget->GetName() == "Commander") {
+    if (widget->GetName() == "Quick Commands") {
       active_widget_ = widget;
       if (IsWidgetAttachedToBrowser(expected_browser_)) {
         expected_browser_ = nullptr;
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_controller.cc b/chrome/browser/ui/views/download/bubble/download_bubble_controller.cc
deleted file mode 100644
index 09ad1ef..0000000
--- a/chrome/browser/ui/views/download/bubble/download_bubble_controller.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/download/bubble/download_bubble_controller.h"
-#include "base/time/time.h"
-#include "chrome/browser/download/download_item_model.h"
-#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
-#include "chrome/browser/ui/views/download/bubble/download_bubble_row_view.h"
-#include "components/download/public/common/download_item.h"
-#include "content/public/browser/download_manager.h"
-
-#include "base/files/file_path.h"
-
-namespace {
-constexpr int kShowDownloadsInBubbleForNumDays = 1;
-}  // namespace
-
-DownloadBubbleUIController::DownloadBubbleUIController(
-    content::DownloadManager* manager)
-    : download_manager_(manager) {}
-
-void DownloadBubbleUIController::OnManagerGoingDown(
-    content::DownloadManager* manager) {
-  if (manager == download_manager_) {
-    download_manager_ = nullptr;
-  }
-}
-
-// TODO(bhatiarohit): Check the order of downloads here. They do not
-// appear chronological sometimes.
-// TODO(bhatiarohit): Include OfflineItems.
-std::unique_ptr<DownloadBubbleRowListView>
-DownloadBubbleUIController::GetMainView() {
-  auto row_list_view = std::make_unique<DownloadBubbleRowListView>();
-  if (!download_manager_)
-    return row_list_view;
-  std::vector<download::DownloadItem*> download_items;
-  download_manager_->GetAllDownloads(&download_items);
-  for (download::DownloadItem* item : download_items) {
-    base::Time end_time = item->GetEndTime();
-    if (end_time.is_null() || ((base::Time::Now() - end_time) <=
-                               base::Days(kShowDownloadsInBubbleForNumDays))) {
-      row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>(
-          DownloadItemModel::Wrap(
-              item,
-              std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()),
-          row_list_view.get()));
-    }
-  }
-  return row_list_view;
-}
-
-// TODO(bhatiarohit): Refine this to remove actioned-upon items, and
-// items that have been displayed on the main view.
-std::unique_ptr<DownloadBubbleRowListView>
-DownloadBubbleUIController::GetPartialView() {
-  auto row_list_view = std::make_unique<DownloadBubbleRowListView>();
-  if (!download_manager_)
-    return row_list_view;
-  std::vector<download::DownloadItem*> download_items;
-  download_manager_->GetAllDownloads(&download_items);
-  for (download::DownloadItem* item : download_items) {
-    base::Time end_time = item->GetEndTime();
-    if (end_time.is_null() || ((base::Time::Now() - end_time) <=
-                               base::Days(kShowDownloadsInBubbleForNumDays))) {
-      row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>(
-          DownloadItemModel::Wrap(
-              item,
-              std::make_unique<DownloadUIModel::BubbleStatusTextBuilder>()),
-          row_list_view.get()));
-    }
-  }
-  return row_list_view;
-}
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_controller.h b/chrome/browser/ui/views/download/bubble/download_bubble_controller.h
deleted file mode 100644
index e1c3710..0000000
--- a/chrome/browser/ui/views/download/bubble/download_bubble_controller.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
-#define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
-
-#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
-#include "components/download/content/public/all_download_item_notifier.h"
-#include "content/public/browser/download_manager.h"
-
-class DownloadBubbleUIController
-    : public download::AllDownloadItemNotifier::Observer {
- public:
-  explicit DownloadBubbleUIController(content::DownloadManager* manager);
-  DownloadBubbleUIController(const DownloadBubbleUIController&) = delete;
-  DownloadBubbleUIController& operator=(const DownloadBubbleUIController&) =
-      delete;
-
-  // AllDownloadItemNotifier::Observer
-  void OnManagerGoingDown(content::DownloadManager* manager) override;
-
-  // Get the main view of the Download Bubble. The main view contains all the
-  // recent downloads (finished within the last 24 hours).
-  std::unique_ptr<DownloadBubbleRowListView> GetMainView();
-
-  // Get the partial view of the Download Bubble. The partial view contains
-  // in-progress and uninteracted downloads, meant to capture the user's
-  // recent tasks. This can only be opened by the browser in the event of new
-  // downloads, and user action only creates a main view.
-  std::unique_ptr<DownloadBubbleRowListView> GetPartialView();
-
- private:
-  content::DownloadManager* download_manager_;
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc
index 7afe0b10..05a6f5df 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.cc
@@ -6,6 +6,7 @@
 
 #include "base/files/file_path.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/bubble/download_bubble_controller.h"
 #include "chrome/browser/download/download_ui_model.h"
 #include "chrome/browser/icon_manager.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -100,11 +101,13 @@
 
 DownloadBubbleRowView::DownloadBubbleRowView(
     DownloadUIModel::DownloadUIModelPtr model,
-    DownloadBubbleRowListView* row_list_view)
+    DownloadBubbleRowListView* row_list_view,
+    DownloadBubbleUIController* bubble_controller)
     : model_(std::move(model)),
       context_menu_(
           std::make_unique<DownloadShelfContextMenuView>(model_->GetWeakPtr())),
-      row_list_view_(row_list_view) {
+      row_list_view_(row_list_view),
+      bubble_controller_(bubble_controller) {
   model_->AddObserver(this);
   set_context_menu_controller(this);
 
@@ -169,6 +172,7 @@
 }
 
 void DownloadBubbleRowView::OnCancelButtonPressed() {
+  bubble_controller_->RemoveContentIdFromPartialView(model_->GetContentId());
   model_->Cancel(/*user_cancel=*/true);
 }
 
@@ -189,10 +193,15 @@
   // PreferredSizeChanged();
 }
 
-// TODO(bhatiarohit): Use these methods to update main and partial view.
-void DownloadBubbleRowView::OnDownloadOpened() {}
+void DownloadBubbleRowView::OnDownloadOpened() {
+  bubble_controller_->RemoveContentIdFromPartialView(model_->GetContentId());
+}
 
-void DownloadBubbleRowView::OnDownloadDestroyed() {}
+void DownloadBubbleRowView::OnDownloadDestroyed() {
+  // This will return ownership and destroy this object at the end of the
+  // method.
+  auto row_view_ptr = row_list_view_->RemoveChildViewT(this);
+}
 
 void DownloadBubbleRowView::ShowContextMenuForViewImpl(
     View* source,
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h
index 522b35db..9575c44 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_row_view.h
@@ -21,6 +21,7 @@
 }  // namespace views
 
 class DownloadShelfContextMenuView;
+class DownloadBubbleUIController;
 
 class DownloadBubbleRowView : public views::View,
                               public views::ContextMenuController,
@@ -29,7 +30,8 @@
   METADATA_HEADER(DownloadBubbleRowView);
 
   explicit DownloadBubbleRowView(DownloadUIModel::DownloadUIModelPtr model,
-                                 DownloadBubbleRowListView* row_list_view);
+                                 DownloadBubbleRowListView* row_list_view,
+                                 DownloadBubbleUIController* bubble_controller);
   DownloadBubbleRowView(const DownloadBubbleRowView&) = delete;
   DownloadBubbleRowView& operator=(const DownloadBubbleRowView&) = delete;
   ~DownloadBubbleRowView() override;
@@ -37,8 +39,8 @@
   void AddedToWidget() override;
 
   // Overrides DownloadUIModel::Observer:
-  void OnDownloadUpdated() override;
   void OnDownloadOpened() override;
+  void OnDownloadUpdated() override;
   void OnDownloadDestroyed() override;
 
   // Overrides views::ContextMenuController:
@@ -92,6 +94,9 @@
   // Parent row list view.
   raw_ptr<DownloadBubbleRowListView> row_list_view_ = nullptr;
 
+  // Controller for keeping track of downloads.
+  raw_ptr<DownloadBubbleUIController> bubble_controller_ = nullptr;
+
   base::WeakPtrFactory<DownloadBubbleRowView> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
index fca95bb..ac625f6 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/download/bubble/download_bubble_controller.h"
 #include "chrome/browser/download/bubble/download_display_controller.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
@@ -13,7 +14,8 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/download/bubble/download_bubble_controller.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_row_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_dialog_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/grit/generated_resources.h"
@@ -32,6 +34,17 @@
 constexpr int kProgressRingRadius = 9;
 constexpr float kProgressRingStrokeWidth = 1.7f;
 
+std::unique_ptr<DownloadBubbleRowListView> CreateRowListView(
+    std::vector<DownloadUIModelPtr> model_list,
+    DownloadBubbleUIController* bubble_controller) {
+  auto row_list_view = std::make_unique<DownloadBubbleRowListView>();
+  for (DownloadUIModelPtr& model : model_list) {
+    row_list_view->AddChildView(std::make_unique<DownloadBubbleRowView>(
+        std::move(model), row_list_view.get(), bubble_controller));
+  }
+  return row_list_view;
+}
+
 }  // namespace
 
 DownloadToolbarButtonView::DownloadToolbarButtonView(BrowserView* browser_view)
@@ -45,13 +58,13 @@
   GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kDialog);
   SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_DOWNLOAD_ICON));
   Profile* profile = browser_->profile();
-  content::DownloadManager* manager = profile->GetDownloadManager();
   SetVisible(false);
 
-  bubble_controller_ = std::make_unique<DownloadBubbleUIController>(manager);
+  bubble_controller_ = std::make_unique<DownloadBubbleUIController>(profile);
   // Wait until we're done with everything else before creating `controller_`
   // since it can call `Show()` synchronously.
-  controller_ = std::make_unique<DownloadDisplayController>(this, manager);
+  controller_ = std::make_unique<DownloadDisplayController>(
+      this, profile, bubble_controller_.get());
 }
 
 DownloadToolbarButtonView::~DownloadToolbarButtonView() {
@@ -112,7 +125,8 @@
 void DownloadToolbarButtonView::ShowDetails() {
   if (!bubble_delegate_) {
     std::unique_ptr<views::BubbleDialogDelegate> bubble_delegate =
-        CreateBubbleDialogDelegate(bubble_controller_->GetPartialView());
+        CreateBubbleDialogDelegate(CreateRowListView(
+            bubble_controller_->GetPartialView(), bubble_controller_.get()));
     bubble_delegate_ = bubble_delegate.get();
     views::BubbleDialogDelegate::CreateBubble(std::move(bubble_delegate));
     bubble_delegate_->GetWidget()->Show();
@@ -183,7 +197,8 @@
   if (!bubble_delegate_) {
     std::unique_ptr<views::BubbleDialogDelegate> bubble_delegate =
         CreateBubbleDialogDelegate(std::make_unique<DownloadDialogView>(
-            browser_, bubble_controller_->GetMainView()));
+            browser_, CreateRowListView(bubble_controller_->GetMainView(),
+                                        bubble_controller_.get())));
     bubble_delegate_ = bubble_delegate.get();
     views::BubbleDialogDelegate::CreateBubble(std::move(bubble_delegate));
     bubble_delegate_->GetWidget()->Show();
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
index 7647968..5bbd238 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
@@ -8,7 +8,6 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/download/bubble/download_display.h"
 #include "chrome/browser/download/bubble/download_icon_state.h"
-#include "chrome/browser/ui/views/download/bubble/download_bubble_controller.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
@@ -16,6 +15,7 @@
 class Browser;
 class BrowserView;
 class DownloadDisplayController;
+class DownloadBubbleUIController;
 
 // Download icon shown in the trusted area of the toolbar. Its lifetime is tied
 // to that of its parent ToolbarView. The icon is made visible when downloads
@@ -51,11 +51,13 @@
   void OnBubbleDelegateDeleted();
 
   raw_ptr<Browser> browser_;
-  // Controller for the DownloadToolbarButton.
+  // Controller for the DownloadToolbarButton UI.
   std::unique_ptr<DownloadDisplayController> controller_;
-  // Controller for the DownloadBubbleUI, both main view and partial view.
+  // Controller for keeping track of items for both main view and partial view.
   std::unique_ptr<DownloadBubbleUIController> bubble_controller_;
   raw_ptr<views::BubbleDialogDelegate> bubble_delegate_ = nullptr;
+
+  base::WeakPtrFactory<DownloadToolbarButtonView> weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_TOOLBAR_BUTTON_VIEW_H_
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index 0144f27d..118f311 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -66,6 +66,7 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/text/bytes_formatting.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/color/color_id.h"
 #include "ui/compositor/layer.h"
@@ -84,7 +85,6 @@
 #include "ui/gfx/range/range.h"
 #include "ui/gfx/text_constants.h"
 #include "ui/gfx/text_elider.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/animation/ink_drop_host_view.h"
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
index e0b4d05..c7c9131 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
@@ -37,7 +37,6 @@
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/link.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/layout/box_layout.h"
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -222,7 +221,7 @@
   // Indent by the size of the icon.
   layout->set_inside_border_insets(gfx::Insets(
       0,
-      views::GetImageSkiaFromImageModel(GetWindowIcon(), nullptr).width() +
+      GetWindowIcon().Size().width() +
           provider->GetDistanceMetric(DISTANCE_UNRELATED_CONTROL_HORIZONTAL),
       0, 0));
   layout->set_cross_axis_alignment(
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
index 3bad726..e87118a 100644
--- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/url_formatter/url_formatter.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/extension_urls.h"
@@ -184,22 +185,22 @@
   parent_view->ReorderChildView(site_access_view, new_index);
 }
 
-// Returns the active webcontent's host. This method should only be called when
+// Returns the web content's host. This method should only be called when
 // web contents are present.
-std::u16string GetCurrentSite(Browser* browser) {
-  auto* web_contents = browser->tab_strip_model()->GetActiveWebContents();
+std::u16string GetCurrentHost(content::WebContents* web_contents) {
   DCHECK(web_contents);
-  return base::UTF8ToUTF16(web_contents->GetLastCommittedURL().host());
+  return url_formatter::IDNToUnicode(
+      url_formatter::StripWWW(web_contents->GetLastCommittedURL().host()));
 }
 
-// Sets the `label` text to `message_id` with `current_site` emphasized.
+// Sets the `label` text to `message_id` with `current_host` emphasized.
 void SetLabelTextAndStyle(views::Label& label,
                           int message_id,
-                          std::u16string current_site) {
+                          std::u16string current_host) {
   size_t offset = 0u;
-  label.SetText(l10n_util::GetStringFUTF16(message_id, current_site, &offset));
+  label.SetText(l10n_util::GetStringFUTF16(message_id, current_host, &offset));
   label.SetTextStyleRange(ChromeTextStyle::STYLE_EMPHASIZED,
-                          gfx::Range(offset, offset + current_site.length()));
+                          gfx::Range(offset, offset + current_host.length()));
 }
 
 }  // namespace
@@ -481,7 +482,11 @@
 }
 
 void ExtensionsTabbedMenuView::CreateSiteAccessTab() {
-  auto current_site = GetCurrentSite(browser_);
+  auto* web_contents = browser_->tab_strip_model()->GetActiveWebContents();
+  if (!web_contents)
+    return;
+
+  auto current_host = GetCurrentHost(web_contents);
   const int horizontal_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_BUTTON_HORIZONTAL_PADDING);
   const int vertical_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric(
@@ -539,10 +544,10 @@
           .Build();
 
   const auto create_radio_button_builder =
-      [this, current_site](UserSiteSetting site_settings, int label_id) {
+      [this, current_host](UserSiteSetting site_settings, int label_id) {
         auto label = ((site_settings == UserSiteSetting::kGrantAllExtensions) ||
                       (site_settings == UserSiteSetting::kBlockAllExtensions))
-                         ? l10n_util::GetStringFUTF16(label_id, current_site)
+                         ? l10n_util::GetStringFUTF16(label_id, current_host)
                          : l10n_util::GetStringUTF16(label_id);
         return views::Builder<views::RadioButton>(
                    std::make_unique<views::RadioButton>(label, kGroupId))
@@ -699,18 +704,19 @@
   // when there are no active web contents (e.g tab strip update is closing its
   // tabs).
   // TODO(emiliapaz): Consider adding a message instead of hiding the views.
-  if (!browser_->tab_strip_model()->GetActiveWebContents()) {
+  auto* web_contents = browser_->tab_strip_model()->GetActiveWebContents();
+  if (!web_contents) {
     has_access_.container->SetVisible(false);
     requests_access_.container->SetVisible(false);
     site_access_message_->SetVisible(false);
     return;
   }
 
-  auto current_site = GetCurrentSite(browser_);
+  auto current_host = GetCurrentHost(web_contents);
 
-  auto update_section = [current_site](SiteAccessSection* section) {
+  auto update_section = [current_host](SiteAccessSection* section) {
     SetLabelTextAndStyle(*section->header, section->header_string_id,
-                         current_site);
+                         current_host);
     bool should_be_visible = !section->items->children().empty();
     if (section->container->GetVisible() != should_be_visible)
       section->container->SetVisible(should_be_visible);
@@ -726,7 +732,7 @@
     SetLabelTextAndStyle(
         *site_access_message_,
         IDS_EXTENSIONS_MENU_SITE_ACCESS_TAB_NO_EXTENSIONS_HAVE_ACCESS_TEXT,
-        current_site);
+        current_host);
   } else {
     site_access_message_->SetVisible(false);
   }
diff --git a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
index 7309977..0cb4cff1 100644
--- a/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_tabbed_menu_view_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/notification_types.h"
@@ -701,7 +702,7 @@
 
   auto no_extensions_have_access_text = l10n_util::GetStringFUTF16(
       IDS_EXTENSIONS_MENU_SITE_ACCESS_TAB_NO_EXTENSIONS_HAVE_ACCESS_TEXT,
-      base::UTF8ToUTF16(url.host()));
+      url_formatter::IDNToUnicode(url_formatter::StripWWW(url.host())));
 
   // Verify only the correct message is displayed when no extensions have access
   // to the current site.
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 09d827f..7c4292631 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/download/bubble/download_bubble_prefs.h"
 #include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
@@ -795,10 +796,7 @@
   }
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  if ((base::FeatureList::IsEnabled(lens::features::kLensStandalone) &&
-       lens::features::kEnableSidePanelForLensImageSearch.Get()) ||
-      (base::FeatureList::IsEnabled(lens::features::kLensRegionSearch) &&
-       lens::features::kEnableSidePanelForLensRegionSearch.Get())) {
+  if (lens::features::IsLensSidePanelEnabled()) {
     lens_side_panel_ = AddChildView(std::make_unique<SidePanel>(this));
     // If the separator was not already created, create one.
     if (!right_aligned_side_panel_separator_)
@@ -2347,6 +2345,13 @@
 }
 
 DownloadShelf* BrowserView::GetDownloadShelf() {
+  // Don't show download shelf if download bubble is enabled, except that the
+  // shelf is already showing (this can happen if prefs were changed at
+  // runtime).
+  if (download::IsDownloadBubbleEnabled(browser_->profile()) &&
+      !download_shelf_) {
+    return nullptr;
+  }
   if (!download_shelf_) {
     if (base::FeatureList::IsEnabled(features::kWebUIDownloadShelf)) {
       download_shelf_ = AddChildView(
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index 8bb97b2..8eb74dd 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -45,7 +45,6 @@
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/strings/grit/ui_strings.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/win/hwnd_util.h"
 #include "ui/views/window/client_view.h"
 
@@ -795,8 +794,8 @@
     HICON small_icon = nullptr;
     HICON big_icon = nullptr;
 
-    gfx::ImageSkia icon = views::GetImageSkiaFromImageModel(
-        browser_view()->GetWindowIcon(), GetColorProvider());
+    gfx::ImageSkia icon =
+        browser_view()->GetWindowIcon().Rasterize(GetColorProvider());
 
     if (!icon.isNull()) {
       // Keep previous icons alive as long as they are referenced by the HWND.
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index 72c3695..5384b82d 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -20,11 +20,11 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
+#include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/services/app_service/public/cpp/intent_constants.h"
+#include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/navigation_handle.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -82,11 +82,8 @@
         provider->GetDistanceMetric(DISTANCE_CONTENT_LIST_VERTICAL_MULTI),
         provider->GetInsetsMetric(views::INSETS_DIALOG).left())));
     views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
-    views::InkDrop::Get(this)->SetVisibleOpacity(kToolbarInkDropVisibleOpacity);
-    views::InkDrop::Get(this)->SetHighlightOpacity(
-        kToolbarInkDropHighlightVisibleOpacity);
     views::InkDrop::Get(this)->SetBaseColorCallback(
-        base::BindRepeating(&GetToolbarInkDropBaseColor, this));
+        base::BindRepeating(&HoverButton::GetInkDropColor, this));
   }
   IntentPickerLabelButton(const IntentPickerLabelButton&) = delete;
   IntentPickerLabelButton& operator=(const IntentPickerLabelButton&) = delete;
@@ -203,7 +200,7 @@
 }
 
 void IntentPickerBubbleView::OnDialogCancelled() {
-  const char* launch_name = apps::kUseBrowserForLink;
+  const char* launch_name = apps_util::kUseBrowserForLink;
   bool should_persist = remember_selection_checkbox_ &&
                         remember_selection_checkbox_->GetChecked();
   RunCallbackAndCloseBubble(launch_name, apps::PickerEntryType::kUnknown,
diff --git a/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc b/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc
index 61d3b27d..6591632 100644
--- a/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc
+++ b/chrome/browser/ui/views/lens/lens_region_search_controller_unittest.cc
@@ -19,7 +19,7 @@
  public:
   void SetUp() override {
     base::test::ScopedFeatureList features;
-    features.InitWithFeatures({features::kLensRegionSearch}, {});
+    features.InitWithFeatures({features::kLensStandalone}, {});
     TestWithBrowserView::SetUp();
 
     // Create an active web contents.
diff --git a/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc b/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc
index d46e5f8..3792122f 100644
--- a/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc
+++ b/chrome/browser/ui/views/lens/lens_side_panel_controller_unittest.cc
@@ -30,8 +30,7 @@
   void SetUp() override {
     base::test::ScopedFeatureList features;
     features.InitWithFeaturesAndParameters(
-        {{features::kLensRegionSearch,
-          {{"region-search-enable-side-panel", "true"}}},
+        {{features::kLensStandalone, {{"enable-side-panel", "true"}}},
          {::features::kSidePanel, {}},
          {reading_list::switches::kReadLater, {}}},
         {});
@@ -116,15 +115,27 @@
       content::OpenURLParams(GURL("http://foo.com"), content::Referrer(),
                              WindowOpenDisposition::NEW_FOREGROUND_TAB,
                              ui::PAGE_TRANSITION_LINK, false));
+  EXPECT_TRUE(browser_view()->lens_side_panel_controller());
+  EXPECT_TRUE(browser_view()->lens_side_panel()->GetVisible());
+
+  // Closing the controller should hide side panel and delete controller
+  // pointer.
   controller_->Close();
-  // Verify pointer was reset.
   EXPECT_FALSE(browser_view()->lens_side_panel_controller());
-  // Lens side panel controller needs to be recreated before reopen.
+  EXPECT_FALSE(browser_view()->lens_side_panel()->GetVisible());
+
+  // Creating a new controller in browser view should fix pointer, but side
+  // panel is still not visible.
   browser_view()->CreateLensSidePanelController();
+  controller_ = browser_view()->lens_side_panel_controller();
+  EXPECT_TRUE(browser_view()->lens_side_panel_controller());
+  EXPECT_FALSE(browser_view()->lens_side_panel()->GetVisible());
+
   controller_->OpenWithURL(
       content::OpenURLParams(GURL("http://bar.com"), content::Referrer(),
                              WindowOpenDisposition::NEW_FOREGROUND_TAB,
                              ui::PAGE_TRANSITION_LINK, false));
+  EXPECT_TRUE(browser_view()->lens_side_panel_controller());
   EXPECT_TRUE(browser_view()->lens_side_panel()->GetVisible());
   controller_->Close();
 
@@ -172,6 +183,7 @@
   // Creating a new controller in browser view should fix pointer, but side
   // panel is still not visible.
   browser_view()->CreateLensSidePanelController();
+  controller_ = browser_view()->lens_side_panel_controller();
   EXPECT_TRUE(browser_view()->lens_side_panel_controller());
   EXPECT_FALSE(browser_view()->lens_side_panel()->GetVisible());
 
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
index 168edc31..0d45628 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
 #include "chrome/browser/ui/page_info/page_info_dialog.h"
@@ -88,13 +89,13 @@
     security_state::SecurityLevel security_level) {
   switch (security_level) {
     case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
-      return ui::kColorPwaSecurityChipForegroundPolicyCert;
+      return kColorPwaSecurityChipForegroundPolicyCert;
     case security_state::SECURE:
-      return ui::kColorPwaSecurityChipForegroundSecure;
+      return kColorPwaSecurityChipForegroundSecure;
     case security_state::DANGEROUS:
-      return ui::kColorPwaSecurityChipForegroundDangerous;
+      return kColorPwaSecurityChipForegroundDangerous;
     default:
-      return ui::kColorPwaSecurityChipForeground;
+      return kColorPwaSecurityChipForeground;
   }
 }
 
@@ -326,24 +327,22 @@
 
 void CustomTabBarView::OnThemeChanged() {
   views::AccessiblePaneView::OnThemeChanged();
-  absl::optional<SkColor> optional_theme_color = GetThemeColor();
-
-  title_bar_color_ = optional_theme_color.value_or(GetDefaultFrameColor());
-
   const auto* color_provider = GetColorProvider();
+
+  title_bar_color_ = color_provider->GetColor(kColorPwaTheme);
   const SkColor foreground_color =
-      color_provider->GetColor(ui::kColorPwaToolbarForeground);
+      color_provider->GetColor(kColorPwaToolbarForeground);
   SetImageFromVectorIconWithColor(
       close_button_, vector_icons::kCloseRoundedIcon,
       GetLayoutConstant(LOCATION_BAR_ICON_SIZE), foreground_color);
 
-  background_color_ = color_provider->GetColor(ui::kColorPwaToolbarBackground);
+  background_color_ = color_provider->GetColor(kColorPwaToolbarBackground);
   SetBackground(views::CreateSolidBackground(background_color_));
 
   title_origin_view_->SetColors(background_color_);
   if (web_app_menu_button_) {
-    web_app_menu_button_->SetColor(GetThemeProvider()->GetColor(
-        ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON));
+    web_app_menu_button_->SetColor(
+        color_provider->GetColor(kColorPwaMenuButtonIcon));
   }
 }
 
@@ -403,7 +402,7 @@
 }
 
 SkColor CustomTabBarView::GetIconLabelBubbleBackgroundColor() const {
-  return GetColorProvider()->GetColor(ui::kColorPwaToolbarBackground);
+  return GetColorProvider()->GetColor(kColorPwaToolbarBackground);
 }
 
 content::WebContents* CustomTabBarView::GetWebContents() {
@@ -451,20 +450,6 @@
   return title_origin_view_ && title_origin_view_->IsShowingOriginForTesting();
 }
 
-// TODO(tluk): Remove the use of GetDefaultFrameColor() completely here. When
-// drawing the separator the current frame color should be queried directly and
-// not assume knowledge of what the color might be.
-SkColor CustomTabBarView::GetDefaultFrameColor() const {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Ash system frames differ from ChromeOS browser frames.
-  return chromeos::kDefaultFrameColor;
-#else
-  return ThemeProperties::GetDefaultColor(
-      ThemeProperties::COLOR_FRAME_ACTIVE, false,
-      GetNativeTheme()->ShouldUseDarkColors());
-#endif
-}
-
 void CustomTabBarView::GoBackToApp() {
   content::WebContents* web_contents = GetWebContents();
   content::NavigationController& controller = web_contents->GetController();
@@ -536,20 +521,10 @@
       views::MenuAnchorPosition::kTopLeft, source_type);
 }
 
-absl::optional<SkColor> CustomTabBarView::GetThemeColor() const {
-  web_app::AppBrowserController* application_controller = app_controller();
-  return application_controller ? application_controller->GetThemeColor()
-                                : absl::nullopt;
-}
-
 bool CustomTabBarView::GetShowTitle() const {
   return app_controller() != nullptr;
 }
 
 BEGIN_METADATA(CustomTabBarView, views::AccessiblePaneView)
-ADD_READONLY_PROPERTY_METADATA(SkColor,
-                               DefaultFrameColor,
-                               ui::metadata::SkColorConverter)
-ADD_READONLY_PROPERTY_METADATA(absl::optional<SkColor>, ThemeColor)
 ADD_READONLY_PROPERTY_METADATA(bool, ShowTitle)
 END_METADATA
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
index 359e5226..18d4fab 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
@@ -92,9 +92,6 @@
   bool IsShowingOriginForTesting() const;
 
  private:
-  // Calculate the view's frame color from the current theme provider.
-  SkColor GetDefaultFrameColor() const;
-
   // Takes the web contents for the custom tab bar back to the app scope.
   void GoBackToApp();
 
@@ -116,9 +113,6 @@
     return browser_->app_controller();
   }
 
-  // Convenience method to return the theme color from |app_controller_|.
-  absl::optional<SkColor> GetThemeColor() const;
-
   // Populates child elements with page details from the current WebContents.
   void UpdateContents();
 
diff --git a/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc b/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc
index 86d7952..5128993 100644
--- a/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/intent_chip_button_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -13,7 +14,6 @@
 #include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
-#include "chrome/common/chrome_features.h"
 #include "content/public/test/browser_test.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/test/button_test_api.h"
@@ -28,7 +28,10 @@
     : public web_app::WebAppNavigationBrowserTest {
  public:
   IntentChipButtonBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(features::kLinkCapturingUiUpdate);
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{apps::features::kLinkCapturingUiUpdate,
+                              apps::features::kIntentChipSkipsPicker},
+        /*disabled_features=*/{});
   }
 
   void OpenNewTab(const GURL& url) {
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index b966ff4..38d81a9 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -20,6 +20,7 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/accuracy_tips/accuracy_service_factory.h"
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/command_updater.h"
@@ -279,7 +280,7 @@
   selected_keyword_view_ = AddChildView(std::make_unique<SelectedKeywordView>(
       this, TemplateURLServiceFactory::GetForProfile(profile_), font_list));
 
-  if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) {
+  if (apps::features::LinkCapturingUiUpdateEnabled()) {
     intent_chip_ =
         AddChildView(std::make_unique<IntentChipButton>(browser_, this));
   }
@@ -313,7 +314,7 @@
             autofill::features::kAutofillEnableToolbarStatusChip)) {
       params.types_enabled.push_back(PageActionIconType::kManagePasswords);
     }
-    if (!base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate))
+    if (!apps::features::LinkCapturingUiUpdateEnabled())
       params.types_enabled.push_back(PageActionIconType::kIntentPicker);
     params.types_enabled.push_back(PageActionIconType::kPwaInstall);
 #if BUILDFLAG(ENABLE_SIDE_SEARCH)
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
index cc311e0..a8470a6 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
@@ -165,7 +165,26 @@
   service->OnStartPresentationContextCreated(std::move(context));
 
   MediaToolbarButtonView* const media_button = GetMediaButton();
-  if (!media_button) {
+  // If there exists a media button, anchor the dialog to this media button.
+  if (media_button) {
+    scoped_widget_observations_.AddObservation(MediaDialogView::ShowDialog(
+        media_button, views::BubbleBorder::TOP_RIGHT, service, profile,
+        initiator(),
+        global_media_controls::GlobalMediaControlsEntryPoint::kPresentation));
+    return;
+  }
+  Browser* const browser = chrome::FindBrowserWithWebContents(initiator());
+  BrowserView* const browser_view =
+      browser ? BrowserView::GetBrowserViewForBrowser(browser) : nullptr;
+  // If there exists a browser_view, anchor the dialog to the top center of the
+  // browser_view. This is necessary only for Mac, but works for other
+  // platforms.
+  if (browser_view) {
+    scoped_widget_observations_.AddObservation(MediaDialogView::ShowDialog(
+        browser_view->top_container(), views::BubbleBorder::TOP_CENTER, service,
+        profile, initiator(),
+        global_media_controls::GlobalMediaControlsEntryPoint::kPresentation));
+  } else {
     // Show the GMC dialog anchored to the top of the web contents.
     gfx::Rect anchor_bounds = initiator()->GetContainerBounds();
     anchor_bounds.set_height(0);
@@ -174,13 +193,7 @@
             anchor_bounds, service, profile, initiator(),
             global_media_controls::GlobalMediaControlsEntryPoint::
                 kPresentation));
-    return;
   }
-
-  scoped_widget_observations_.AddObservation(MediaDialogView::ShowDialog(
-      media_button, views::BubbleBorder::TOP_RIGHT, service, profile,
-      initiator(),
-      global_media_controls::GlobalMediaControlsEntryPoint::kPresentation));
 }
 
 MediaToolbarButtonView* MediaRouterDialogControllerViews::GetMediaButton() {
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
index 2f0218a..05b45e4 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc
@@ -8,6 +8,8 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/media_router/cast_dialog_view.h"
 #include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -135,10 +137,9 @@
   auto* view = MediaDialogView::GetDialogViewForTesting();
   // If there does not exist a media button, the dialog should not have an
   // anchor view.
-  EXPECT_FALSE(view->GetAnchorView());
-  gfx::Rect anchor_bounds = initiator_->GetContainerBounds();
-  anchor_bounds.set_height(0);
-  EXPECT_EQ(anchor_bounds, view->GetAnchorRect());
+  views::View* anchor_view =
+      BrowserView::GetBrowserViewForBrowser(browser())->top_container();
+  EXPECT_EQ(anchor_view, view->GetAnchorView());
 }
 
 IN_PROC_BROWSER_TEST_F(GlobalMediaControlsDialogTest,
diff --git a/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc b/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc
index 86c7a39..5d024d3 100644
--- a/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc
@@ -5,7 +5,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/views/menu_test_base.h"
-#include "ui/native_theme/themed_vector_icon.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/menu/submenu_view.h"
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 0fe5235..25d4974e 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -35,6 +35,7 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/image_model.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/display/display.h"
@@ -43,7 +44,6 @@
 #include "ui/gfx/image/canvas_image_source.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/vector_icon_types.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/ink_drop.h"
@@ -54,7 +54,6 @@
 #include "ui/views/controls/link.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
@@ -301,9 +300,9 @@
     ImageView::OnThemeChanged();
     constexpr int kBadgePadding = 1;
     DCHECK(!avatar_image_.IsEmpty());
-    gfx::ImageSkia sized_avatar_image = views::GetImageSkiaFromImageModel(
-        SizeImageModel(avatar_image_, ProfileMenuViewBase::kIdentityImageSize),
-        GetColorProvider());
+    gfx::ImageSkia sized_avatar_image =
+        SizeImageModel(avatar_image_, ProfileMenuViewBase::kIdentityImageSize)
+            .Rasterize(GetColorProvider());
     sized_avatar_image = AddCircularBackground(
         sized_avatar_image, GetBackgroundColor(), kIdentityImageSizeInclBorder);
     gfx::ImageSkia sized_badge = AddCircularBackground(
diff --git a/chrome/browser/ui/views/side_search/default_search_icon_source.cc b/chrome/browser/ui/views/side_search/default_search_icon_source.cc
index 09fe943e6..34e1099 100644
--- a/chrome/browser/ui/views/side_search/default_search_icon_source.cc
+++ b/chrome/browser/ui/views/side_search/default_search_icon_source.cc
@@ -55,18 +55,9 @@
 }
 
 ui::ImageModel DefaultSearchIconSource::GetSizedIconImage(int size) const {
-  content::WebContents* active_contents =
-      browser_->tab_strip_model()->GetActiveWebContents();
-  if (!active_contents)
-    return ui::ImageModel();
-
-  // Attempt to synchronously get the current default search engine's favicon.
-  auto* omnibox_view = search::GetOmniboxView(active_contents);
-  DCHECK(omnibox_view);
-  gfx::Image icon =
-      omnibox_view->model()->client()->GetFaviconForDefaultSearchProvider(
-          base::BindRepeating(&DefaultSearchIconSource::OnIconFetched,
-                              weak_ptr_factory_.GetWeakPtr()));
+  // If `icon` is empty we may have missed in the cache. Early return and notify
+  // clients when the icon is ready.
+  gfx::Image icon = GetRawIconImage();
   if (icon.IsEmpty())
     return ui::ImageModel();
 
@@ -85,6 +76,24 @@
                                                         padding_border));
 }
 
+ui::ImageModel DefaultSearchIconSource::GetIconImage() const {
+  return ui::ImageModel::FromImage(GetRawIconImage());
+}
+
+gfx::Image DefaultSearchIconSource::GetRawIconImage() const {
+  content::WebContents* active_contents =
+      browser_->tab_strip_model()->GetActiveWebContents();
+  if (!active_contents)
+    return gfx::Image();
+
+  // Attempt to synchronously get the current default search engine's favicon.
+  auto* omnibox_view = search::GetOmniboxView(active_contents);
+  DCHECK(omnibox_view);
+  return omnibox_view->model()->client()->GetFaviconForDefaultSearchProvider(
+      base::BindRepeating(&DefaultSearchIconSource::OnIconFetched,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
 void DefaultSearchIconSource::OnIconFetched(const gfx::Image& icon) {
   // The favicon requested in the call to GetFaviconForDefaultSearchProvider()
   // will now have been cached by ChromeOmniboxClient's FaviconCache and
diff --git a/chrome/browser/ui/views/side_search/default_search_icon_source.h b/chrome/browser/ui/views/side_search/default_search_icon_source.h
index bdf544f2..d0ef6760 100644
--- a/chrome/browser/ui/views/side_search/default_search_icon_source.h
+++ b/chrome/browser/ui/views/side_search/default_search_icon_source.h
@@ -36,16 +36,25 @@
   void OnTemplateURLServiceChanged() override;
   void OnTemplateURLServiceShuttingDown() override;
 
-  // Gets the icon image for the current default search provider. Will return an
-  // empty image model if this misses in the icon cache and will notify the
-  // `icon_changed_subscription_` when the icon is ready.
+  // Gets the icon image for the current default search provider with padding
+  // added to bring the resulting ImageModel up to `size`.
   ui::ImageModel GetSizedIconImage(int size) const;
 
+  // Similar to `GetSizedIconImage()` except this does not add padding.
+  ui::ImageModel GetIconImage() const;
+
  private:
   // Callback used for when `GetSizedIconImage()` does not return the icon image
   // immediately but instead fetches the image asynchronously.
   void OnIconFetched(const gfx::Image& icon);
 
+  // Gets the raw gfx::Image icon from the TemplateURL for the current default
+  // search provider. Will return an empty image if this misses in the icon
+  // cache and instead will notify the `icon_changed_subscription_` when the
+  // icon is ready. FaviconCache guarantees favicons will be of size
+  // gfx::kFaviconSize (16x16)
+  gfx::Image GetRawIconImage() const;
+
   // Used to fetch the ChromeOmniboxClient associated with the browser's active
   // tab.
   raw_ptr<Browser> browser_;
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
index 873763b62..17542d3 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
@@ -18,23 +18,30 @@
 #include "chrome/browser/ui/user_education/feature_promo_controller.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel.h"
+#include "chrome/browser/ui/views/side_search/default_search_icon_source.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/feature_engagement/public/event_constants.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/tracker.h"
+#include "components/url_formatter/elide_url.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/navigation_handle.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/pointer/touch_ui_controller.h"
+#include "ui/color/color_id.h"
+#include "ui/gfx/image/canvas_image_source.h"
+#include "ui/views/animation/ink_drop.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/highlight_path_generator.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/layout/box_layout.h"
@@ -46,23 +53,8 @@
 
 namespace {
 
-constexpr int kSidePanelWidth = 380;
 constexpr int kDefaultTouchableIconSize = 24;
 
-// Below are hardcoded color constants for the side panel. This is a UX decision
-// to ensure that the colors align with the tier 2 Google SRP which only
-// supports light mode. These are not intended to change to match the light/dark
-// system setting or custom theme colors.
-
-// White background to match the Google SRP.
-constexpr SkColor kHeaderBackgroundColor = SK_ColorWHITE;
-
-// Default light mode icon color for controls.
-constexpr SkColor kIconColor = gfx::kGoogleGrey700;
-
-// Default light mode separator color.
-constexpr SkColor kSeparatorColor = gfx::kGoogleGrey300;
-
 // Base header button class. Responds appropriately to touch ui changes.
 class HeaderButton : public views::ImageButton {
  public:
@@ -79,6 +71,13 @@
   }
   ~HeaderButton() override = default;
 
+  // views::ImageButton:
+  void OnThemeChanged() override {
+    ImageButton::OnThemeChanged();
+    views::InkDrop::Get(this)->SetBaseColor(
+        GetColorProvider()->GetColor(ui::kColorIcon));
+  }
+
   void UpdateIcon() {
     const int icon_size =
         ui::TouchUiController::Get()->touch_ui()
@@ -86,7 +85,11 @@
             : ChromeLayoutProvider::Get()->GetDistanceMetric(
                   ChromeDistanceMetric::
                       DISTANCE_SIDE_PANEL_HEADER_VECTOR_ICON_SIZE);
-    views::SetImageFromVectorIconWithColor(this, icon_, icon_size, kIconColor);
+    SetImageModel(Button::STATE_NORMAL, ui::ImageModel::FromVectorIcon(
+                                            icon_, ui::kColorIcon, icon_size));
+    SetImageModel(Button::STATE_DISABLED,
+                  ui::ImageModel::FromVectorIcon(icon_, ui::kColorIconDisabled,
+                                                 icon_size));
   }
 
  private:
@@ -96,22 +99,64 @@
 BEGIN_METADATA(HeaderButton, views::ImageButton)
 END_METADATA
 
-// Header view used to house the close control at the top of the side panel.
+// A view that tracks the icon image of the current DSE.
+class DseImageView : public views::ImageView {
+ public:
+  METADATA_HEADER(DseImageView);
+  explicit DseImageView(Browser* browser)
+      : default_search_icon_source_(
+            browser,
+            base::BindRepeating(&DseImageView::UpdateIconImage,
+                                base::Unretained(this))) {
+    SetBorder(views::CreateEmptyBorder(
+        gfx::Insets(0, views::LayoutProvider::Get()->GetDistanceMetric(
+                           views::DISTANCE_RELATED_CONTROL_VERTICAL))));
+  }
+  ~DseImageView() override = default;
+
+  void UpdateIconImage() {
+    SetImage(default_search_icon_source_.GetIconImage());
+  }
+
+ private:
+  DefaultSearchIconSource default_search_icon_source_;
+};
+
+BEGIN_METADATA(DseImageView, views::ImageView)
+END_METADATA
+
+// Header view for the side search side panel. The structure is as follows.
+//  ___________________________________________________________________________
+// | dse_image_view | simple_site_name        | feedback_button | close_button |
+// |
+//  ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+// The image view and buttons are fixed at their preferred size. The simple site
+// name label is configured to consume the remaining horizontal space.
 class HeaderView : public views::View {
  public:
   METADATA_HEADER(HeaderView);
-  explicit HeaderView(base::RepeatingClosure callback, Browser* browser)
-      : close_button_(AddChildView(
-            std::make_unique<HeaderButton>(vector_icons::kCloseIcon,
-                                           std::move(callback)))),
-        layout_(SetLayoutManager(std::make_unique<views::BoxLayout>())) {
-    views::InstallCircleHighlightPathGenerator(close_button_);
-    close_button_->SetID(SideSearchBrowserController::SideSearchViewID::
-                             VIEW_ID_SIDE_PANEL_CLOSE_BUTTON);
-    close_button_->SetAccessibleName(
-        l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_SEARCH_CLOSE_BUTTON));
-    close_button_->SetTooltipText(
-        l10n_util::GetStringUTF16(IDS_TOOLTIP_SIDE_SEARCH_CLOSE_BUTTON));
+  HeaderView(base::RepeatingClosure callback, Browser* browser)
+      : layout_(SetLayoutManager(std::make_unique<views::FlexLayout>())) {
+    layout_->SetOrientation(views::LayoutOrientation::kHorizontal)
+        .SetMainAxisAlignment(views::LayoutAlignment::kStart)
+        .SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
+
+    dse_image_view_ = AddChildView(std::make_unique<DseImageView>(browser));
+    dse_image_view_->SetProperty(
+        views::kFlexBehaviorKey,
+        views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+                                 views::MaximumFlexSizeRule::kPreferred));
+
+    auto* simple_site_name = AddChildView(std::make_unique<views::Label>());
+    simple_site_name->SetID(SideSearchBrowserController::SideSearchViewID::
+                                VIEW_ID_SIDE_PANEL_TITLE_LABEL);
+    simple_site_name->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    simple_site_name->SetTextContext(CONTEXT_SIDE_PANEL_TITLE);
+    simple_site_name->SetTextStyle(views::style::STYLE_PRIMARY);
+    simple_site_name->SetProperty(
+        views::kFlexBehaviorKey,
+        views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+                                 views::MaximumFlexSizeRule::kUnbounded));
 
     if (base::FeatureList::IsEnabled(features::kSideSearchFeedback)) {
       base::RepeatingClosure feedback_callback = base::BindRepeating(
@@ -135,33 +180,61 @@
           l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_SEARCH_FEEDBACK_BUTTON));
       feedback_button_->SetTooltipText(
           l10n_util::GetStringUTF16(IDS_TOOLTIP_SIDE_SEARCH_FEEDBACK_BUTTON));
+      feedback_button_->SetProperty(
+          views::kFlexBehaviorKey,
+          views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+                                   views::MaximumFlexSizeRule::kPreferred));
     }
 
+    close_button_ = AddChildView(std::make_unique<HeaderButton>(
+        vector_icons::kCloseIcon, std::move(callback)));
+    views::InstallCircleHighlightPathGenerator(close_button_);
+    close_button_->SetID(SideSearchBrowserController::SideSearchViewID::
+                             VIEW_ID_SIDE_PANEL_CLOSE_BUTTON);
+    close_button_->SetAccessibleName(
+        l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_SEARCH_CLOSE_BUTTON));
+    close_button_->SetTooltipText(
+        l10n_util::GetStringUTF16(IDS_TOOLTIP_SIDE_SEARCH_CLOSE_BUTTON));
+    close_button_->SetProperty(
+        views::kFlexBehaviorKey,
+        views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+                                 views::MaximumFlexSizeRule::kPreferred));
+
+    // Ensure the header view's containing view keeps its vertical size at the
+    // preferred size when laying out the side panel. The side panel does this
+    // using a flex layout so we need to ensure we set the correct flex
+    // behavior.
     SetProperty(
         views::kFlexBehaviorKey,
         views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
                                  views::MaximumFlexSizeRule::kPreferred));
-    SetBackground(views::CreateSolidBackground(kHeaderBackgroundColor));
-    layout_->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);
+    SetBackground(
+        views::CreateThemedSolidBackground(this, ui::kColorDialogBackground));
     UpdateSpacing();
   }
   ~HeaderView() override = default;
 
  private:
+  // Updates the toolbar insets which may change as we enter / leave touch mode.
+  // Icons are also updated to give them the opportunity to resize and adjust
+  // their insets.
   void UpdateSpacing() {
-    close_button_->UpdateIcon();
-
+    dse_image_view_->UpdateIconImage();
     if (feedback_button_)
       feedback_button_->UpdateIcon();
+    close_button_->UpdateIcon();
 
-    layout_->set_inside_border_insets(
+    layout_->SetInteriorMargin(
         GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN));
   }
 
-  HeaderButton* const close_button_;
-  HeaderButton* feedback_button_ = nullptr;
-  views::BoxLayout* const layout_;
+  raw_ptr<DseImageView> dse_image_view_ = nullptr;
+  raw_ptr<HeaderButton> feedback_button_ = nullptr;
+  raw_ptr<HeaderButton> close_button_ = nullptr;
 
+  raw_ptr<views::FlexLayout> const layout_;
+
+  // Used to listen for when the UI enters / leaves touch mode.
   base::CallbackListSubscription subscription_ =
       ui::TouchUiController::Get()->RegisterCallback(
           base::BindRepeating(&HeaderView::UpdateSpacing,
@@ -171,26 +244,16 @@
 BEGIN_METADATA(HeaderView, views::View)
 END_METADATA
 
-std::unique_ptr<views::Separator> CreateSeparator() {
-  auto separator = std::make_unique<views::Separator>();
-  separator->SetColor(kSeparatorColor);
-  return separator;
-}
-
 views::WebView* ConfigureSidePanel(views::View* side_panel,
                                    Profile* profile,
                                    Browser* browser,
                                    base::RepeatingClosure callback) {
-  // BrowserViewLayout will layout the SidePanel to match the height of the
-  // content area.
-  side_panel->SetPreferredSize(gfx::Size(kSidePanelWidth, 1));
-
   auto container = std::make_unique<views::FlexLayoutView>();
   container->SetOrientation(views::LayoutOrientation::kVertical);
   container->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
   container->AddChildView(
       std::make_unique<HeaderView>(std::move(callback), browser));
-  container->AddChildView(CreateSeparator());
+  container->AddChildView(std::make_unique<views::Separator>());
 
   // The WebView will fill the remaining space after the header view has been
   // laid out.
@@ -440,6 +503,17 @@
   web_view_->SetWebContents(will_show_side_panel
                                 ? tab_contents_helper->GetSidePanelContents()
                                 : nullptr);
+
+  // Update the side panel header title text if necessary
+  if (auto last_search_url = tab_contents_helper->last_search_url()) {
+    views::Label* title_label =
+        static_cast<views::Label*>(side_panel_->GetViewByID(
+            static_cast<int>(VIEW_ID_SIDE_PANEL_TITLE_LABEL)));
+    title_label->SetText(
+        url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
+            last_search_url.value()));
+  }
+
   side_panel_->SetVisible(will_show_side_panel);
 
   // Update the side panel entrypoints - either the page action or the toolbar
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.h b/chrome/browser/ui/views/side_search/side_search_browser_controller.h
index bb33ead..77d6efc 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller.h
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.h
@@ -34,6 +34,7 @@
   enum SideSearchViewID {
     VIEW_ID_NONE = 0,
     VIEW_ID_SIDE_PANEL_CLOSE_BUTTON,
+    VIEW_ID_SIDE_PANEL_TITLE_LABEL,
   };
 
   SideSearchBrowserController(SidePanel* side_panel, BrowserView* browser_view);
diff --git a/chrome/browser/ui/views/tab_icon_view.cc b/chrome/browser/ui/views/tab_icon_view.cc
index aa90736..6a3acbb8 100644
--- a/chrome/browser/ui/views/tab_icon_view.cc
+++ b/chrome/browser/ui/views/tab_icon_view.cc
@@ -20,7 +20,6 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/favicon_size.h"
 #include "ui/gfx/paint_throbber.h"
-#include "ui/views/image_model_utils.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
@@ -140,8 +139,8 @@
       return;
     }
 
-    gfx::ImageSkia favicon = views::GetImageSkiaFromImageModel(
-        model_->GetFaviconForTabIconView(), GetColorProvider());
+    gfx::ImageSkia favicon =
+        model_->GetFaviconForTabIconView().Rasterize(GetColorProvider());
     if (!favicon.isNull()) {
       PaintFavicon(canvas, favicon);
       return;
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index 4df9f0a5..43328c8 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -57,6 +57,7 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/image_model.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
 #include "ui/gfx/canvas.h"
@@ -66,7 +67,6 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/text_utils.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
index 43ea6c3..0085e4e 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
@@ -51,14 +51,6 @@
         "chrome-labs-tab-search-media-tabs", version_info::Channel::BETA,
         tab_search_media_tabs_variation_description));
 
-    // Lens Region Search
-    lab_info.emplace_back(LabInfo(
-        flag_descriptions::kEnableLensRegionSearchFlagId,
-        l10n_util::GetStringUTF16(IDS_LENS_REGION_SEARCH_EXPERIMENT_NAME),
-        l10n_util::GetStringUTF16(
-            IDS_LENS_REGION_SEARCH_EXPERIMENT_DESCRIPTION),
-        "chrome-labs-lens-region-search", version_info::Channel::BETA));
-
     // Side Panel.
     lab_info.emplace_back(LabInfo(
         flag_descriptions::kSidePanelFlagId,
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc
index 0a01c35..7683244 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc
@@ -48,7 +48,7 @@
   // kTabSearchSelected = 2,
   kTabScrollingSelected = 3,
   kSidePanelSelected = 4,
-  kLensRegionSearchSelected = 5,
+  // kLensRegionSearchSelected = 5,
   kWebUITabStripSelected = 6,
   kTabSearchMediaTabsSelected = 7,
   kMaxValue = kTabSearchMediaTabsSelected,
@@ -77,8 +77,6 @@
       return ChromeLabsSelectedLab::kTabScrollingSelected;
     if (internal_name == flag_descriptions::kSidePanelFlagId)
       return ChromeLabsSelectedLab::kSidePanelSelected;
-    if (internal_name == flag_descriptions::kEnableLensRegionSearchFlagId)
-      return ChromeLabsSelectedLab::kLensRegionSearchSelected;
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) && \
     (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS_ASH))
     if (internal_name == flag_descriptions::kWebUITabStripFlagId)
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index b02bd62..9f76962e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -19,7 +19,9 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/apps/intent_helper/intent_picker_features.h"
 #include "chrome/browser/command_updater.h"
+#include "chrome/browser/download/bubble/download_bubble_prefs.h"
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
@@ -268,7 +270,7 @@
   }
 
   std::unique_ptr<DownloadToolbarButtonView> download_button;
-  if (base::FeatureList::IsEnabled(safe_browsing::kDownloadBubble)) {
+  if (download::IsDownloadBubbleEnabled(browser_->profile())) {
     download_button =
         std::make_unique<DownloadToolbarButtonView>(browser_view_);
   }
@@ -507,8 +509,9 @@
   views::Button* highlighted_button = nullptr;
   if (bubble_type == IntentPickerBubbleView::BubbleType::kClickToCall) {
     highlighted_button =
+
         GetPageActionIconView(PageActionIconType::kClickToCall);
-  } else if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) {
+  } else if (apps::features::LinkCapturingUiUpdateEnabled()) {
     highlighted_button = GetIntentChipButton();
   } else {
     highlighted_button =
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc
index 8585711..bb13ab1 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -13,10 +13,12 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/themes/browser_theme_pack.h"
+#include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window_state.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/tabs/tab_menu_model_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_delegate.h"
@@ -38,6 +40,9 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/models/image_model.h"
+#include "ui/color/color_id.h"
+#include "ui/color/color_recipe.h"
+#include "ui/color/color_transform.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/color_palette.h"
@@ -51,6 +56,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/apps/icon_standardizer.h"
+#include "chromeos/ui/base/chromeos_ui_constants.h"
 #endif
 
 namespace {
@@ -389,7 +395,43 @@
 
 void AppBrowserController::AddColorMixers(
     ui::ColorProvider* provider,
-    const ui::ColorProviderManager::Key& key) const {}
+    const ui::ColorProviderManager::Key& key) const {
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+  // This color is the same as the default active frame color.
+  const absl::optional<SkColor> theme_color = GetThemeColor();
+  ui::ColorTransform default_background =
+      key.color_mode == ui::ColorProviderManager::ColorMode::kLight
+          ? ui::ColorTransform(ui::kColorFrameActiveUnthemed)
+          : ui::HSLShift(ui::kColorFrameActiveUnthemed,
+                         ThemeProperties::GetDefaultTint(
+                             ThemeProperties::TINT_FRAME, true));
+#endif
+  ui::ColorMixer& mixer = provider->AddMixer();
+  absl::optional<SkColor> bg_color = GetBackgroundColor();
+  // TODO(kylixrd): The definition of kColorPwaBackground isn't fully fleshed
+  // out yet. Whether or not the PWA background color is set is used in many
+  // locations to derive other colors. Those specific locations would need to be
+  // addressed in their own context.
+  if (bg_color)
+    mixer[kColorPwaBackground] = {bg_color.value()};
+  mixer[kColorPwaMenuButtonIcon] = {kColorToolbarButtonIcon};
+  mixer[kColorPwaSecurityChipForeground] = {ui::kColorSecondaryForeground};
+  mixer[kColorPwaSecurityChipForegroundDangerous] = {
+      ui::kColorAlertHighSeverity};
+  mixer[kColorPwaSecurityChipForegroundPolicyCert] = {
+      ui::kColorDisabledForeground};
+  mixer[kColorPwaSecurityChipForegroundSecure] = {
+      kColorPwaSecurityChipForeground};
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Ash system frames differ from ChromeOS browser frames.
+  mixer[kColorPwaTheme] = {chromeos::kDefaultFrameColor};
+#else
+  mixer[kColorPwaTheme] = theme_color ? ui::ColorTransform(theme_color.value())
+                                      : default_background;
+#endif
+  mixer[kColorPwaToolbarBackground] = {ui::kColorEndpointBackground};
+  mixer[kColorPwaToolbarForeground] = {ui::kColorEndpointForeground};
+}
 
 void AppBrowserController::OnReceivedInitialURL() {
   UpdateCustomTabBarVisibility(/*animate=*/false);
diff --git a/chrome/browser/ui/webui/OWNERS b/chrome/browser/ui/webui/OWNERS
index 624340e..acfb748 100644
--- a/chrome/browser/ui/webui/OWNERS
+++ b/chrome/browser/ui/webui/OWNERS
@@ -18,4 +18,4 @@
 
 per-file signin_internals_ui*=achuith@chromium.org
 
-per-file invalidations_message_handler.*=file://components/invalidation/OWNERS
+per-file invalidations/invalidations_message_handler.*=file://components/invalidation/OWNERS
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index a85cacb..4f14930 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -24,8 +24,9 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/app_constants/constants.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
-#include "components/services/app_service/public/cpp/intent_constants.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
+#include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/cpp/preferred_apps_list_handle.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
@@ -113,7 +114,9 @@
                    if (update.Readiness() == apps::Readiness::kReady) {
                      for (auto& filter : update.IntentFilters()) {
                        if (apps_util::IsSupportedLinkForApp(app_id, filter)) {
-                         intent_filters.emplace_back(std::move(filter));
+                         intent_filters.emplace_back(
+                             apps::ConvertIntentFilterToMojomIntentFilter(
+                                 filter));
                        }
                      }
                    }
@@ -296,7 +299,7 @@
   // Remove the use_browser app ID as it's mainly used inside the intent system and is not an app
   // in app management. This prevents an overlap dialog from being shown when there are no "real"
   // apps that overlap.
-  app_ids.erase(apps::kUseBrowserForLink);
+  app_ids.erase(apps_util::kUseBrowserForLink);
   std::move(callback).Run(std::move(app_ids).extract());
 }
 
diff --git a/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc b/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc
index 0de418ee..29233a4 100644
--- a/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc
+++ b/chrome/browser/ui/webui/app_service_internals/app_service_internals_page_handler_impl.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "components/services/app_service/public/cpp/app_update.h"
-#include "components/services/app_service/public/cpp/intent_constants.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
+#include "components/services/app_service/public/cpp/intent_util.h"
 
 AppServiceInternalsPageHandlerImpl::AppServiceInternalsPageHandlerImpl(
     Profile* profile)
@@ -74,7 +74,7 @@
     auto ptr = mojom::app_service_internals::PreferredAppInfo::New();
     ptr->id = kv.first;
 
-    if (ptr->id == apps::kUseBrowserForLink) {
+    if (ptr->id == apps_util::kUseBrowserForLink) {
       ptr->name = ptr->id;
     } else {
       proxy->AppRegistryCache().ForOneApp(
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
index 0f21a75..42259dda 100644
--- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -14,6 +14,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/files/file_enumerator.h"
@@ -281,6 +282,16 @@
         base::BindRepeating(&DriveInternalsWebUIHandler::SetMirroringEnabled,
                             weak_ptr_factory_.GetWeakPtr()));
     web_ui()->RegisterMessageCallback(
+        "addSyncPath",
+        base::BindRepeating(&DriveInternalsWebUIHandler::ToggleSyncPath,
+                            weak_ptr_factory_.GetWeakPtr(),
+                            drivefs::mojom::MirrorPathStatus::kStart));
+    web_ui()->RegisterMessageCallback(
+        "removeSyncPath",
+        base::BindRepeating(&DriveInternalsWebUIHandler::ToggleSyncPath,
+                            weak_ptr_factory_.GetWeakPtr(),
+                            drivefs::mojom::MirrorPathStatus::kStop));
+    web_ui()->RegisterMessageCallback(
         "enableTracing",
         base::BindRepeating(&DriveInternalsWebUIHandler::SetTracingEnabled,
                             weak_ptr_factory_.GetWeakPtr(), true));
@@ -358,6 +369,8 @@
 
     UpdateDriveDebugSection();
 
+    UpdateMirrorSyncSection();
+
     // When the drive-internals page is reloaded by the reload key, the page
     // content is recreated, but this WebUI object is not (instead, OnPageLoaded
     // is called again). In that case, we have to forget the last sent ID here,
@@ -473,10 +486,6 @@
     MaybeCallJavascript("updateVerboseLogging",
                         base::Value(verbose_logging_enabled));
 
-    bool mirroring_enabled = profile()->GetPrefs()->GetBoolean(
-        drive::prefs::kDriveFsEnableMirrorSync);
-    MaybeCallJavascript("updateMirroring", base::Value(mirroring_enabled));
-
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
         base::BindOnce(GetDeveloperMode),
@@ -496,6 +505,82 @@
     }
   }
 
+  void UpdateMirrorSyncSection() {
+    if (!chromeos::features::IsDriveFsMirroringEnabled()) {
+      SetSectionEnabled("mirror-sync-section", false);
+      return;
+    }
+
+    SetSectionEnabled("mirror-sync-section", true);
+
+    bool mirroring_enabled = profile()->GetPrefs()->GetBoolean(
+        drive::prefs::kDriveFsEnableMirrorSync);
+    MaybeCallJavascript("updateMirroring", base::Value(mirroring_enabled));
+    SetSectionEnabled("mirror-sync-paths", mirroring_enabled);
+    SetSectionEnabled("mirror-path-form", mirroring_enabled);
+    if (!mirroring_enabled) {
+      return;
+    }
+
+    drive::DriveIntegrationService* integration_service =
+        GetIntegrationService();
+    if (!integration_service) {
+      return;
+    }
+
+    integration_service->GetSyncingPaths(
+        base::BindOnce(&DriveInternalsWebUIHandler::OnGetSyncingPaths,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  void OnGetSyncingPaths(drive::FileError status,
+                         const std::vector<base::FilePath>& paths) {
+    if (status != drive::FILE_ERROR_OK) {
+      LOG(ERROR) << "Error retrieving syncing paths: " << status;
+      return;
+    }
+    for (const base::FilePath& sync_path : paths) {
+      MaybeCallJavascript(
+          "onAddSyncPath", base::Value(sync_path.value()),
+          base::Value(drive::FileErrorToString(drive::FILE_ERROR_OK)));
+    }
+  }
+
+  void ToggleSyncPath(drivefs::mojom::MirrorPathStatus status,
+                      const base::Value::List& args) {
+    if (!chromeos::features::IsDriveFsMirroringEnabled()) {
+      return;
+    }
+
+    drive::DriveIntegrationService* integration_service =
+        GetIntegrationService();
+    if (!integration_service) {
+      return;
+    }
+
+    if (args.size() == 1 && args[0].is_string()) {
+      const base::FilePath sync_path(args[0].GetString());
+      auto callback =
+          base::BindOnce((status == drivefs::mojom::MirrorPathStatus::kStart)
+                             ? &DriveInternalsWebUIHandler::OnAddSyncPath
+                             : &DriveInternalsWebUIHandler::OnRemoveSyncPath,
+                         weak_ptr_factory_.GetWeakPtr(), sync_path);
+      integration_service->ToggleSyncForPath(sync_path, status,
+                                             std::move(callback));
+    }
+  }
+
+  void OnAddSyncPath(const base::FilePath& sync_path, drive::FileError status) {
+    MaybeCallJavascript("onAddSyncPath", base::Value(sync_path.value()),
+                        base::Value(drive::FileErrorToString(status)));
+  }
+
+  void OnRemoveSyncPath(const base::FilePath& sync_path,
+                        drive::FileError status) {
+    MaybeCallJavascript("onRemoveSyncPath", base::Value(sync_path.value()),
+                        base::Value(drive::FileErrorToString(status)));
+  }
+
   // Called when GetDeveloperMode() is complete.
   void OnGetDeveloperMode(bool enabled) {
     developer_mode_ = enabled;
@@ -682,6 +767,8 @@
       bool enabled = args[0].GetBool();
       profile()->GetPrefs()->SetBoolean(drive::prefs::kDriveFsEnableMirrorSync,
                                         enabled);
+      SetSectionEnabled("mirror-sync-paths", enabled);
+      SetSectionEnabled("mirror-path-form", enabled);
     }
   }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc
index 958e19b..e840174b 100644
--- a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.cc
@@ -210,4 +210,8 @@
 void ConsolidatedConsentScreenHandler::SetIsDeviceOwner(bool is_owner) {
   CallJS("login.ConsolidatedConsentScreen.setIsDeviceOwner", is_owner);
 }
+
+void ConsolidatedConsentScreenHandler::HideUsageOptin() {
+  CallJS("login.ConsolidatedConsentScreen.setUsageOptinHidden");
+}
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h
index 3fb71919..da9deb5 100644
--- a/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/consolidated_consent_screen_handler.h
@@ -63,6 +63,9 @@
   virtual void SetBackupMode(bool enabled, bool managed) = 0;
   virtual void SetLocationMode(bool enabled, bool managed) = 0;
   virtual void SetIsDeviceOwner(bool is_owner) = 0;
+
+  // Hide the entire section that allows user to opt-in/opt-out from metrics.
+  virtual void HideUsageOptin() = 0;
 };
 
 class ConsolidatedConsentScreenHandler : public ConsolidatedConsentScreenView,
@@ -89,6 +92,7 @@
   void SetBackupMode(bool enabled, bool managed) override;
   void SetLocationMode(bool enabled, bool managed) override;
   void SetIsDeviceOwner(bool is_owner) override;
+  void HideUsageOptin() override;
 
   // content::WebUIMessageHandler:
   void RegisterMessages() override;
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS b/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc
index 3b2387b..230f74f 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.cc
@@ -4,10 +4,10 @@
 
 #include "chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/bind.h"
 #include "base/i18n/time_formatting.h"
 #include "base/values.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 
 namespace chromeos {
 
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h
index 92289bb..c7b3192 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_logs_handler.h
@@ -5,9 +5,9 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_MULTIDEVICE_INTERNALS_MULTIDEVICE_INTERNALS_LOGS_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_MULTIDEVICE_INTERNALS_MULTIDEVICE_INTERNALS_LOGS_HANDLER_H_
 
+#include "ash/components/multidevice/logging/log_buffer.h"
+#include "ash/components/multidevice/logging/logging.h"
 #include "base/scoped_observation.h"
-#include "chromeos/components/multidevice/logging/log_buffer.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace base {
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
index 309f5bd..5fb2c49 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/camera_roll_item.h"
 #include "ash/components/phonehub/fake_phone_hub_manager.h"
 #include "ash/components/phonehub/notification.h"
@@ -14,7 +15,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/phonehub/phone_hub_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS b/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/browser/ui/webui/commander/commander_ui.cc b/chrome/browser/ui/webui/commander/commander_ui.cc
index 25da2b2..1428bb5 100644
--- a/chrome/browser/ui/webui/commander/commander_ui.cc
+++ b/chrome/browser/ui/webui/commander/commander_ui.cc
@@ -13,6 +13,7 @@
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/commander_resources.h"
 #include "chrome/grit/commander_resources_map.h"
+#include "chrome/grit/generated_resources.h"
 #include "content/public/browser/web_ui_data_source.h"
 
 CommanderUI::CommanderUI(content::WebUI* web_ui)
@@ -23,10 +24,13 @@
 
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUICommanderHost);
-  // TODO(lgrey): Localize when these are no longer temp.
-  source->AddString("placeholder", "Type to search Chrome Commands…");
-  source->AddString("noResults", "No Chrome Commands found.");
-  source->AddString("pageTitle", "Commander");
+  static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"placeholder", IDS_QUICK_COMMANDS_PLACEHOLDER},
+      {"noResults", IDS_QUICK_COMMANDS_NO_RESULTS},
+      {"pageTitle", IDS_QUICK_COMMANDS_LABEL},
+  };
+  for (const auto& str : kLocalizedStrings)
+    webui::AddLocalizedString(source, str.name, str.id);
   webui::SetupWebUIDataSource(
       source, base::make_span(kCommanderResources, kCommanderResourcesSize),
       IDR_COMMANDER_COMMANDER_HTML);
diff --git a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc
index 5639e60..cb56c3a8 100644
--- a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc
+++ b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.cc
@@ -86,11 +86,10 @@
 }
 
 void InvalidationsMessageHandler::OnRegistrationChange(
-    const std::multiset<std::string>& registered_handlers) {
+    const std::set<std::string>& registered_handlers) {
   base::ListValue list_of_handlers;
-  for (auto it = registered_handlers.begin(); it != registered_handlers.end();
-       ++it) {
-    list_of_handlers.Append(*it);
+  for (const auto& registered_handler : registered_handlers) {
+    list_of_handlers.Append(registered_handler);
   }
   FireWebUIListener("handlers-updated", list_of_handlers);
 }
diff --git a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h
index 56ee1db..5a3da7f3 100644
--- a/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h
+++ b/chrome/browser/ui/webui/invalidations/invalidations_message_handler.h
@@ -33,7 +33,7 @@
 
   // Implementation of InvalidationLoggerObserver.
   void OnRegistrationChange(
-      const std::multiset<std::string>& registered_handlers) override;
+      const std::set<std::string>& registered_handlers) override;
   void OnStateChange(const invalidation::InvalidatorState& new_state,
                      const base::Time& last_change_timestamp) override;
   void OnUpdatedTopics(
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 857696a..8caf81d 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -99,49 +99,47 @@
 void AddResourcesForCartDiscountConsentV2(content::WebUIDataSource* source) {
   AddRawStringOrDefault(
       source, "modulesCartDiscountConsentContent",
-      ntp_features::kNtpChromeCartModuleDiscountConsentStringChangeContent
-          .Get(),
+      commerce::kNtpChromeCartModuleDiscountConsentStringChangeContent.Get(),
       IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT_V2);
 
   source->AddBoolean(
       "modulesCartConsentStepTwoDifferentColor",
-      ntp_features::
-          kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor.Get());
+      commerce::kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor
+          .Get());
 
   AddRawStringOrDefault(
       source, "modulesCartDiscountConentTitle",
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle
-          .Get(),
+      commerce::kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle.Get(),
       IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_TITLE);
 
   source->AddBoolean(
       "modulesCartStepOneUseStaticContent",
-      ntp_features::
-          kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent.Get());
+      commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent
+          .Get());
   // This does not have a raw string resource.
   source->AddString(
       "modulesCartStepOneStaticContent",
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent
+      commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent
           .Get());
 
   AddRawStringOrDefault(
       source, "modulesCartConsentStepOneOneMerchantContent",
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart
+      commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart
           .Get(),
       IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_MERCHANT_NAME);
   AddRawStringOrDefault(
       source, "modulesCartConsentStepOneTwoMerchantsContent",
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts
+      commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts
           .Get(),
       IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_TWO_MERCHANT_NAMES);
   AddRawStringOrDefault(
       source, "modulesCartConsentStepOneThreeMerchantsContent",
-      ntp_features::
-          kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts.Get(),
+      commerce::kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts
+          .Get(),
       IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_THREE_MERCHANT_NAMES);
   AddRawStringOrDefault(
       source, "modulesCartConsentStepTwoContent",
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepTwoContent.Get(),
+      commerce::kNtpChromeCartModuleDiscountConsentNtpStepTwoContent.Get(),
       IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT_V3);
 
   source->AddLocalizedString(
@@ -150,8 +148,7 @@
 
   source->AddBoolean(
       "modulesCartDiscountInlineCardShowCloseButton",
-      ntp_features::kNtpChromeCartModuleDiscountConsentInlineShowCloseButton
-          .Get());
+      commerce::kNtpChromeCartModuleDiscountConsentInlineShowCloseButton.Get());
 }
 
 content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
@@ -396,7 +393,7 @@
 
   source->AddInteger(
       "modulesCartDiscountConsentVariation",
-      ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
+      commerce::kNtpChromeCartModuleDiscountConsentNtpVariation.Get());
 
   if (base::FeatureList::IsEnabled(commerce::kDiscountConsentV2)) {
     AddResourcesForCartDiscountConsentV2(source);
diff --git a/chrome/browser/ui/webui/settings/chromeos/OWNERS b/chrome/browser/ui/webui/settings/chromeos/OWNERS
index 6b2398e..37ddeebc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/OWNERS
+++ b/chrome/browser/ui/webui/settings/chromeos/OWNERS
@@ -1,6 +1,6 @@
 file://chrome/browser/resources/settings/chromeos/OWNERS
 
 per-file languages_section*=file://chrome/browser/resources/settings/chromeos/os_languages_page/OWNERS
-per-file multidevice_handler*=file://chromeos/components/multidevice/OWNERS
+per-file multidevice_handler*=file://ash/components/multidevice/OWNERS
 per-file account_manager_*=file://ash/components/account_manager/OWNERS
 per-file apps_section*=file://chrome/browser/ui/webui/app_management/OWNERS
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index a809c7c..b31dbdb9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h"
 
+#include "ash/components/multidevice/logging/logging.h"
 #include "ash/components/phonehub/util/histogram_util.h"
 #include "ash/components/proximity_auth/proximity_auth_pref_names.h"
 #include "ash/constants/ash_features.h"
@@ -23,7 +24,6 @@
 #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h"
-#include "chromeos/components/multidevice/logging/logging.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_ui.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
index 01ec488..babc030c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_MULTIDEVICE_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_MULTIDEVICE_HANDLER_H_
 
+#include "ash/components/multidevice/remote_device_ref.h"
 #include "ash/components/phonehub/camera_roll_manager.h"
 #include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/notification_access_setup_operation.h"
@@ -16,7 +17,6 @@
 #include "chrome/browser/ash/android_sms/android_sms_app_manager.h"
 #include "chrome/browser/ash/android_sms/android_sms_service_factory.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
-#include "chromeos/components/multidevice/remote_device_ref.h"
 #include "components/prefs/pref_change_registrar.h"
 
 class PrefService;
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
index 20152ad..f444464 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/components/multidevice/remote_device_test_util.h"
 #include "ash/components/phonehub/fake_camera_roll_manager.h"
 #include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
 #include "ash/components/phonehub/multidevice_feature_access_manager.h"
@@ -20,7 +21,6 @@
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
 #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
-#include "chromeos/components/multidevice/remote_device_test_util.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
diff --git a/chrome/browser/url_param_filter/cross_otr_observer.cc b/chrome/browser/url_param_filter/cross_otr_observer.cc
index a13eec6b..2613591e 100644
--- a/chrome/browser/url_param_filter/cross_otr_observer.cc
+++ b/chrome/browser/url_param_filter/cross_otr_observer.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/url_param_filter/cross_otr_observer.h"
+
 #include <memory>
+
 #include "base/metrics/histogram_functions.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
@@ -27,15 +29,16 @@
           NavigateParams::PrivacySensitivity::CROSS_OTR &&
       params.started_from_context_menu &&
       !ui::PageTransitionCoreTypeIs(params.transition,
-                                    ui::PAGE_TRANSITION_AUTO_BOOKMARK) &&
-      !web_contents->GetUserData(CrossOtrObserver::kUserDataKey)) {
-    web_contents->SetUserData(CrossOtrObserver::kUserDataKey,
-                              std::make_unique<CrossOtrObserver>(web_contents));
+                                    ui::PAGE_TRANSITION_AUTO_BOOKMARK)) {
+    // Inherited from WebContentsUserData and checks for an already-attached
+    // instance internally.
+    CrossOtrObserver::CreateForWebContents(web_contents);
   }
 }
 
 CrossOtrObserver::CrossOtrObserver(content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {}
+    : content::WebContentsObserver(web_contents),
+      content::WebContentsUserData<CrossOtrObserver>(*web_contents) {}
 
 void CrossOtrObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
@@ -85,8 +88,10 @@
 
 void CrossOtrObserver::Detach() {
   base::UmaHistogramCounts100(kCrossOtrRefreshCountMetricName, refresh_count_);
-  web_contents()->RemoveUserData(CrossOtrObserver::kUserDataKey);
+  web_contents()->RemoveUserData(CrossOtrObserver::UserDataKey());
   // DO NOT add code past this point. `this` is destroyed.
 }
 
+WEB_CONTENTS_USER_DATA_KEY_IMPL(CrossOtrObserver);
+
 }  // namespace url_param_filter
diff --git a/chrome/browser/url_param_filter/cross_otr_observer.h b/chrome/browser/url_param_filter/cross_otr_observer.h
index e22f85d..6a7839e7b 100644
--- a/chrome/browser/url_param_filter/cross_otr_observer.h
+++ b/chrome/browser/url_param_filter/cross_otr_observer.h
@@ -5,24 +5,21 @@
 #ifndef CHROME_BROWSER_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_H_
 #define CHROME_BROWSER_URL_PARAM_FILTER_CROSS_OTR_OBSERVER_H_
 
-#include "base/supports_user_data.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
 
 namespace url_param_filter {
 
 // Observes navigations that originate in normal browsing and move into OTR
 // browsing.
 class CrossOtrObserver : public content::WebContentsObserver,
-                         public base::SupportsUserData::Data {
+                         public content::WebContentsUserData<CrossOtrObserver> {
  public:
-  // The key used to associate this observer with the given WebContents.
-  constexpr static const char kUserDataKey[] = "CrossOtrObserver";
   // Attaches the observer in cases where it should do so; leaves `web_contents`
   // unchanged otherwise.
   static void MaybeCreateForWebContents(content::WebContents* web_contents,
                                         const NavigateParams& params);
-  explicit CrossOtrObserver(content::WebContents* web_contents);
   // content::WebContentsObserver:
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
@@ -31,6 +28,13 @@
   void WebContentsDestroyed() override;
 
  private:
+  explicit CrossOtrObserver(content::WebContents* web_contents);
+
+  friend class content::WebContentsUserData<CrossOtrObserver>;
+  // Inherited from content::WebContentsUserData, but should not be used outside
+  // this class. MaybeCreateForWebcontents must be used instead.
+  using content::WebContentsUserData<CrossOtrObserver>::CreateForWebContents;
+  // Flushes metrics and removes the observer from the WebContents.
   void Detach();
   // Drives state machine logic; we write the cross-OTR response code metric
   // only for the first navigation, which is that which would have parameters
@@ -39,6 +43,8 @@
   // Tracks refreshes observed, which could point to an issue with param
   // filtering causing unexpected behavior for the user.
   int refresh_count_ = 0;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
 
 }  // namespace url_param_filter
diff --git a/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc b/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc
index 91a6423..39a7754 100644
--- a/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc
+++ b/chrome/browser/url_param_filter/cross_otr_observer_unittest.cc
@@ -46,7 +46,7 @@
       content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
   CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params);
 
-  ASSERT_EQ(web_contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr);
+  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr);
 }
 TEST_F(CrossOtrObserverTest, DefaultSensitivity) {
   NavigateParams params(profile(), GURL("https://www.foo.com"),
@@ -59,7 +59,7 @@
       content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
   CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params);
 
-  ASSERT_EQ(web_contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr);
+  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr);
 }
 TEST_F(CrossOtrObserverTest, BookmarkLink) {
   NavigateParams params(profile(), GURL("https://www.foo.com"),
@@ -72,7 +72,7 @@
       content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
   CrossOtrObserver::MaybeCreateForWebContents(web_contents.get(), params);
 
-  ASSERT_EQ(web_contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr);
+  ASSERT_EQ(CrossOtrObserver::FromWebContents(web_contents.get()), nullptr);
 }
 TEST_F(CrossOtrObserverTest, CreateKey) {
   NavigateParams params(profile(), GURL("https://www.foo.com"),
@@ -83,7 +83,7 @@
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
 
-  ASSERT_NE(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr);
+  ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
 }
 TEST_F(CrossOtrObserverTest, DuplicateCreateKey) {
   NavigateParams params(profile(), GURL("https://www.foo.com"),
@@ -95,7 +95,7 @@
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
 
-  ASSERT_NE(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr);
+  ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
 }
 TEST_F(CrossOtrObserverTest, HandleRedirects) {
   base::HistogramTester histogram_tester;
@@ -106,8 +106,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
@@ -131,8 +130,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
@@ -155,8 +153,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
@@ -174,8 +171,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
@@ -187,7 +183,7 @@
   // The observer should not cease observation after first load, regardless of
   // whether the headers include a response code. We still want to see
   // the refresh count.
-  ASSERT_NE(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr);
+  ASSERT_NE(CrossOtrObserver::FromWebContents(contents), nullptr);
 }
 TEST_F(CrossOtrObserverTest, RefreshedAfterNavigation) {
   base::HistogramTester histogram_tester;
@@ -198,8 +194,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
@@ -229,8 +224,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
@@ -271,8 +265,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
@@ -294,7 +287,7 @@
   handle->set_has_committed(true);
   observer->DidFinishNavigation(handle.get());
 
-  ASSERT_EQ(contents->GetUserData(CrossOtrObserver::kUserDataKey), nullptr);
+  ASSERT_EQ(CrossOtrObserver::FromWebContents(contents), nullptr);
 
   histogram_tester.ExpectTotalCount(kCrossOtrRefreshCountMetricName, 1);
   ASSERT_EQ(histogram_tester.GetTotalSum(kCrossOtrRefreshCountMetricName), 2);
@@ -312,8 +305,7 @@
   params.privacy_sensitivity = NavigateParams::PrivacySensitivity::CROSS_OTR;
   content::WebContents* contents = web_contents();
   CrossOtrObserver::MaybeCreateForWebContents(contents, params);
-  CrossOtrObserver* observer = static_cast<CrossOtrObserver*>(
-      contents->GetUserData(CrossOtrObserver::kUserDataKey));
+  CrossOtrObserver* observer = CrossOtrObserver::FromWebContents(contents);
   ASSERT_NE(observer, nullptr);
   std::unique_ptr<content::MockNavigationHandle> handle =
       std::make_unique<NiceMock<content::MockNavigationHandle>>(contents);
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index 0c2d0c38..37911fa 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -4,14 +4,27 @@
 
 #include "chrome/browser/web_applications/app_service/web_app_publisher_helper.h"
 
+#include <atomic>
+#include <ostream>
+#include <set>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/check.h"
+#include "base/check_op.h"
 #include "base/containers/contains.h"
 #include "base/containers/extend.h"
 #include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_base.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
@@ -26,13 +39,12 @@
 #include "chrome/browser/ui/web_applications/web_app_launch_manager.h"
 #include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h"
 #include "chrome/browser/web_applications/commands/run_on_os_login_command.h"
+#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
-#include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
-#include "chrome/browser/web_applications/web_app_prefs_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
@@ -45,6 +57,7 @@
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/cpp/publisher_base.h"
 #include "components/services/app_service/public/cpp/run_on_os_login_types.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "content/public/browser/clear_site_data_utils.h"
 #include "third_party/blink/public/mojom/manifest/capture_links.mojom.h"
 #include "ui/base/window_open_disposition.h"
@@ -66,6 +79,7 @@
 #include "ash/constants/ash_features.h"
 #include "chrome/browser/ash/crostini/crostini_terminal.h"
 #include "chrome/browser/ash/crostini/crostini_util.h"
+#include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/arc/arc_web_contents_data.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
@@ -82,8 +96,14 @@
 
 using apps::IconEffects;
 
+namespace content {
+class BrowserContext;
+}
+
 namespace web_app {
 
+class WebAppInstallManager;
+
 namespace {
 
 // Only supporting important permissions for now.
@@ -499,18 +519,8 @@
   DCHECK_EQ(web_app->IsSystemApp(),
             app->install_reason == apps::InstallReason::kSystem);
 
-  GURL install_url;
-  if (registrar().HasExternalAppWithInstallSource(
-          web_app->app_id(), ExternalInstallSource::kExternalPolicy)) {
-    std::map<AppId, GURL> installed_apps =
-        registrar().GetExternallyInstalledApps(
-            ExternalInstallSource::kExternalPolicy);
-    auto it = installed_apps.find(web_app->app_id());
-    if (it != installed_apps.end()) {
-      install_url = it->second;
-    }
-  }
-  app->policy_id = install_url.spec();
+  app->policy_id = GetPolicyId(*web_app);
+
   app->permissions = CreatePermissions(web_app);
 
   SetWebAppShowInFields(web_app, *app);
@@ -581,18 +591,7 @@
   app->install_source = ConvertInstallSourceToMojom(
       provider_->registrar().GetAppInstallSourceForMetrics(web_app->app_id()));
 
-  GURL install_url;
-  if (registrar().HasExternalAppWithInstallSource(
-          web_app->app_id(), ExternalInstallSource::kExternalPolicy)) {
-    std::map<AppId, GURL> installed_apps =
-        registrar().GetExternallyInstalledApps(
-            ExternalInstallSource::kExternalPolicy);
-    auto it = installed_apps.find(web_app->app_id());
-    if (it != installed_apps.end()) {
-      install_url = it->second;
-    }
-  }
-  app->policy_id = install_url.spec();
+  app->policy_id = GetPolicyId(*web_app);
 
   // For system web apps (only), the install source is |kSystem|.
   DCHECK_EQ(web_app->IsSystemApp(),
@@ -1606,6 +1605,29 @@
   std::move(callback).Run(LaunchAppWithParams(std::move(params)));
 }
 
+std::string WebAppPublisherHelper::GetPolicyId(const WebApp& web_app) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // File Manager SWA uses File Manager Extension's ID for policy.
+  if (chromeos::features::IsFileManagerSwaEnabled() &&
+      web_app.app_id() == file_manager::kFileManagerSwaAppId) {
+    return file_manager::kFileManagerAppId;
+  }
+#endif
+
+  GURL install_url;
+  if (registrar().HasExternalAppWithInstallSource(
+          web_app.app_id(), ExternalInstallSource::kExternalPolicy)) {
+    std::map<AppId, GURL> installed_apps =
+        registrar().GetExternallyInstalledApps(
+            ExternalInstallSource::kExternalPolicy);
+    auto it = installed_apps.find(web_app.app_id());
+    if (it != installed_apps.end()) {
+      install_url = it->second;
+    }
+  }
+  return install_url.spec();
+}
+
 #if BUILDFLAG(IS_CHROMEOS)
 void WebAppPublisherHelper::UpdateAppDisabledMode(apps::App& app) {
   if (provider_->policy_manager().IsDisabledAppsModeHidden()) {
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
index 7ab8162d..d9b19d1 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
@@ -5,22 +5,28 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_
 
+#include <stdint.h>
 #include <map>
 #include <memory>
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/types/id_type.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h"
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
-#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/paused_apps.h"
 #include "chrome/browser/web_applications/app_registrar_observer.h"
+#include "chrome/browser/web_applications/web_app_constants.h"
+#include "chrome/browser/web_applications/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_install_manager_observer.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
@@ -30,9 +36,13 @@
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/cpp/permission.h"
+#include "components/services/app_service/public/cpp/run_on_os_login_types.h"
 #include "components/services/app_service/public/mojom/app_service.mojom.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "components/services/app_service/public/mojom/types.mojom-forward.h"
+#include "components/services/app_service/public/mojom/types.mojom-shared.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "ui/gfx/native_widget_types.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
@@ -44,11 +54,24 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/notifications/notification_common.h"
 #include "chrome/browser/notifications/notification_display_service.h"
+#include "content/public/browser/media_request_state.h"
+#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "ui/message_center/public/cpp/notification.h"
 #endif
 
+class ContentSettingsPattern;
+class ContentSettingsTypeSet;
+class GURL;
 class Profile;
 
+namespace apps {
+struct AppLaunchParams;
+}
+
+namespace base {
+class Time;
+}
+
 namespace content {
 class WebContents;
 }
@@ -57,7 +80,6 @@
 
 class WebApp;
 class WebAppProvider;
-class WebAppRegistrar;
 class WebAppLaunchManager;
 
 struct ShortcutIdTypeMarker {};
@@ -360,6 +382,10 @@
       int64_t display_id,
       base::OnceCallback<void(content::WebContents*)> callback);
 
+  // Get the identifier for the app that will be used in policy controls, such
+  // as force-installation and pinning. May be empty.
+  std::string GetPolicyId(const WebApp& web_app);
+
 #if BUILDFLAG(IS_CHROMEOS)
   // Updates app visibility.
   void UpdateAppDisabledMode(apps::App& app);
diff --git a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
index c70166c..59696c6 100644
--- a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
@@ -29,7 +29,6 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/views/image_model_utils.h"
 #include "url/gurl.h"
 
 namespace web_app {
@@ -135,17 +134,15 @@
   controller->SetReadIconCallbackForTesting(
       base::BindLambdaForTesting([controller, &image_skia, &run_loop, this]() {
         EXPECT_TRUE(app_service_test().AreIconImageEqual(
-            image_skia, views::GetImageSkiaFromImageModel(
-                            controller->GetWindowAppIcon(), nullptr)));
+            image_skia, controller->GetWindowAppIcon().Rasterize(nullptr)));
         run_loop.Quit();
       }));
   run_loop.Run();
 #else
   controller->SetReadIconCallbackForTesting(
       base::BindLambdaForTesting([controller, &run_loop]() {
-        const SkBitmap* bitmap = views::GetImageSkiaFromImageModel(
-                                     controller->GetWindowAppIcon(), nullptr)
-                                     .bitmap();
+        const SkBitmap* bitmap =
+            controller->GetWindowAppIcon().Rasterize(nullptr).bitmap();
         EXPECT_EQ(SK_ColorBLUE, bitmap->getColor(0, 0));
         EXPECT_EQ(32, bitmap->width());
         EXPECT_EQ(32, bitmap->height());
diff --git a/chrome/browser/win/chrome_elf_init.cc b/chrome/browser/win/chrome_elf_init.cc
index 1f77232..e9c6998 100644
--- a/chrome/browser/win/chrome_elf_init.cc
+++ b/chrome/browser/win/chrome_elf_init.cc
@@ -26,8 +26,8 @@
 #include "content/public/common/content_features.h"
 #include "sandbox/policy/features.h"
 
-const char kBrowserBlacklistTrialName[] = "BrowserBlacklist";
-const char kBrowserBlacklistTrialDisabledGroupName[] = "NoBlacklist";
+const char kBrowserBlocklistTrialName[] = "BrowserBlocklist";
+const char kBrowserBlocklistTrialDisabledGroupName[] = "NoBlocklist";
 
 namespace {
 
@@ -35,40 +35,40 @@
 // Hence,
 //   (a) existing enumerated constants should never be deleted or reordered, and
 //   (b) new constants should only be appended in front of
-//       BLACKLIST_SETUP_EVENT_MAX.
-enum BlacklistSetupEventType {
-  // The blacklist beacon has placed to enable the browser blacklisting.
-  BLACKLIST_SETUP_ENABLED = 0,
+//       BLOCKLIST_SETUP_EVENT_MAX.
+enum BlocklistSetupEventType {
+  // The blocklist beacon has placed to enable the browser blocklisting.
+  BLOCKLIST_SETUP_ENABLED = 0,
 
-  // The blacklist was successfully enabled.
-  BLACKLIST_SETUP_RAN_SUCCESSFULLY,
+  // The blocklist was successfully enabled.
+  BLOCKLIST_SETUP_RAN_SUCCESSFULLY,
 
-  // The blacklist setup code failed to execute.
-  BLACKLIST_SETUP_FAILED,
+  // The blocklist setup code failed to execute.
+  BLOCKLIST_SETUP_FAILED,
 
-  // The blacklist thunk setup code failed. This is probably an indication
+  // The blocklist thunk setup code failed. This is probably an indication
   // that something else patched that code first.
-  BLACKLIST_THUNK_SETUP_FAILED,
+  BLOCKLIST_THUNK_SETUP_FAILED,
 
-  // Deprecated. The blacklist interception code failed to execute.
-  BLACKLIST_INTERCEPTION_FAILED,
+  // Deprecated. The blocklist interception code failed to execute.
+  BLOCKLIST_INTERCEPTION_FAILED,
 
-  // The blacklist was disabled for this run (after it failed too many times).
-  BLACKLIST_SETUP_DISABLED,
+  // The blocklist was disabled for this run (after it failed too many times).
+  BLOCKLIST_SETUP_DISABLED,
 
   // Always keep this at the end.
-  BLACKLIST_SETUP_EVENT_MAX,
+  BLOCKLIST_SETUP_EVENT_MAX,
 };
 
-void RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event) {
+void RecordBlocklistSetupEvent(BlocklistSetupEventType blocklist_setup_event) {
   base::UmaHistogramEnumeration("ChromeElf.Beacon.SetupStatus",
-                                blacklist_setup_event,
-                                BLACKLIST_SETUP_EVENT_MAX);
+                                blocklist_setup_event,
+                                BLOCKLIST_SETUP_EVENT_MAX);
 }
 
 std::wstring GetBeaconRegistryPath() {
   return install_static::GetRegistryPath().append(
-      blacklist::kRegistryBeaconKeyName);
+      blocklist::kRegistryBeaconKeyName);
 }
 
 // This enum is used to define the buckets for an enumerated UMA histogram.
@@ -119,13 +119,13 @@
 }  // namespace
 
 void InitializeChromeElf() {
-  if (base::FieldTrialList::FindFullName(kBrowserBlacklistTrialName) ==
-      kBrowserBlacklistTrialDisabledGroupName) {
-    // Disable the blacklist for all future runs by removing the beacon.
-    base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER);
-    blacklist_registry_key.DeleteKey(GetBeaconRegistryPath().c_str());
+  if (base::FieldTrialList::FindFullName(kBrowserBlocklistTrialName) ==
+      kBrowserBlocklistTrialDisabledGroupName) {
+    // Disable the blocklist for all future runs by removing the beacon.
+    base::win::RegKey blocklist_registry_key(HKEY_CURRENT_USER);
+    blocklist_registry_key.DeleteKey(GetBeaconRegistryPath().c_str());
   } else {
-    BrowserBlacklistBeaconSetup();
+    BrowserBlocklistBeaconSetup();
   }
 
   // Make sure the registry key we read earlier in startup
@@ -157,69 +157,69 @@
   }
 }
 
-void BrowserBlacklistBeaconSetup() {
-  base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER,
+void BrowserBlocklistBeaconSetup() {
+  base::win::RegKey blocklist_registry_key(HKEY_CURRENT_USER,
                                            GetBeaconRegistryPath().c_str(),
                                            KEY_QUERY_VALUE | KEY_SET_VALUE);
 
   // No point in trying to continue if the registry key isn't valid.
-  if (!blacklist_registry_key.Valid())
+  if (!blocklist_registry_key.Valid())
     return;
 
-  // Record the results of the last blacklist setup.
-  DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
-  blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state);
+  // Record the results of the last blocklist setup.
+  DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX;
+  blocklist_registry_key.ReadValueDW(blocklist::kBeaconState, &blocklist_state);
 
-  if (blacklist_state == blacklist::BLACKLIST_ENABLED) {
-    // The blacklist setup didn't crash, so we report if it was enabled or not.
+  if (blocklist_state == blocklist::BLOCKLIST_ENABLED) {
+    // The blocklist setup didn't crash, so we report if it was enabled or not.
     if (IsThirdPartyInitialized()) {
-      RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY);
+      RecordBlocklistSetupEvent(BLOCKLIST_SETUP_RAN_SUCCESSFULLY);
     } else {
-      // The only way for the blacklist to be enabled, but not fully
-      // initialized is if the thunk setup failed. See blacklist.cc
+      // The only way for the blocklist to be enabled, but not fully
+      // initialized is if the thunk setup failed. See blocklist.cc
       // for more details.
-      RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED);
+      RecordBlocklistSetupEvent(BLOCKLIST_THUNK_SETUP_FAILED);
     }
 
-    // Regardless of if the blacklist was fully enabled or not, report how many
+    // Regardless of if the blocklist was fully enabled or not, report how many
     // times we had to try to set it up.
     DWORD attempt_count = 0;
-    blacklist_registry_key.ReadValueDW(blacklist::kBeaconAttemptCount,
+    blocklist_registry_key.ReadValueDW(blocklist::kBeaconAttemptCount,
                                        &attempt_count);
     base::UmaHistogramCounts100("ChromeElf.Beacon.RetryAttemptsBeforeSuccess",
                                 attempt_count);
-  } else if (blacklist_state == blacklist::BLACKLIST_SETUP_FAILED) {
+  } else if (blocklist_state == blocklist::BLOCKLIST_SETUP_FAILED) {
     // We can set the state to disabled without checking that the maximum number
-    // of attempts was exceeded because blacklist.cc has already done this.
-    RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED);
-    blacklist_registry_key.WriteValue(blacklist::kBeaconState,
-                                      blacklist::BLACKLIST_DISABLED);
-  } else if (blacklist_state == blacklist::BLACKLIST_DISABLED) {
-    RecordBlacklistSetupEvent(BLACKLIST_SETUP_DISABLED);
+    // of attempts was exceeded because blocklist.cc has already done this.
+    RecordBlocklistSetupEvent(BLOCKLIST_SETUP_FAILED);
+    blocklist_registry_key.WriteValue(blocklist::kBeaconState,
+                                      blocklist::BLOCKLIST_DISABLED);
+  } else if (blocklist_state == blocklist::BLOCKLIST_DISABLED) {
+    RecordBlocklistSetupEvent(BLOCKLIST_SETUP_DISABLED);
   }
 
-  // Find the last recorded blacklist version.
-  std::wstring blacklist_version;
-  blacklist_registry_key.ReadValue(blacklist::kBeaconVersion,
-                                   &blacklist_version);
+  // Find the last recorded blocklist version.
+  std::wstring blocklist_version;
+  blocklist_registry_key.ReadValue(blocklist::kBeaconVersion,
+                                   &blocklist_version);
 
-  if (blacklist_version != TEXT(CHROME_VERSION_STRING)) {
-    // The blacklist hasn't been enabled for this version yet, so enable it
+  if (blocklist_version != TEXT(CHROME_VERSION_STRING)) {
+    // The blocklist hasn't been enabled for this version yet, so enable it
     // and reset the failure count to zero.
-    LONG set_version = blacklist_registry_key.WriteValue(
-        blacklist::kBeaconVersion,
+    LONG set_version = blocklist_registry_key.WriteValue(
+        blocklist::kBeaconVersion,
         TEXT(CHROME_VERSION_STRING));
 
-    LONG set_state = blacklist_registry_key.WriteValue(
-        blacklist::kBeaconState,
-        blacklist::BLACKLIST_ENABLED);
+    LONG set_state = blocklist_registry_key.WriteValue(
+        blocklist::kBeaconState,
+        blocklist::BLOCKLIST_ENABLED);
 
-    blacklist_registry_key.WriteValue(blacklist::kBeaconAttemptCount,
+    blocklist_registry_key.WriteValue(blocklist::kBeaconAttemptCount,
                                       static_cast<DWORD>(0));
 
-    // Only report the blacklist as getting setup when both registry writes
-    // succeed, since otherwise the blacklist wasn't properly setup.
+    // Only report the blocklist as getting setup when both registry writes
+    // succeed, since otherwise the blocklist wasn't properly setup.
     if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS)
-      RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED);
+      RecordBlocklistSetupEvent(BLOCKLIST_SETUP_ENABLED);
   }
 }
diff --git a/chrome/browser/win/chrome_elf_init.h b/chrome/browser/win/chrome_elf_init.h
index f598828..19660b7 100644
--- a/chrome/browser/win/chrome_elf_init.h
+++ b/chrome/browser/win/chrome_elf_init.h
@@ -6,14 +6,14 @@
 #define CHROME_BROWSER_WIN_CHROME_ELF_INIT_H_
 
 // Field trial name and full name for the blacklist disabled group.
-extern const char kBrowserBlacklistTrialName[];
-extern const char kBrowserBlacklistTrialDisabledGroupName[];
+extern const char kBrowserBlocklistTrialName[];
+extern const char kBrowserBlocklistTrialDisabledGroupName[];
 
 // Prepare any initialization code for Chrome Elf's setup (This will generally
 // only affect future runs since Chrome Elf is already setup by this point).
 void InitializeChromeElf();
 
 // Set the required state for an enabled browser blacklist.
-void BrowserBlacklistBeaconSetup();
+void BrowserBlocklistBeaconSetup();
 
 #endif  // CHROME_BROWSER_WIN_CHROME_ELF_INIT_H_
diff --git a/chrome/browser/win/chrome_elf_init_unittest.cc b/chrome/browser/win/chrome_elf_init_unittest.cc
index 35f101c..25490b3 100644
--- a/chrome/browser/win/chrome_elf_init_unittest.cc
+++ b/chrome/browser/win/chrome_elf_init_unittest.cc
@@ -20,14 +20,14 @@
 
 namespace {
 
-class ChromeBlacklistTrialTest : public testing::Test {
+class ChromeBlocklistTrialTest : public testing::Test {
  public:
-  ChromeBlacklistTrialTest(const ChromeBlacklistTrialTest&) = delete;
-  ChromeBlacklistTrialTest& operator=(const ChromeBlacklistTrialTest&) = delete;
+  ChromeBlocklistTrialTest(const ChromeBlocklistTrialTest&) = delete;
+  ChromeBlocklistTrialTest& operator=(const ChromeBlocklistTrialTest&) = delete;
 
  protected:
-  ChromeBlacklistTrialTest() {}
-  ~ChromeBlacklistTrialTest() override {}
+  ChromeBlocklistTrialTest() {}
+  ~ChromeBlocklistTrialTest() override {}
 
   void SetUp() override {
     testing::Test::SetUp();
@@ -35,125 +35,125 @@
     ASSERT_NO_FATAL_FAILURE(
         override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
 
-    blacklist_registry_key_ = std::make_unique<base::win::RegKey>(
+    blocklist_registry_key_ = std::make_unique<base::win::RegKey>(
         HKEY_CURRENT_USER,
         install_static::GetRegistryPath()
-            .append(blacklist::kRegistryBeaconKeyName)
+            .append(blocklist::kRegistryBeaconKeyName)
             .c_str(),
         KEY_QUERY_VALUE | KEY_SET_VALUE);
   }
 
-  DWORD GetBlacklistState() {
-    DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
-    blacklist_registry_key_->ReadValueDW(blacklist::kBeaconState,
-                                         &blacklist_state);
+  DWORD GetBlocklistState() {
+    DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX;
+    blocklist_registry_key_->ReadValueDW(blocklist::kBeaconState,
+                                         &blocklist_state);
 
-    return blacklist_state;
+    return blocklist_state;
   }
 
-  std::wstring GetBlacklistVersion() {
-    std::wstring blacklist_version;
-    blacklist_registry_key_->ReadValue(blacklist::kBeaconVersion,
-                                       &blacklist_version);
+  std::wstring GetBlocklistVersion() {
+    std::wstring blocklist_version;
+    blocklist_registry_key_->ReadValue(blocklist::kBeaconVersion,
+                                       &blocklist_version);
 
-    return blacklist_version;
+    return blocklist_version;
   }
 
-  std::unique_ptr<base::win::RegKey> blacklist_registry_key_;
+  std::unique_ptr<base::win::RegKey> blocklist_registry_key_;
   registry_util::RegistryOverrideManager override_manager_;
   content::BrowserTaskEnvironment task_environment_;
 };
 
-// Ensure that the default trial sets up the blacklist beacons.
-TEST_F(ChromeBlacklistTrialTest, DefaultRun) {
+// Ensure that the default trial sets up the blocklist beacons.
+TEST_F(ChromeBlocklistTrialTest, DefaultRun) {
   // Set some dummy values as beacons.
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconState,
-                                      blacklist::BLACKLIST_DISABLED);
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion, L"Data");
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconState,
+                                      blocklist::BLOCKLIST_DISABLED);
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion, L"Data");
 
   // This setup code should result in the default group, which should have
-  // the blacklist set up.
+  // the blocklist set up.
   InitializeChromeElf();
 
   // Ensure the beacon values are now correct, indicating the
-  // blacklist beacon was setup.
-  ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_ENABLED),
-            GetBlacklistState());
+  // blocklist beacon was setup.
+  ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_ENABLED),
+            GetBlocklistState());
   std::wstring version(base::UTF8ToWide(version_info::GetVersionNumber()));
-  ASSERT_EQ(version, GetBlacklistVersion());
+  ASSERT_EQ(version, GetBlocklistVersion());
 }
 
-// Ensure that the blacklist is disabled for any users in the
-// "BlacklistDisabled" finch group.
-TEST_F(ChromeBlacklistTrialTest, BlacklistDisabledRun) {
+// Ensure that the blocklist is disabled for any users in the
+// "BlocklistDisabled" finch group.
+TEST_F(ChromeBlocklistTrialTest, BlocklistDisabledRun) {
   // Set the beacons to enabled values.
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconState,
-                                      blacklist::BLACKLIST_ENABLED);
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion, L"Data");
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconState,
+                                      blocklist::BLOCKLIST_ENABLED);
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion, L"Data");
 
   scoped_refptr<base::FieldTrial> trial(
     base::FieldTrialList::CreateFieldTrial(
-      kBrowserBlacklistTrialName, kBrowserBlacklistTrialDisabledGroupName));
+      kBrowserBlocklistTrialName, kBrowserBlocklistTrialDisabledGroupName));
 
-  // This setup code should now delete any existing blacklist beacons.
+  // This setup code should now delete any existing blocklist beacons.
   InitializeChromeElf();
 
   // Ensure invalid values are returned to indicate that the beacon
   // values are indeed gone.
-  ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_STATE_MAX),
-            GetBlacklistState());
-  ASSERT_EQ(std::wstring(), GetBlacklistVersion());
+  ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_STATE_MAX),
+            GetBlocklistState());
+  ASSERT_EQ(std::wstring(), GetBlocklistVersion());
 }
 
-TEST_F(ChromeBlacklistTrialTest, VerifyFirstRun) {
-  BrowserBlacklistBeaconSetup();
+TEST_F(ChromeBlocklistTrialTest, VerifyFirstRun) {
+  BrowserBlocklistBeaconSetup();
 
   // Verify the state is properly set after the first run.
-  ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_ENABLED),
-            GetBlacklistState());
+  ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_ENABLED),
+            GetBlocklistState());
 
   std::wstring version(base::UTF8ToWide(version_info::GetVersionNumber()));
-  ASSERT_EQ(version, GetBlacklistVersion());
+  ASSERT_EQ(version, GetBlocklistVersion());
 }
 
-TEST_F(ChromeBlacklistTrialTest, BlacklistFailed) {
-  // Ensure when the blacklist set up failed we set the state to disabled for
+TEST_F(ChromeBlocklistTrialTest, BlocklistFailed) {
+  // Ensure when the blocklist set up failed we set the state to disabled for
   // future runs.
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion,
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion,
                                       TEXT(CHROME_VERSION_STRING));
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconState,
-                                      blacklist::BLACKLIST_SETUP_FAILED);
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconState,
+                                      blocklist::BLOCKLIST_SETUP_FAILED);
 
-  BrowserBlacklistBeaconSetup();
+  BrowserBlocklistBeaconSetup();
 
-  ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_DISABLED),
-            GetBlacklistState());
+  ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_DISABLED),
+            GetBlocklistState());
 }
 
-TEST_F(ChromeBlacklistTrialTest, VersionChanged) {
-  // Mark the blacklist as disabled for an older version, it should
+TEST_F(ChromeBlocklistTrialTest, VersionChanged) {
+  // Mark the blocklist as disabled for an older version, it should
   // get enabled for this new version.  Also record a non-zero number of
   // setup failures, which should be reset to zero.
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconVersion,
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconVersion,
                                       L"old_version");
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconState,
-                                      blacklist::BLACKLIST_DISABLED);
-  blacklist_registry_key_->WriteValue(blacklist::kBeaconAttemptCount,
-                                      blacklist::kBeaconMaxAttempts);
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconState,
+                                      blocklist::BLOCKLIST_DISABLED);
+  blocklist_registry_key_->WriteValue(blocklist::kBeaconAttemptCount,
+                                      blocklist::kBeaconMaxAttempts);
 
-  BrowserBlacklistBeaconSetup();
+  BrowserBlocklistBeaconSetup();
 
   // The beacon should now be marked as enabled for the current version.
-  ASSERT_EQ(static_cast<DWORD>(blacklist::BLACKLIST_ENABLED),
-            GetBlacklistState());
+  ASSERT_EQ(static_cast<DWORD>(blocklist::BLOCKLIST_ENABLED),
+            GetBlocklistState());
 
   std::wstring expected_version(
       base::UTF8ToWide(version_info::GetVersionNumber()));
-  ASSERT_EQ(expected_version, GetBlacklistVersion());
+  ASSERT_EQ(expected_version, GetBlocklistVersion());
 
   // The counter should be reset.
-  DWORD attempt_count = blacklist::kBeaconMaxAttempts;
-  blacklist_registry_key_->ReadValueDW(blacklist::kBeaconAttemptCount,
+  DWORD attempt_count = blocklist::kBeaconMaxAttempts;
+  blocklist_registry_key_->ReadValueDW(blocklist::kBeaconAttemptCount,
                                        &attempt_count);
   ASSERT_EQ(static_cast<DWORD>(0), attempt_count);
 }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 222d329..7d7c79f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1647539747-03593157d39498fa2c27de59ce8a69f2ad2dc181.profdata
+chrome-linux-main-1647604379-4659adbcb89e3313f9b9bb5d6a35e7f4c78d7573.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 072a88e..d1af8a3 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1647518309-2f3a06193beb03804c4ef7e2b1de409100653ccf.profdata
+chrome-mac-arm-main-1647604379-78700c750a15a2472b8201eff336583a43ad72ff.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index b9b1ccd0..da0b99d 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1647518309-3ba1408f248e578199edd78d33c40ef3480f390a.profdata
+chrome-mac-main-1647604379-24bb3d5266972ce87702b39862f29f6a869a3062.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index d75b41d..c40c12e 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1647529177-0c0f4c7a803ac1e2ee193e2ed924338211e72623.profdata
+chrome-win32-main-1647561516-884869d651bf583160193923673428f3dc81c1bc.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4fd3b21a..a35249b 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1647529177-8d2c74d53e6f0c6ecd578ad9eab3ddf9d6e0bb1f.profdata
+chrome-win64-main-1647561516-63c50e8a42320ad1820259d4ee943133030ba3e7.profdata
diff --git a/chrome/chrome_elf/chrome_elf_constants.cc b/chrome/chrome_elf/chrome_elf_constants.cc
index 91ea5c4f..0f720ac8 100644
--- a/chrome/chrome_elf/chrome_elf_constants.cc
+++ b/chrome/chrome_elf/chrome_elf_constants.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/chrome_elf/chrome_elf_constants.h"
 
-namespace blacklist {
+namespace blocklist {
 
 const wchar_t kRegistryBeaconKeyName[] = L"\\BLBeacon";
 const wchar_t kBeaconVersion[] = L"version";
@@ -13,7 +13,7 @@
 
 const DWORD kBeaconMaxAttempts = 2;
 
-}  // namespace blacklist
+}  // namespace blocklist
 
 namespace elf_sec {
 
diff --git a/chrome/chrome_elf/chrome_elf_constants.h b/chrome/chrome_elf/chrome_elf_constants.h
index d10904b..c3e1311 100644
--- a/chrome/chrome_elf/chrome_elf_constants.h
+++ b/chrome/chrome_elf/chrome_elf_constants.h
@@ -9,34 +9,34 @@
 
 #include <windows.h>
 
-namespace blacklist {
+namespace blocklist {
 
-// The name of the blacklist beacon registry key.
+// The name of the blocklist beacon registry key.
 extern const wchar_t kRegistryBeaconKeyName[];
 
-// The properties for the blacklist beacon.
+// The properties for the blocklist beacon.
 extern const wchar_t kBeaconVersion[];
 extern const wchar_t kBeaconState[];
 extern const wchar_t kBeaconAttemptCount[];
 
 // The number of failures that can occur on startup with the beacon enabled
-// before we give up and turn off the blacklist.
+// before we give up and turn off the blocklist.
 extern const DWORD kBeaconMaxAttempts;
 
-// The states for the blacklist setup code.
-enum BlacklistState {
-  BLACKLIST_DISABLED = 0,
-  BLACKLIST_ENABLED,
-  // The blacklist setup code is running. If this is the state at startup, it
+// The states for the blocklist setup code.
+enum BlocklistState {
+  BLOCKLIST_DISABLED = 0,
+  BLOCKLIST_ENABLED,
+  // The blocklist setup code is running. If this is the state at startup, it
   // means the last setup crashed.
-  BLACKLIST_SETUP_RUNNING,
+  BLOCKLIST_SETUP_RUNNING,
   // If the last setup crashed, we reassign the state to failed.
-  BLACKLIST_SETUP_FAILED,
+  BLOCKLIST_SETUP_FAILED,
   // Always keep this at the end.
-  BLACKLIST_STATE_MAX,
+  BLOCKLIST_STATE_MAX,
 };
 
-}  // namespace blacklist
+}  // namespace blocklist
 
 namespace elf_sec {
 
diff --git a/chrome/chrome_elf/third_party_dlls/beacon.cc b/chrome/chrome_elf/third_party_dlls/beacon.cc
index 41a7bf1..0970103 100644
--- a/chrome/chrome_elf/third_party_dlls/beacon.cc
+++ b/chrome/chrome_elf/third_party_dlls/beacon.cc
@@ -15,16 +15,16 @@
 
   if (!nt::CreateRegKey(nt::HKCU,
                         install_static::GetRegistryPath()
-                            .append(blacklist::kRegistryBeaconKeyName)
+                            .append(blocklist::kRegistryBeaconKeyName)
                             .c_str(),
                         KEY_QUERY_VALUE | KEY_SET_VALUE, &key_handle)) {
     return false;
   }
 
-  DWORD blocking_state = blacklist::BLACKLIST_STATE_MAX;
-  if (!nt::QueryRegValueDWORD(key_handle, blacklist::kBeaconState,
+  DWORD blocking_state = blocklist::BLOCKLIST_STATE_MAX;
+  if (!nt::QueryRegValueDWORD(key_handle, blocklist::kBeaconState,
                               &blocking_state) ||
-      blocking_state == blacklist::BLACKLIST_DISABLED) {
+      blocking_state == blocklist::BLOCKLIST_DISABLED) {
     nt::CloseRegKey(key_handle);
     return false;
   }
@@ -32,30 +32,30 @@
   // Handle attempt count.
   // Only return true if BL is enabled and succeeded on previous run.
   bool success = false;
-  if (blocking_state == blacklist::BLACKLIST_ENABLED) {
+  if (blocking_state == blocklist::BLOCKLIST_ENABLED) {
     // If the blocking was successfully initialized on the previous run, reset
     // the failure counter. Then update the beacon state.
-    if (nt::SetRegValueDWORD(key_handle, blacklist::kBeaconAttemptCount,
+    if (nt::SetRegValueDWORD(key_handle, blocklist::kBeaconAttemptCount,
                              static_cast<DWORD>(0))) {
-      if (nt::SetRegValueDWORD(key_handle, blacklist::kBeaconState,
-                               blacklist::BLACKLIST_SETUP_RUNNING))
+      if (nt::SetRegValueDWORD(key_handle, blocklist::kBeaconState,
+                               blocklist::BLOCKLIST_SETUP_RUNNING))
         success = true;
     }
   } else {
     // Some part of the blocking setup failed last time. If this has occurred
-    // blacklist::kBeaconMaxAttempts times in a row, we switch the state to
+    // blocklist::kBeaconMaxAttempts times in a row, we switch the state to
     // failed and skip setting up the blocking.
     DWORD attempt_count = 0;
 
-    nt::QueryRegValueDWORD(key_handle, blacklist::kBeaconAttemptCount,
+    nt::QueryRegValueDWORD(key_handle, blocklist::kBeaconAttemptCount,
                            &attempt_count);
     ++attempt_count;
-    nt::SetRegValueDWORD(key_handle, blacklist::kBeaconAttemptCount,
+    nt::SetRegValueDWORD(key_handle, blocklist::kBeaconAttemptCount,
                          attempt_count);
 
-    if (attempt_count >= blacklist::kBeaconMaxAttempts) {
-      blocking_state = blacklist::BLACKLIST_SETUP_FAILED;
-      nt::SetRegValueDWORD(key_handle, blacklist::kBeaconState, blocking_state);
+    if (attempt_count >= blocklist::kBeaconMaxAttempts) {
+      blocking_state = blocklist::BLOCKLIST_SETUP_FAILED;
+      nt::SetRegValueDWORD(key_handle, blocklist::kBeaconState, blocking_state);
     }
   }
 
@@ -68,14 +68,14 @@
 
   if (!nt::CreateRegKey(nt::HKCU,
                         install_static::GetRegistryPath()
-                            .append(blacklist::kRegistryBeaconKeyName)
+                            .append(blocklist::kRegistryBeaconKeyName)
                             .c_str(),
                         KEY_QUERY_VALUE | KEY_SET_VALUE, &key_handle)) {
     return false;
   }
 
-  DWORD blocking_state = blacklist::BLACKLIST_STATE_MAX;
-  if (!nt::QueryRegValueDWORD(key_handle, blacklist::kBeaconState,
+  DWORD blocking_state = blocklist::BLOCKLIST_STATE_MAX;
+  if (!nt::QueryRegValueDWORD(key_handle, blocklist::kBeaconState,
                               &blocking_state)) {
     nt::CloseRegKey(key_handle);
     return false;
@@ -84,9 +84,9 @@
   // Reaching this point with the setup running state means the setup did not
   // crash, so we reset to enabled.  Any other state indicates that setup was
   // skipped; in that case we leave the state alone for later recording.
-  if (blocking_state == blacklist::BLACKLIST_SETUP_RUNNING) {
-    if (!nt::SetRegValueDWORD(key_handle, blacklist::kBeaconState,
-                              blacklist::BLACKLIST_ENABLED)) {
+  if (blocking_state == blocklist::BLOCKLIST_SETUP_RUNNING) {
+    if (!nt::SetRegValueDWORD(key_handle, blocklist::kBeaconState,
+                              blocklist::BLOCKLIST_ENABLED)) {
       nt::CloseRegKey(key_handle);
       return false;
     }
diff --git a/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc b/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc
index ee73e0d..4767087 100644
--- a/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc
+++ b/chrome/chrome_elf/third_party_dlls/beacon_unittest.cc
@@ -37,7 +37,7 @@
     beacon_registry_key_ = std::make_unique<base::win::RegKey>(
         HKEY_CURRENT_USER,
         install_static::GetRegistryPath()
-            .append(blacklist::kRegistryBeaconKeyName)
+            .append(blocklist::kRegistryBeaconKeyName)
             .c_str(),
         KEY_QUERY_VALUE | KEY_SET_VALUE);
   }
@@ -56,10 +56,10 @@
 // Ensure that the beacon state starts off 'running' if a version is specified.
 TEST_F(BeaconTest, Beacon) {
   LONG result = beacon_registry_key_->WriteValue(
-      blacklist::kBeaconState, blacklist::BLACKLIST_SETUP_RUNNING);
+      blocklist::kBeaconState, blocklist::BLOCKLIST_SETUP_RUNNING);
   EXPECT_EQ(ERROR_SUCCESS, result);
 
-  result = beacon_registry_key_->WriteValue(blacklist::kBeaconVersion,
+  result = beacon_registry_key_->WriteValue(blocklist::kBeaconVersion,
                                             L"beacon_version");
   EXPECT_EQ(ERROR_SUCCESS, result);
 
@@ -73,76 +73,76 @@
 void TestResetBeacon(std::unique_ptr<base::win::RegKey>& key,
                      DWORD input_state,
                      DWORD expected_output_state) {
-  LONG result = key->WriteValue(blacklist::kBeaconState, input_state);
+  LONG result = key->WriteValue(blocklist::kBeaconState, input_state);
   EXPECT_EQ(ERROR_SUCCESS, result);
 
   EXPECT_TRUE(ResetBeacon());
-  DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
-  result = key->ReadValueDW(blacklist::kBeaconState, &blacklist_state);
+  DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX;
+  result = key->ReadValueDW(blocklist::kBeaconState, &blocklist_state);
   EXPECT_EQ(ERROR_SUCCESS, result);
-  EXPECT_EQ(expected_output_state, blacklist_state);
+  EXPECT_EQ(expected_output_state, blocklist_state);
 }
 
 TEST_F(BeaconTest, ResetBeacon) {
   // Ensure that ResetBeacon resets properly on successful runs and not on
   // failed or disabled runs.
-  TestResetBeacon(beacon_registry_key_, blacklist::BLACKLIST_SETUP_RUNNING,
-                  blacklist::BLACKLIST_ENABLED);
+  TestResetBeacon(beacon_registry_key_, blocklist::BLOCKLIST_SETUP_RUNNING,
+                  blocklist::BLOCKLIST_ENABLED);
 
-  TestResetBeacon(beacon_registry_key_, blacklist::BLACKLIST_SETUP_FAILED,
-                  blacklist::BLACKLIST_SETUP_FAILED);
+  TestResetBeacon(beacon_registry_key_, blocklist::BLOCKLIST_SETUP_FAILED,
+                  blocklist::BLOCKLIST_SETUP_FAILED);
 
-  TestResetBeacon(beacon_registry_key_, blacklist::BLACKLIST_DISABLED,
-                  blacklist::BLACKLIST_DISABLED);
+  TestResetBeacon(beacon_registry_key_, blocklist::BLOCKLIST_DISABLED,
+                  blocklist::BLOCKLIST_DISABLED);
 }
 
 TEST_F(BeaconTest, SetupFailed) {
   // Ensure that when the number of failed tries reaches the maximum allowed,
-  // the blacklist state is set to failed.
+  // the blocklist state is set to failed.
   LONG result = beacon_registry_key_->WriteValue(
-      blacklist::kBeaconState, blacklist::BLACKLIST_SETUP_RUNNING);
+      blocklist::kBeaconState, blocklist::BLOCKLIST_SETUP_RUNNING);
   EXPECT_EQ(ERROR_SUCCESS, result);
 
-  // Set the attempt count so that on the next failure the blacklist is
+  // Set the attempt count so that on the next failure the blocklist is
   // disabled.
-  result = beacon_registry_key_->WriteValue(blacklist::kBeaconAttemptCount,
-                                            blacklist::kBeaconMaxAttempts - 1);
+  result = beacon_registry_key_->WriteValue(blocklist::kBeaconAttemptCount,
+                                            blocklist::kBeaconMaxAttempts - 1);
   EXPECT_EQ(ERROR_SUCCESS, result);
 
   EXPECT_FALSE(LeaveSetupBeacon());
 
   DWORD attempt_count = 0;
-  beacon_registry_key_->ReadValueDW(blacklist::kBeaconAttemptCount,
+  beacon_registry_key_->ReadValueDW(blocklist::kBeaconAttemptCount,
                                     &attempt_count);
-  EXPECT_EQ(attempt_count, blacklist::kBeaconMaxAttempts);
+  EXPECT_EQ(attempt_count, blocklist::kBeaconMaxAttempts);
 
-  DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
-  result = beacon_registry_key_->ReadValueDW(blacklist::kBeaconState,
-                                             &blacklist_state);
+  DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX;
+  result = beacon_registry_key_->ReadValueDW(blocklist::kBeaconState,
+                                             &blocklist_state);
   EXPECT_EQ(ERROR_SUCCESS, result);
-  EXPECT_EQ(blacklist_state,
-            static_cast<DWORD>(blacklist::BLACKLIST_SETUP_FAILED));
+  EXPECT_EQ(blocklist_state,
+            static_cast<DWORD>(blocklist::BLOCKLIST_SETUP_FAILED));
 }
 
 TEST_F(BeaconTest, SetupSucceeded) {
   // Starting with the enabled beacon should result in the setup running state
   // and the attempt counter reset to zero.
-  LONG result = beacon_registry_key_->WriteValue(blacklist::kBeaconState,
-                                                 blacklist::BLACKLIST_ENABLED);
+  LONG result = beacon_registry_key_->WriteValue(blocklist::kBeaconState,
+                                                 blocklist::BLOCKLIST_ENABLED);
   EXPECT_EQ(ERROR_SUCCESS, result);
-  result = beacon_registry_key_->WriteValue(blacklist::kBeaconAttemptCount,
-                                            blacklist::kBeaconMaxAttempts);
+  result = beacon_registry_key_->WriteValue(blocklist::kBeaconAttemptCount,
+                                            blocklist::kBeaconMaxAttempts);
   EXPECT_EQ(ERROR_SUCCESS, result);
 
   EXPECT_TRUE(LeaveSetupBeacon());
 
-  DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
-  beacon_registry_key_->ReadValueDW(blacklist::kBeaconState, &blacklist_state);
-  EXPECT_EQ(blacklist_state,
-            static_cast<DWORD>(blacklist::BLACKLIST_SETUP_RUNNING));
+  DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX;
+  beacon_registry_key_->ReadValueDW(blocklist::kBeaconState, &blocklist_state);
+  EXPECT_EQ(blocklist_state,
+            static_cast<DWORD>(blocklist::BLOCKLIST_SETUP_RUNNING));
 
-  DWORD attempt_count = blacklist::kBeaconMaxAttempts;
-  beacon_registry_key_->ReadValueDW(blacklist::kBeaconAttemptCount,
+  DWORD attempt_count = blocklist::kBeaconMaxAttempts;
+  beacon_registry_key_->ReadValueDW(blocklist::kBeaconAttemptCount,
                                     &attempt_count);
   EXPECT_EQ(static_cast<DWORD>(0), attempt_count);
 }
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index b3f10c3..2ce4816f 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -639,11 +639,6 @@
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
-#if !BUILDFLAG(IS_ANDROID)
-const base::Feature kLinkCapturingUiUpdate{"LinkCapturingUiUpdate",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
 COMPONENT_EXPORT(CHROME_FEATURES)
 const base::Feature kLinuxLowMemoryMonitor{"LinuxLowMemoryMonitor",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 6baaf07..402b942a 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -440,11 +440,6 @@
 extern const base::Feature kKernelnextVMs;
 #endif
 
-#if !BUILDFLAG(IS_ANDROID)
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kLinkCapturingUiUpdate;
-#endif
-
 #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kLinuxLowMemoryMonitor;
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 0ca172f..c352572 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -49,7 +49,7 @@
 // Type of the mounted volume.
 enum VolumeType { drive, downloads, removable, archive, provided, mtp,
                   media_view, crostini, android_files, documents_provider,
-                  testing, smb, system_internal };
+                  testing, smb, system_internal, guest_os };
 
 // Device type. Available if this is removable volume.
 enum DeviceType { usb, sd, optical, mobile, unknown };
diff --git a/chrome/common/extensions/api/scripting.idl b/chrome/common/extensions/api/scripting.idl
index 550a519..e0e3532 100644
--- a/chrome/common/extensions/api/scripting.idl
+++ b/chrome/common/extensions/api/scripting.idl
@@ -31,6 +31,11 @@
     // of specific frames to inject into.
     long[]? frameIds;
 
+    // The <a href="https://developer.chrome.com/extensions/webNavigation#document_ids">IDs</a>
+    // of specific documentIds to inject into. This must not be set if
+    // <code>frameIds</code> is set.
+    [nodoc] DOMString[]? documentIds;
+
     // Whether the script should inject into all frames within the tab. Defaults
     // to false.
     // This must not be true if <code>frameIds</code> is specified.
@@ -96,6 +101,9 @@
 
     // The frame associated with the injection.
     long frameId;
+
+    // The document associated with the injection.
+    [nodoc] DOMString documentId;
   };
 
   // Describes a content script to be injected into a web page registered
diff --git a/chrome/common/media/cdm_registration.cc b/chrome/common/media/cdm_registration.cc
index 2fd0dc6..7a1d144 100644
--- a/chrome/common/media/cdm_registration.cc
+++ b/chrome/common/media/cdm_registration.cc
@@ -32,10 +32,14 @@
 #include "base/no_destructor.h"
 #include "components/cdm/common/cdm_manifest.h"
 #include "media/cdm/supported_audio_codecs.h"
-// TODO(crbug.com/663554): Needed for WIDEVINE_CDM_VERSION_STRING. Support
-// component updated CDM on all desktop platforms and remove this.
-// This file is In SHARED_INTERMEDIATE_DIR.
+// Needed for WIDEVINE_CDM_MIN_GLIBC_VERSION. This file is in
+// SHARED_INTERMEDIATE_DIR.
 #include "widevine_cdm_version.h"  // nogncheck
+// The following must be after widevine_cdm_version.h.
+#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
+#include <gnu/libc-version.h>
+#include "base/version.h"
+#endif  // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/common/media/component_widevine_cdm_hint_file_linux.h"
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -138,6 +142,15 @@
 
 void AddSoftwareSecureWidevine(std::vector<content::CdmInfo>* cdms) {
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
+  base::Version glibc_version(gnu_get_libc_version());
+  DCHECK(glibc_version.IsValid());
+  if (glibc_version < base::Version(WIDEVINE_CDM_MIN_GLIBC_VERSION)) {
+    LOG(WARNING) << "Widevine not registered because glibc version is too low";
+    return;
+  }
+#endif  // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
+
   // The Widevine CDM on Linux needs to be registered (and loaded) before the
   // zygote is locked down. The CDM can be found from the version bundled with
   // Chrome (if BUNDLE_WIDEVINE_CDM = true) and/or the version downloaded by
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 312ca326..9d36967 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -245,6 +245,7 @@
 const char kChromeUIReadLaterHost[] = "read-later.top-chrome";
 const char kChromeUIReadLaterURL[] = "chrome://read-later.top-chrome/";
 const char kChromeUIWebAppInternalsHost[] = "web-app-internals";
+const char kChromeUIWebUITestHost[] = "webui-test";
 #endif
 
 #if BUILDFLAG(PLATFORM_CFM)
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 54dc618..f6d491d 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -233,6 +233,7 @@
 extern const char kChromeUIReadLaterHost[];
 extern const char kChromeUIReadLaterURL[];
 extern const char kChromeUIWebAppInternalsHost[];
+extern const char kChromeUIWebUITestHost[];
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index d364d2d..6fc55684 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -275,6 +275,7 @@
     "common/postinst.include",
     "common/prerm.include",
     "common/repo.cron",
+    "common/repo_variables.include",
     "common/rpm.include",
     "common/rpmrepo.cron",
     "common/symlinks.include",
diff --git a/chrome/installer/linux/common/apt.include b/chrome/installer/linux/common/apt.include
index a9bfd76..5855311 100644
--- a/chrome/installer/linux/common/apt.include
+++ b/chrome/installer/linux/common/apt.include
@@ -1,4 +1,4 @@
-@@include@@variables.include
+@@include@@repo_variables.include
 
 APT_GET="`command -v apt-get 2> /dev/null`"
 APT_CONFIG="`command -v apt-config 2> /dev/null`"
diff --git a/chrome/installer/linux/common/postinst.include b/chrome/installer/linux/common/postinst.include
index 3a35f74..4e3023e 100644
--- a/chrome/installer/linux/common/postinst.include
+++ b/chrome/installer/linux/common/postinst.include
@@ -22,9 +22,9 @@
 update_defaults_list() {
   # $1: name of the .desktop file
 
-  local DEFAULTS_FILE="/usr/share/applications/defaults.list"
+  local DEFAULTS_LIST="/usr/share/applications/defaults.list"
 
-  if [ ! -f "${DEFAULTS_FILE}" ]; then
+  if [ ! -f "${DEFAULTS_LIST}" ]; then
     return
   fi
 
@@ -35,17 +35,17 @@
                 cut -d '=' -f 2- |
                 tr ';' ' ')"
   for mime_type in ${mime_types}; do
-    if egrep -q "^${mime_type}=" "${DEFAULTS_FILE}"; then
-      if ! egrep -q "^${mime_type}=.*${1}" "${DEFAULTS_FILE}"; then
-        default_apps="$(grep ${mime_type}= "${DEFAULTS_FILE}" |
+    if egrep -q "^${mime_type}=" "${DEFAULTS_LIST}"; then
+      if ! egrep -q "^${mime_type}=.*${1}" "${DEFAULTS_LIST}"; then
+        default_apps="$(grep ${mime_type}= "${DEFAULTS_LIST}" |
                         cut -d '=' -f 2-)"
-        egrep -v "^${mime_type}=" "${DEFAULTS_FILE}" > "${DEFAULTS_FILE}.new"
-        echo "${mime_type}=${default_apps};${1}" >> "${DEFAULTS_FILE}.new"
-        mv "${DEFAULTS_FILE}.new" "${DEFAULTS_FILE}"
+        egrep -v "^${mime_type}=" "${DEFAULTS_LIST}" > "${DEFAULTS_LIST}.new"
+        echo "${mime_type}=${default_apps};${1}" >> "${DEFAULTS_LIST}.new"
+        mv "${DEFAULTS_LIST}.new" "${DEFAULTS_LIST}"
       fi
     else
       # If there's no mention of the mime type in the file, add it.
-      echo "${mime_type}=${1};" >> "${DEFAULTS_FILE}"
+      echo "${mime_type}=${1};" >> "${DEFAULTS_LIST}"
     fi
   done
 }
diff --git a/chrome/installer/linux/common/repo.cron b/chrome/installer/linux/common/repo.cron
index 2750e49..59404018 100755
--- a/chrome/installer/linux/common/repo.cron
+++ b/chrome/installer/linux/common/repo.cron
@@ -17,10 +17,11 @@
 # "false" as desired. An empty $DEFAULTS_FILE is the same as setting both values
 # to "false".
 
+@@include@@../common/variables.include
+
 @@include@@apt.include
 
 ## MAIN ##
-DEFAULTS_FILE="/etc/default/@@PACKAGE@@"
 if [ -r "$DEFAULTS_FILE" ]; then
   . "$DEFAULTS_FILE"
 fi
diff --git a/chrome/installer/linux/common/repo_variables.include b/chrome/installer/linux/common/repo_variables.include
new file mode 100644
index 0000000..06b20b7
--- /dev/null
+++ b/chrome/installer/linux/common/repo_variables.include
@@ -0,0 +1,3 @@
+# sources.list setting for @@PACKAGE@@ updates.
+REPOCONFIG="@@REPOCONFIG@@"
+REPOCONFIGREGEX="@@REPOCONFIGREGEX@@"
diff --git a/chrome/installer/linux/common/rpm.include b/chrome/installer/linux/common/rpm.include
index 4931afe..3b959f99 100644
--- a/chrome/installer/linux/common/rpm.include
+++ b/chrome/installer/linux/common/rpm.include
@@ -1,4 +1,4 @@
-@@include@@variables.include
+@@include@@repo_variables.include
 
 # Install the repository signing key (see also:
 # https://www.google.com/linuxrepositories/)
diff --git a/chrome/installer/linux/common/rpmrepo.cron b/chrome/installer/linux/common/rpmrepo.cron
index 1a9344f..778f43a 100755
--- a/chrome/installer/linux/common/rpmrepo.cron
+++ b/chrome/installer/linux/common/rpmrepo.cron
@@ -14,10 +14,11 @@
 # setting "repo_add_once" to "true" or "false" as desired. An empty
 # $DEFAULTS_FILE is the same as setting the value to "false".
 
+@@include@@../common/variables.include
+
 @@include@@rpm.include
 
 ## MAIN ##
-DEFAULTS_FILE="/etc/default/@@PACKAGE@@"
 if [ -r "$DEFAULTS_FILE" ]; then
   . "$DEFAULTS_FILE"
 fi
diff --git a/chrome/installer/linux/common/variables.include b/chrome/installer/linux/common/variables.include
index be94314..5533249 100644
--- a/chrome/installer/linux/common/variables.include
+++ b/chrome/installer/linux/common/variables.include
@@ -1,6 +1,2 @@
 # System-wide package configuration.
 DEFAULTS_FILE="/etc/default/@@PACKAGE@@"
-
-# sources.list setting for @@PACKAGE@@ updates.
-REPOCONFIG="@@REPOCONFIG@@"
-REPOCONFIGREGEX="@@REPOCONFIGREGEX@@"
diff --git a/chrome/installer/linux/debian/postinst b/chrome/installer/linux/debian/postinst
index b284f042..34c6c20 100755
--- a/chrome/installer/linux/debian/postinst
+++ b/chrome/installer/linux/debian/postinst
@@ -6,6 +6,8 @@
 
 set -e
 
+@@include@@../common/variables.include
+
 @@include@@../common/postinst.include
 
 # Add to the alternatives system
diff --git a/chrome/installer/linux/debian/postrm b/chrome/installer/linux/debian/postrm
index c55d98e..705bb9f 100755
--- a/chrome/installer/linux/debian/postrm
+++ b/chrome/installer/linux/debian/postrm
@@ -13,6 +13,8 @@
   exit 0
 fi
 
+@@include@@../common/variables.include
+
 @@include@@../common/apt.include
 
 @@include@@../common/symlinks.include
diff --git a/chrome/installer/linux/rpm/chrome.spec.template b/chrome/installer/linux/rpm/chrome.spec.template
index c755e4c..e968b5f 100644
--- a/chrome/installer/linux/rpm/chrome.spec.template
+++ b/chrome/installer/linux/rpm/chrome.spec.template
@@ -111,6 +111,8 @@
 #------------------------------------------------------------------------------
 %post
 
+@@include@@../common/variables.include
+
 @@include@@../common/postinst.include
 
 @@include@@../common/rpm.include
@@ -122,7 +124,6 @@
 
 remove_udev_symlinks
 
-DEFAULTS_FILE="/etc/default/@@PACKAGE@@"
 if [ ! -e "$DEFAULTS_FILE" ]; then
   echo 'repo_add_once="true"' > "$DEFAULTS_FILE"
 fi
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index 51e6723a..d92390e 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -523,21 +523,21 @@
       base::BindRepeating(&DeleteUserRegistryKeys, base::Unretained(&paths)));
 }
 
-// Removes the persistent blacklist state for the current user.  Note: this will
+// Removes the persistent blocklist state for the current user.  Note: this will
 // not remove the state for users other than the one uninstalling Chrome on a
 // system-level install (http://crbug.com/388725). Doing so would require
 // extracting the per-user registry hive iteration from
 // UninstallActiveSetupEntries so that it could service multiple tasks.
-void RemoveBlacklistState() {
+void RemoveBlocklistState() {
   DeleteRegistryKey(HKEY_CURRENT_USER,
                     install_static::GetRegistryPath().append(
-                        blacklist::kRegistryBeaconKeyName),
+                        blocklist::kRegistryBeaconKeyName),
                     0);  // wow64_access
 }
 
 // Removes the browser's persistent state in the Windows registry for the
 // current user. Note: this will not remove the state for users other than the
-// one uninstalling Chrome on a system-level install; see RemoveBlacklistState
+// one uninstalling Chrome on a system-level install; see RemoveBlocklistState
 // for details.
 void RemoveDistributionRegistryState() {
   // Delete the contents of the distribution key except for those parts used by
@@ -951,7 +951,7 @@
 
   UninstallFirewallRules(chrome_exe);
 
-  RemoveBlacklistState();
+  RemoveBlocklistState();
 
   // Notify the shell that associations have changed since Chrome was likely
   // unregistered.
diff --git a/chrome/installer/util/google_update_util.cc b/chrome/installer/util/google_update_util.cc
index fa1d369..063d81c 100644
--- a/chrome/installer/util/google_update_util.cc
+++ b/chrome/installer/util/google_update_util.cc
@@ -89,10 +89,12 @@
   base::LaunchOptions launch_options;
   launch_options.force_breakaway_from_job_ = true;
 
-  if (base::win::UserAccountControlIsEnabled())
+  if (base::win::UserAccountControlIsEnabled()) {
+    launch_options.elevated = true;
     base::LaunchElevatedProcess(cmd, launch_options);
-  else
+  } else {
     base::LaunchProcess(cmd, launch_options);
+  }
 }
 
 }  // namespace google_update
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index ef8b021a..1528c9f 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -69,8 +69,6 @@
     "loadtimes_extension_bindings.h",
     "media/chrome_key_systems.cc",
     "media/chrome_key_systems.h",
-    "media/chrome_key_systems_provider.cc",
-    "media/chrome_key_systems_provider.h",
     "media/flash_embed_rewrite.cc",
     "media/flash_embed_rewrite.h",
     "media/media_feeds.cc",
@@ -372,8 +370,6 @@
     sources += [
       "cart/commerce_hint_agent.cc",
       "cart/commerce_hint_agent.h",
-      "cart/commerce_renderer_feature_list.cc",
-      "cart/commerce_renderer_feature_list.h",
       "media/chrome_speech_recognition_client.cc",
       "media/chrome_speech_recognition_client.h",
       "searchbox/searchbox.cc",
@@ -385,6 +381,7 @@
     deps += [
       "//chrome/common/cart:mojo_bindings",
       "//components/commerce/core:commerce_heuristics_data",
+      "//components/commerce/core:feature_list",
       "//components/crx_file",
       "//components/search:search",
     ]
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 96b7dcbb..90a20da 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -51,6 +51,7 @@
   "+components/printing/renderer",
   "+components/search/ntp_features.h",
   "+components/commerce/core/commerce_heuristics_data.h",
+  "+components/commerce/core/commerce_feature_list.h",
   "+components/safe_browsing/buildflags.h",
   "+components/safe_browsing/content/renderer",
   "+components/safe_browsing/content/common",
diff --git a/chrome/renderer/cart/commerce_hint_agent.cc b/chrome/renderer/cart/commerce_hint_agent.cc
index 26ab21c..0382bdca 100644
--- a/chrome/renderer/cart/commerce_hint_agent.cc
+++ b/chrome/renderer/cart/commerce_hint_agent.cc
@@ -16,7 +16,7 @@
 #include "base/values.h"
 #include "chrome/common/chrome_isolated_world_ids.h"
 #include "chrome/grit/renderer_resources.h"
-#include "chrome/renderer/cart/commerce_renderer_feature_list.h"
+#include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/commerce_heuristics_data.h"
 #include "components/search/ntp_features.h"
 #include "content/public/renderer/render_frame.h"
@@ -175,8 +175,7 @@
     ""};
 
 constexpr base::FeatureParam<std::string> kCouponProductIdPatternMapping{
-    &commerce_renderer_feature::kRetailCoupons,
-    "coupon-product-id-pattern-mapping",
+    &commerce::kRetailCoupons, "coupon-product-id-pattern-mapping",
     // Empty JSON string.
     ""};
 
@@ -318,6 +317,13 @@
 }
 
 const re2::RE2& GetAddToCartPattern() {
+  auto* pattern_from_component =
+      commerce_heuristics::CommerceHeuristicsData::GetInstance()
+          .GetAddToCartRequestPattern();
+  if (pattern_from_component &&
+      kAddToCartPattern.Get() == kAddToCartPattern.default_value) {
+    return *pattern_from_component;
+  }
   re2::RE2::Options options;
   options.set_case_sensitive(false);
   static base::NoDestructor<re2::RE2> instance(kAddToCartPattern.Get(),
@@ -353,6 +359,13 @@
   options.set_case_sensitive(false);
   const std::string& domain = eTLDPlusOne(url);
   if (heuristic_string_map->find(domain) == heuristic_string_map->end()) {
+    auto* pattern_from_component =
+        commerce_heuristics::CommerceHeuristicsData::GetInstance()
+            .GetCartPageURLPattern();
+    if (pattern_from_component &&
+        kCartPattern.Get() == kCartPattern.default_value) {
+      return *pattern_from_component;
+    }
     static base::NoDestructor<re2::RE2> instance(kCartPattern.Get(), options);
     return *instance;
   }
@@ -366,6 +379,13 @@
 
 // TODO(crbug/1164236): cover more shopping sites.
 const re2::RE2& GetVisitCheckoutPattern() {
+  auto* pattern_from_component =
+      commerce_heuristics::CommerceHeuristicsData::GetInstance()
+          .GetCheckoutPageURLPattern();
+  if (pattern_from_component &&
+      kCheckoutPattern.Get() == kCheckoutPattern.default_value) {
+    return *pattern_from_component;
+  }
   re2::RE2::Options options;
   options.set_case_sensitive(false);
   static base::NoDestructor<re2::RE2> instance(kCheckoutPattern.Get(), options);
@@ -391,6 +411,13 @@
 
 // TODO(crbug/1164236): need i18n.
 const re2::RE2& GetPurchaseTextPattern() {
+  auto* pattern_from_component =
+      commerce_heuristics::CommerceHeuristicsData::GetInstance()
+          .GetPurchaseButtonTextPattern();
+  if (pattern_from_component &&
+      kPurchaseButtonPattern.Get() == kPurchaseButtonPattern.default_value) {
+    return *pattern_from_component;
+  }
   re2::RE2::Options options;
   options.set_case_sensitive(false);
   static base::NoDestructor<re2::RE2> instance(kPurchaseButtonPattern.Get(),
@@ -517,7 +544,7 @@
   }
   if (is_add_to_cart) {
     std::string url_product_id;
-    if (commerce_renderer_feature::IsPartnerMerchant(navigation_url)) {
+    if (commerce::IsPartnerMerchant(navigation_url)) {
       GetProductIdFromRequest(url.spec().substr(0, kLengthLimit),
                               &url_product_id);
     }
@@ -577,7 +604,7 @@
 
     if (CommerceHintAgent::IsAddToCart(str, skip_length_limit)) {
       std::string product_id;
-      if (commerce_renderer_feature::IsPartnerMerchant(url)) {
+      if (commerce::IsPartnerMerchant(url)) {
         GetProductIdFromRequest(str.substr(0, kLengthLimit), &product_id);
       }
       RecordCommerceEvent(CommerceEvent::kAddToCartByForm);
@@ -862,7 +889,7 @@
   // that the cart is not loaded.
   if (!extracted_products->is_list())
     return;
-  bool is_partner = commerce_renderer_feature::IsPartnerMerchant(
+  bool is_partner = commerce::IsPartnerMerchant(
       GURL(render_frame()->GetWebFrame()->GetDocument().Url()));
   std::vector<mojom::ProductPtr> products;
   for (const auto& product : extracted_products->GetListDeprecated()) {
@@ -939,7 +966,7 @@
 
 void CommerceHintAgent::OnNavigation(const GURL& url,
                                      OnNavigationCallback callback) {
-  if (!commerce_renderer_feature::kOptimizeRendererSignal.Get()) {
+  if (!commerce::kOptimizeRendererSignal.Get()) {
     std::move(callback).Run(false);
     return;
   }
diff --git a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
index dc2e5d3..9870b8f 100644
--- a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
+++ b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
@@ -16,8 +16,8 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/renderer/cart/commerce_renderer_feature_list.h"
 #include "chrome/test/base/chrome_test_utils.h"
+#include "components/commerce/core/commerce_feature_list.h"
 #include "components/metrics/content/subprocess_metrics_provider.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
@@ -698,7 +698,7 @@
             )###"},
            // Extend timeout to avoid flakiness.
            {"cart-extraction-timeout", "1m"}}},
-         {commerce_renderer_feature::kRetailCoupons,
+         {commerce::kRetailCoupons,
           {{"coupon-partner-merchant-pattern", "(eee.com)"},
            {"coupon-product-id-pattern-mapping",
             R"###(
diff --git a/chrome/renderer/cart/commerce_hint_agent_unittest.cc b/chrome/renderer/cart/commerce_hint_agent_unittest.cc
index 7a23dfc..3ed0b98 100644
--- a/chrome/renderer/cart/commerce_hint_agent_unittest.cc
+++ b/chrome/renderer/cart/commerce_hint_agent_unittest.cc
@@ -776,16 +776,47 @@
 
 using cart::CommerceHintAgent;
 
-TEST(CommerceHintAgentTest, IsAddToCart) {
+class CommerceHintAgentUnitTest : public testing::Test {
+ public:
+  void TearDown() override {
+    // Clear out heuristics data that is set up during testing.
+    commerce_heuristics::CommerceHeuristicsData::GetInstance()
+        .PopulateDataFromComponent(
+            /*hint_json_data=*/"{}", /*global_json_data=*/"{}",
+            /*product_id_json_data=*/"{}", /*cart_extraction_script=*/"");
+  }
+};
+
+TEST_F(CommerceHintAgentUnitTest, IsAddToCart) {
+  // Heuristics from feature param default value.
   for (auto* str : kAddToCart) {
     EXPECT_TRUE(CommerceHintAgent::IsAddToCart(str)) << str;
   }
   for (auto* str : kNotAddToCart) {
     EXPECT_FALSE(CommerceHintAgent::IsAddToCart(str)) << str;
   }
+
+  // Heuristics from component.
+  const std::string& component_pattern = R"###(
+      {
+        "add_to_cart_request_regex": "bar"
+      }
+  )###";
+  EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance()
+                  .PopulateDataFromComponent("{}", component_pattern, "", ""));
+  EXPECT_TRUE(CommerceHintAgent::IsAddToCart("request_bar"));
+  for (auto* str : kAddToCart) {
+    EXPECT_FALSE(CommerceHintAgent::IsAddToCart(str)) << str;
+  }
+
+  // Feature param has a higher priority.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      ntp_features::kNtpChromeCartModule, {{"add-to-cart-pattern", "foo"}});
+  EXPECT_FALSE(CommerceHintAgent::IsAddToCart("request_bar"));
 }
 
-TEST(CommerceHintAgentTest, IsAddToCart_SkipLengthLimit) {
+TEST_F(CommerceHintAgentUnitTest, IsAddToCart_SkipLengthLimit) {
   std::string str = "a";
   for (int i = 0; i < 12; ++i) {
     str += str;
@@ -798,25 +829,60 @@
   EXPECT_TRUE(CommerceHintAgent::IsAddToCart(str, true));
 }
 
-TEST(CommerceHintAgentTest, IsVisitCart) {
+TEST_F(CommerceHintAgentUnitTest, IsVisitCart) {
+  // Heuristics from feature param default value.
   for (auto* str : kVisitCart) {
     EXPECT_TRUE(CommerceHintAgent::IsVisitCart(GURL(str))) << str;
   }
   for (auto* str : kNotVisitCart) {
     EXPECT_FALSE(CommerceHintAgent::IsVisitCart(GURL(str))) << str;
   }
+
+  // Heuristics from component.
+  const std::string& component_pattern = R"###(
+      {
+        "cart_page_url_regex": "bar"
+      }
+  )###";
+  EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance()
+                  .PopulateDataFromComponent("{}", component_pattern, "", ""));
+  EXPECT_TRUE(CommerceHintAgent::IsVisitCart(GURL("https://wwww.bar.com")));
+
+  // Feature param has a higher priority.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      ntp_features::kNtpChromeCartModule, {{"cart-pattern", "foo"}});
+  EXPECT_FALSE(CommerceHintAgent::IsVisitCart(GURL("https://wwww.bar.com")));
 }
 
-TEST(CommerceHintAgentTest, IsVisitCheckout) {
+TEST_F(CommerceHintAgentUnitTest, IsVisitCheckout) {
+  // Heuristics from feature param default value.
   for (auto* str : kVisitCheckout) {
     EXPECT_TRUE(CommerceHintAgent::IsVisitCheckout(GURL(str))) << str;
   }
   for (auto* str : kNotVisitCheckout) {
     EXPECT_FALSE(CommerceHintAgent::IsVisitCheckout(GURL(str))) << str;
   }
+
+  // Heuristics from component.
+  const std::string& component_pattern = R"###(
+      {
+        "checkout_page_url_regex": "bar"
+      }
+  )###";
+  EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance()
+                  .PopulateDataFromComponent("{}", component_pattern, "", ""));
+  EXPECT_TRUE(CommerceHintAgent::IsVisitCheckout(GURL("https://wwww.bar.com")));
+
+  // Feature param has a higher priority.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      ntp_features::kNtpChromeCartModule, {{"checkout-pattern", "foo"}});
+  EXPECT_FALSE(
+      CommerceHintAgent::IsVisitCheckout(GURL("https://wwww.bar.com")));
 }
 
-TEST(CommerceHintAgentTest, IsPurchaseByURL) {
+TEST_F(CommerceHintAgentUnitTest, IsPurchaseByURL) {
   for (auto* str : kPurchaseURL) {
     EXPECT_TRUE(CommerceHintAgent::IsPurchase(GURL(str))) << str;
   }
@@ -825,16 +891,36 @@
   }
 }
 
-TEST(CommerceHintAgentTest, IsPurchaseByForm) {
+TEST_F(CommerceHintAgentUnitTest, IsPurchaseByForm) {
+  // Heuristics from feature param default value.
   for (auto* str : kPurchaseText) {
     EXPECT_TRUE(CommerceHintAgent::IsPurchase(GURL(), str)) << str;
   }
   for (auto* str : kNotPurchaseText) {
     EXPECT_FALSE(CommerceHintAgent::IsPurchase(GURL(), str)) << str;
   }
+
+  // Heuristics from component.
+  const std::string& component_pattern = R"###(
+      {
+        "purchase_button_text_regex": "bar"
+      }
+  )###";
+  EXPECT_TRUE(commerce_heuristics::CommerceHeuristicsData::GetInstance()
+                  .PopulateDataFromComponent("{}", component_pattern, "", ""));
+  EXPECT_TRUE(CommerceHintAgent::IsPurchase(GURL(), "bar"));
+  for (auto* str : kPurchaseText) {
+    EXPECT_FALSE(CommerceHintAgent::IsPurchase(GURL(), str)) << str;
+  }
+
+  // Feature param has a higher priority.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      ntp_features::kNtpChromeCartModule, {{"purchase-button-pattern", "foo"}});
+  EXPECT_FALSE(CommerceHintAgent::IsPurchase(GURL(), "bar"));
 }
 
-TEST(CommerceHintAgentTest, ShouldSkipFromFeatureParam) {
+TEST_F(CommerceHintAgentUnitTest, ShouldSkipFromFeatureParam) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeatureWithParameters(
       ntp_features::kNtpChromeCartModule, kSkipParams);
@@ -847,7 +933,7 @@
   }
 }
 
-TEST(CommerceHintAgentTest, ShouldSkipFromComponent) {
+TEST_F(CommerceHintAgentUnitTest, ShouldSkipFromComponent) {
   EXPECT_TRUE(
       commerce_heuristics::CommerceHeuristicsData::GetInstance()
           .PopulateDataFromComponent("{}", kGlobalHeuristicsJSONData, "", ""));
@@ -860,7 +946,7 @@
   }
 }
 
-TEST(CommerceHintAgentTest, ShouldSkip_Priority) {
+TEST_F(CommerceHintAgentUnitTest, ShouldSkip_Priority) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeatureWithParameters(
       ntp_features::kNtpChromeCartModule, kSkipParams);
@@ -882,7 +968,7 @@
   }
 }
 
-TEST(CommerceHintAgentTest, ShouldSkipAddToCartFromResource) {
+TEST_F(CommerceHintAgentUnitTest, ShouldSkipAddToCartFromResource) {
   for (auto const& entry : kSkipAddToCartRequests) {
     EXPECT_TRUE(CommerceHintAgent::ShouldSkipAddToCartRequest(
         GURL(entry.first), GURL(entry.second)));
@@ -986,7 +1072,7 @@
 #define MAYBE_RegexBenchmark RegexBenchmark
 #endif
 
-TEST(CommerceHintAgentTest, MAYBE_RegexBenchmark) {
+TEST_F(CommerceHintAgentUnitTest, MAYBE_RegexBenchmark) {
   std::string str = "abcdefghijklmnop";
   const GURL basic_url = GURL("http://example.com/");
   // Compile regex before benchmark loop.
diff --git a/chrome/renderer/cart/commerce_renderer_feature_list.cc b/chrome/renderer/cart/commerce_renderer_feature_list.cc
deleted file mode 100644
index 1ff9975..0000000
--- a/chrome/renderer/cart/commerce_renderer_feature_list.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/renderer/cart/commerce_renderer_feature_list.h"
-
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/no_destructor.h"
-#include "third_party/re2/src/re2/re2.h"
-
-namespace commerce_renderer_feature {
-
-namespace {
-
-constexpr base::FeatureParam<std::string> kCouponPartnerMerchantPattern{
-    &commerce_renderer_feature::kRetailCoupons,
-    "coupon-partner-merchant-pattern",
-    // This regex does not match anything.
-    "\\b\\B"};
-
-constexpr base::FeatureParam<std::string> kDiscountPartnerMerchantPattern{
-    &ntp_features::kNtpChromeCartModule, "partner-merchant-pattern",
-    // This regex does not match anything.
-    "\\b\\B"};
-
-const re2::RE2& GetCouponPartnerMerchantPattern() {
-  re2::RE2::Options options;
-  options.set_case_sensitive(false);
-  static base::NoDestructor<re2::RE2> instance(
-      kCouponPartnerMerchantPattern.Get(), options);
-  return *instance;
-}
-
-bool IsCouponPartnerMerchant(const GURL& url) {
-  const std::string& url_string = url.spec();
-  return RE2::PartialMatch(
-      re2::StringPiece(url_string.data(), url_string.size()),
-      GetCouponPartnerMerchantPattern());
-}
-
-const re2::RE2& GetDiscountPartnerMerchantPattern() {
-  re2::RE2::Options options;
-  options.set_case_sensitive(false);
-  static base::NoDestructor<re2::RE2> instance(
-      kDiscountPartnerMerchantPattern.Get(), options);
-  return *instance;
-}
-
-bool IsDiscountPartnerMerchant(const GURL& url) {
-  const std::string& url_string = url.spec();
-  return RE2::PartialMatch(
-      re2::StringPiece(url_string.data(), url_string.size()),
-      GetDiscountPartnerMerchantPattern());
-}
-
-}  // namespace
-
-const base::Feature kRetailCoupons{"RetailCoupons",
-                                   base::FEATURE_ENABLED_BY_DEFAULT};
-
-bool IsPartnerMerchant(const GURL& url) {
-  return IsCouponPartnerMerchant(url) || IsDiscountPartnerMerchant(url);
-}
-
-}  // namespace commerce_renderer_feature
diff --git a/chrome/renderer/cart/commerce_renderer_feature_list.h b/chrome/renderer/cart/commerce_renderer_feature_list.h
deleted file mode 100644
index c8d1b32..0000000
--- a/chrome/renderer/cart/commerce_renderer_feature_list.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_CART_COMMERCE_RENDERER_FEATURE_LIST_H_
-#define CHROME_RENDERER_CART_COMMERCE_RENDERER_FEATURE_LIST_H_
-
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "components/search/ntp_features.h"
-#include "url/gurl.h"
-
-namespace commerce_renderer_feature {
-extern const base::Feature kRetailCoupons;
-
-// Whether to use OptimizationGuide to optimize renderer signal collection.
-constexpr base::FeatureParam<bool> kOptimizeRendererSignal(
-    &ntp_features::kNtpChromeCartModule,
-    "optimize-renderer-signal",
-    false);
-
-// Check if a URL belongs to a partner merchant of any type of discount.
-bool IsPartnerMerchant(const GURL& url);
-}  // namespace commerce_renderer_feature
-
-#endif  // CHROME_RENDERER_CART_COMMERCE_RENDERER_FEATURE_LIST_H_
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index b388532..bbac287 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -48,6 +48,7 @@
 #include "chrome/renderer/chrome_render_frame_observer.h"
 #include "chrome/renderer/chrome_render_thread_observer.h"
 #include "chrome/renderer/loadtimes_extension_bindings.h"
+#include "chrome/renderer/media/chrome_key_systems.h"
 #include "chrome/renderer/media/flash_embed_rewrite.h"
 #include "chrome/renderer/media/webrtc_logging_agent_impl.h"
 #include "chrome/renderer/net/net_error_helper.h"
@@ -1471,11 +1472,7 @@
 
 void ChromeContentRendererClient::GetSupportedKeySystems(
     media::GetSupportedKeySystemsCB cb) {
-  key_systems_provider_.GetSupportedKeySystems(std::move(cb));
-}
-
-bool ChromeContentRendererClient::IsKeySystemsUpdateNeeded() {
-  return key_systems_provider_.IsKeySystemsUpdateNeeded();
+  GetChromeKeySystems(std::move(cb));
 }
 
 bool ChromeContentRendererClient::ShouldReportDetailedMessageForSource(
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 743e9836..5230b897 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -17,7 +17,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
 #include "chrome/common/media/webrtc_logging.mojom.h"
-#include "chrome/renderer/media/chrome_key_systems_provider.h"
 #include "components/nacl/common/buildflags.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "content/public/renderer/content_renderer_client.h"
@@ -158,7 +157,6 @@
       media::SpeechRecognitionClient::OnReadyCallback callback) override;
 #endif
   void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb) override;
-  bool IsKeySystemsUpdateNeeded() override;
   bool IsPluginAllowedToUseCameraDeviceAPI(const GURL& url) override;
   void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override;
   void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
@@ -267,8 +265,6 @@
   std::unique_ptr<web_cache::WebCacheImpl> web_cache_impl_;
   std::unique_ptr<chrome::WebRtcLoggingAgentImpl> webrtc_logging_agent_impl_;
 
-  ChromeKeySystemsProvider key_systems_provider_;
-
 #if BUILDFLAG(ENABLE_SPELLCHECK)
   std::unique_ptr<SpellCheck> spellcheck_;
 #endif
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc
index 0cdaa37d..ff769b9 100644
--- a/chrome/renderer/media/chrome_key_systems.cc
+++ b/chrome/renderer/media/chrome_key_systems.cc
@@ -32,26 +32,18 @@
 #include "components/cdm/renderer/android_key_systems.h"
 #endif
 
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN)
 #include "base/feature_list.h"
 #include "content/public/renderer/key_system_support.h"
 #include "media/base/media_switches.h"
 #include "media/base/video_codecs.h"
 #if BUILDFLAG(ENABLE_WIDEVINE)
 #include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
-// TODO(crbug.com/663554): Needed for WIDEVINE_CDM_MIN_GLIBC_VERSION.
-// component updated CDM on all desktop platforms and remove this.
-#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR. // nogncheck
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC) && BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC) && BUILDFLAG(IS_CHROMEOS_ASH)
-// The following must be after widevine_cdm_version.h.
-#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
-#include <gnu/libc-version.h>
-#include "base/version.h"
-#endif  // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
 #endif  // BUILDFLAG(ENABLE_WIDEVINE)
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN)
 
 using media::EmeFeatureSupport;
 using media::EmeSessionTypeSupport;
@@ -61,46 +53,7 @@
 
 namespace {
 
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-
-// Helper callback for chained key system query operations.
-using TrampolineCB = media::GetSupportedKeySystemsCB;
-
-void OnExternalClearKeyQueried(
-    TrampolineCB cb,
-    KeySystemPropertiesVector key_systems,
-    bool is_supported,
-    media::mojom::KeySystemCapabilityPtr capability) {
-  DVLOG(1) << __func__;
-
-  // TODO(xhwang): Actually use `capability` to determine capabilities.
-  if (is_supported) {
-    key_systems.push_back(std::make_unique<cdm::ExternalClearKeyProperties>());
-  } else {
-    DVLOG(1) << "External Clear Key not supported";
-  }
-
-  std::move(cb).Run(std::move(key_systems));
-}
-
-// External Clear Key (used for testing).
-void QueryExternalClearKey(TrampolineCB cb,
-                           KeySystemPropertiesVector key_systems) {
-  DVLOG(1) << __func__;
-
-  if (!base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting)) {
-    std::move(cb).Run(std::move(key_systems));
-    return;
-  }
-
-  static const char kExternalClearKeyKeySystem[] =
-      "org.chromium.externalclearkey";
-
-  content::IsKeySystemSupported(
-      kExternalClearKeyKeySystem,
-      base::BindOnce(&OnExternalClearKeyQueried, std::move(cb),
-                     std::move(key_systems)));
-}
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(ENABLE_WIDEVINE)
 SupportedCodecs GetVP9Codecs(
@@ -273,7 +226,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
-bool AddWidevine(media::mojom::KeySystemCapabilityPtr capability,
+bool AddWidevine(const media::mojom::KeySystemCapabilityPtr& capability,
                  KeySystemPropertiesVector* key_systems) {
   // Codecs and encryption schemes.
   SupportedCodecs codecs = media::EME_CODEC_NONE;
@@ -294,6 +247,8 @@
     cdm_supports_persistent_license =
         base::Contains(capability->sw_secure_capability->session_types,
                        media::CdmSessionType::kPersistentLicense);
+
+    DVLOG(2) << "Software secure Widevine supported";
   }
 
   if (capability->hw_secure_capability) {
@@ -311,6 +266,7 @@
     // session support between software and hardware CDMs. This should be
     // fixed so that if there is both a software and a hardware CDM, persistent
     // session support can be different between the versions.
+    DVLOG(2) << "Hardware secure Widevine supported";
   }
 
   // Robustness.
@@ -347,60 +303,65 @@
       distinctive_identifier_support));
   return true;
 }
+#endif  // BUILDFLAG(ENABLE_WIDEVINE)
 
-void OnWidevineQueried(TrampolineCB cb,
-                       KeySystemPropertiesVector key_systems,
-                       bool is_supported,
-                       media::mojom::KeySystemCapabilityPtr capability) {
+const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
+
+void AddExternalClearKey(
+    const media::mojom::KeySystemCapabilityPtr& /*capability*/,
+    KeySystemPropertiesVector* key_systems) {
   DVLOG(1) << __func__;
 
-  if (is_supported) {
-    if (!AddWidevine(std::move(capability), &key_systems))
-      DVLOG(1) << "Invalid Widevine CDM capability.";
-  } else {
-    DVLOG(1) << "Widevine CDM is not currently available.";
-  }
-
-  std::move(cb).Run(std::move(key_systems));
-}
-
-void QueryWidevine(TrampolineCB cb, KeySystemPropertiesVector key_systems) {
-  DVLOG(1) << __func__;
-
-#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
-  base::Version glibc_version(gnu_get_libc_version());
-  DCHECK(glibc_version.IsValid());
-  if (glibc_version < base::Version(WIDEVINE_CDM_MIN_GLIBC_VERSION)) {
-    std::move(cb).Run(std::move(key_systems));
+  if (!base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting)) {
+    DLOG(ERROR) << "ExternalClearKey supported despite not enabled.";
     return;
   }
-#endif  // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
 
-  content::IsKeySystemSupported(
-      kWidevineKeySystem, base::BindOnce(&OnWidevineQueried, std::move(cb),
-                                         std::move(key_systems)));
+  // TODO(xhwang): Actually use `capability` to determine capabilities.
+  key_systems->push_back(std::make_unique<cdm::ExternalClearKeyProperties>());
 }
+
+void OnKeySystemSupportUpdated(
+    media::GetSupportedKeySystemsCB cb,
+    content::KeySystemCapabilityPtrMap key_system_capabilities) {
+  KeySystemPropertiesVector key_systems;
+  for (const auto& entry : key_system_capabilities) {
+    const auto& key_system = entry.first;
+    const auto& capability = entry.second;
+#if BUILDFLAG(ENABLE_WIDEVINE)
+    if (key_system == kWidevineKeySystem) {
+      AddWidevine(capability, &key_systems);
+      continue;
+    }
 #endif  // BUILDFLAG(ENABLE_WIDEVINE)
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
+    if (key_system == kExternalClearKeyKeySystem) {
+      AddExternalClearKey(capability, &key_systems);
+      continue;
+    }
+
+    DLOG(ERROR) << "Unrecognized key system: " << key_system;
+  }
+
+  cb.Run(std::move(key_systems));
+}
+
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN)
 
 }  // namespace
 
 void GetChromeKeySystems(media::GetSupportedKeySystemsCB cb) {
-  KeySystemPropertiesVector key_systems;
-
 #if BUILDFLAG(IS_ANDROID)
+  KeySystemPropertiesVector key_systems;
   cdm::AddAndroidWidevine(&key_systems);
-#endif  // BUILDFLAG(IS_ANDROID)
-
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-#if BUILDFLAG(ENABLE_WIDEVINE)
-  auto trampoline_cb = base::BindOnce(&QueryWidevine, std::move(cb));
-#else
-  auto trampoline_cb = std::move(cb);
-#endif  // BUILDFLAG(ENABLE_WIDEVINE)
-
-  QueryExternalClearKey(std::move(trampoline_cb), std::move(key_systems));
-#else
   std::move(cb).Run(std::move(key_systems));
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+  return;
+#elif BUILDFLAG(ENABLE_LIBRARY_CDMS) || BUILDFLAG(IS_WIN)
+  content::ObserveKeySystemSupportUpdate(
+      base::BindRepeating(&OnKeySystemSupportUpdated, std::move(cb)));
+  return;
+#else
+  std::move(cb).Run({});
+  return;
+#endif
 }
diff --git a/chrome/renderer/media/chrome_key_systems_provider.cc b/chrome/renderer/media/chrome_key_systems_provider.cc
deleted file mode 100644
index e359643..0000000
--- a/chrome/renderer/media/chrome_key_systems_provider.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/renderer/media/chrome_key_systems_provider.h"
-
-#include "chrome/renderer/media/chrome_key_systems.h"
-#include "third_party/widevine/cdm/buildflags.h"
-
-#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
-#include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
-#endif
-
-ChromeKeySystemsProvider::ChromeKeySystemsProvider() = default;
-ChromeKeySystemsProvider::~ChromeKeySystemsProvider() = default;
-
-void ChromeKeySystemsProvider::GetSupportedKeySystems(
-    media::GetSupportedKeySystemsCB cb) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (!test_provider_.is_null()) {
-    media::KeySystemPropertiesVector key_systems;
-    test_provider_.Run(&key_systems);
-    OnSupportedKeySystemsReady(std::move(cb), std::move(key_systems));
-    return;
-  }
-
-  GetChromeKeySystems(
-      base::BindOnce(&ChromeKeySystemsProvider::OnSupportedKeySystemsReady,
-                     weak_factory_.GetWeakPtr(), std::move(cb)));
-}
-
-bool ChromeKeySystemsProvider::IsKeySystemsUpdateNeeded() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  // Always needs update if we have never updated, regardless the
-  // |last_update_time_ticks_|'s initial value.
-  if (!has_updated_) {
-    DCHECK(is_update_needed_);
-    return true;
-  }
-
-  if (!is_update_needed_)
-    return false;
-
-  // The update could be expensive. For example, it could involve an IPC to the
-  // browser process. Use a minimum update interval to avoid unnecessarily
-  // frequent update.
-  static const int kMinUpdateIntervalInMilliseconds = 1000;
-  if ((tick_clock_->NowTicks() - last_update_time_ticks_).InMilliseconds() <
-      kMinUpdateIntervalInMilliseconds) {
-    return false;
-  }
-
-  return true;
-}
-
-void ChromeKeySystemsProvider::SetTickClockForTesting(
-    const base::TickClock* tick_clock) {
-  tick_clock_ = tick_clock;
-}
-
-void ChromeKeySystemsProvider::SetProviderDelegateForTesting(
-    KeySystemsProviderDelegate test_provider) {
-  test_provider_ = std::move(test_provider);
-}
-
-void ChromeKeySystemsProvider::OnSupportedKeySystemsReady(
-    media::GetSupportedKeySystemsCB cb,
-    media::KeySystemPropertiesVector key_systems) {
-  has_updated_ = true;
-  last_update_time_ticks_ = tick_clock_->NowTicks();
-
-// Check whether all potentially supported key systems are supported. If so,
-// no need to update again.
-#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
-  for (const auto& properties : key_systems) {
-    if (properties->GetBaseKeySystemName() == kWidevineKeySystem) {
-      is_update_needed_ = false;
-    }
-  }
-#else
-  is_update_needed_ = false;
-#endif
-
-  std::move(cb).Run(std::move(key_systems));
-}
diff --git a/chrome/renderer/media/chrome_key_systems_provider.h b/chrome/renderer/media/chrome_key_systems_provider.h
deleted file mode 100644
index 5753760..0000000
--- a/chrome/renderer/media/chrome_key_systems_provider.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_PROVIDER_H_
-#define CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_PROVIDER_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/default_tick_clock.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "media/base/key_system_properties.h"
-
-using KeySystemsProviderDelegate =
-    base::RepeatingCallback<void(media::KeySystemPropertiesVector*)>;
-
-class ChromeKeySystemsProvider {
- public:
-  ChromeKeySystemsProvider();
-  ChromeKeySystemsProvider(const ChromeKeySystemsProvider&) = delete;
-  ChromeKeySystemsProvider& operator=(const ChromeKeySystemsProvider&) = delete;
-  ~ChromeKeySystemsProvider();
-
-  // Gets supported key system properties.
-  void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb);
-
-  // Returns whether client key systems properties should be updated.
-  // TODO(chcunningham): Refactor this to a proper change "observer" API that is
-  // less fragile (don't assume GetSupportedKeySystems has just one caller).
-  bool IsKeySystemsUpdateNeeded();
-
-  void SetTickClockForTesting(const base::TickClock* tick_clock);
-
-  void SetProviderDelegateForTesting(KeySystemsProviderDelegate test_provider);
-
- private:
-  void OnSupportedKeySystemsReady(media::GetSupportedKeySystemsCB cb,
-                                  media::KeySystemPropertiesVector key_systems);
-
-  // Whether GetSupportedKeySystems() has ever been called.
-  bool has_updated_ = false;
-
-  // Whether a future update is needed. For example, when some potentially
-  // supported key systems are NOT supported yet. This could happen when the
-  // required component for a key system is not yet available.
-  bool is_update_needed_ = true;
-
-  // Throttle how often we signal an update is needed to avoid unnecessary high
-  // frequency of expensive IPC calls.
-  base::TimeTicks last_update_time_ticks_;
-  const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance();
-
-  // Ensure all methods are called from the same (Main) thread.
-  base::ThreadChecker thread_checker_;
-
-  // For unit tests to inject their own key systems. Will bypass adding default
-  // Chrome key systems when set.
-  KeySystemsProviderDelegate test_provider_;
-
-  base::WeakPtrFactory<ChromeKeySystemsProvider> weak_factory_{this};
-};
-
-#endif  // CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_PROVIDER_H_
diff --git a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc b/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
deleted file mode 100644
index 605c1cb..0000000
--- a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/renderer/media/chrome_key_systems_provider.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/test/bind.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/test/task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/widevine/cdm/buildflags.h"
-#include "third_party/widevine/cdm/widevine_cdm_common.h"
-
-namespace {
-
-class TestKeySystemProperties : public media::KeySystemProperties {
- public:
-  explicit TestKeySystemProperties(const std::string& key_system_name)
-      : key_system_name_(key_system_name) {}
-
-  std::string GetBaseKeySystemName() const override { return key_system_name_; }
-
-  bool IsSupportedInitDataType(
-      media::EmeInitDataType init_data_type) const override {
-    return false;
-  }
-
-  media::EmeConfigRule GetEncryptionSchemeConfigRule(
-      media::EncryptionScheme encryption_scheme) const override {
-    return media::EmeConfigRule::NOT_SUPPORTED;
-  }
-
-  media::SupportedCodecs GetSupportedCodecs() const override {
-    return media::EME_CODEC_NONE;
-  }
-
-  media::EmeConfigRule GetRobustnessConfigRule(
-      const std::string& key_system,
-      media::EmeMediaType media_type,
-      const std::string& requested_robustness,
-      const bool* /*hw_secure_requirement*/) const override {
-    return requested_robustness.empty() ? media::EmeConfigRule::SUPPORTED
-                                        : media::EmeConfigRule::NOT_SUPPORTED;
-  }
-
-  media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport()
-      const override {
-    return media::EmeSessionTypeSupport::NOT_SUPPORTED;
-  }
-
-  media::EmeFeatureSupport GetPersistentStateSupport() const override {
-    return media::EmeFeatureSupport::NOT_SUPPORTED;
-  }
-
-  media::EmeFeatureSupport GetDistinctiveIdentifierSupport() const override {
-    return media::EmeFeatureSupport::NOT_SUPPORTED;
-  }
-
- private:
-  const std::string key_system_name_;
-};
-
-class TestKeySystemsProviderDelegate {
- public:
-  TestKeySystemsProviderDelegate() : include_widevine_(false) {}
-
-  void AddTestKeySystems(
-      std::vector<std::unique_ptr<media::KeySystemProperties>>* key_systems) {
-    key_systems->emplace_back(
-        new TestKeySystemProperties("com.example.foobar"));
-
-    if (include_widevine_) {
-#if BUILDFLAG(ENABLE_WIDEVINE)
-      key_systems->emplace_back(
-          new TestKeySystemProperties(kWidevineKeySystem));
-#else
-      // Tests should only attempt to include Widevine when it is available.
-      NOTREACHED();
-#endif
-    }
-  }
-
-  void set_include_widevine(bool include_widevine) {
-    include_widevine_ = include_widevine;
-  }
-
- private:
-  bool include_widevine_;
-};
-
-void CopyKeySystems(media::KeySystemPropertiesVector* output,
-                    media::KeySystemPropertiesVector input) {
-  *output = std::move(input);
-}
-
-}  // namespace
-
-TEST(ChromeKeySystemsProviderTest, IsKeySystemsUpdateNeeded) {
-  base::test::TaskEnvironment task_environment_;
-  ChromeKeySystemsProvider key_systems_provider;
-  media::KeySystemPropertiesVector key_systems;
-
-  base::SimpleTestTickClock tick_clock;
-  key_systems_provider.SetTickClockForTesting(&tick_clock);
-
-  std::unique_ptr<TestKeySystemsProviderDelegate> provider_delegate(
-      new TestKeySystemsProviderDelegate());
-  key_systems_provider.SetProviderDelegateForTesting(
-      base::BindRepeating(&TestKeySystemsProviderDelegate::AddTestKeySystems,
-                          base::Unretained(provider_delegate.get())));
-
-  // IsKeySystemsUpdateNeeded() always returns true after construction.
-  EXPECT_TRUE(key_systems_provider.IsKeySystemsUpdateNeeded());
-
-  key_systems_provider.GetSupportedKeySystems(
-      base::BindOnce(&CopyKeySystems, &key_systems));
-
-  // No update needed immediately after GetSupportedKeySystems() call.
-  EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-
-  // Widevine not initially provided.
-  EXPECT_EQ(key_systems.size(), 1U);
-  EXPECT_EQ(key_systems[0]->GetBaseKeySystemName(), "com.example.foobar");
-
-  // This is timing related. The update interval for Widevine is 1000 ms.
-  EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-  tick_clock.Advance(base::Milliseconds(990));
-  EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-  tick_clock.Advance(base::Milliseconds(10));
-
-#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
-  // Require update once enough time has passed for builds that install Widevine
-  // as a component.
-  EXPECT_TRUE(key_systems_provider.IsKeySystemsUpdateNeeded());
-
-  // Now add Widevine.
-  provider_delegate->set_include_widevine(true);
-  key_systems.clear();
-
-  key_systems_provider.GetSupportedKeySystems(
-      base::BindOnce(&CopyKeySystems, &key_systems));
-
-  // Widevine should now be among the list.
-  bool found_widevine = false;
-  for (const auto& key_system_properties : key_systems) {
-    if (key_system_properties->GetBaseKeySystemName() == kWidevineKeySystem) {
-      found_widevine = true;
-      break;
-    }
-  }
-  EXPECT_TRUE(found_widevine);
-
-  // Update not needed now, nor later because Widevine has been described.
-  EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-  tick_clock.Advance(base::Milliseconds(1000));
-  EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-  tick_clock.Advance(base::Milliseconds(1000));
-  EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-#else
-  // No update needed for builds that either don't offer Widevine or do so
-  // as part of Chrome rather than component installer.
-  EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-#endif  // BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
-}
diff --git a/chrome/services/sharing/nearby/platform.cc b/chrome/services/sharing/nearby/platform.cc
index 694666e..604b83d 100644
--- a/chrome/services/sharing/nearby/platform.cc
+++ b/chrome/services/sharing/nearby/platform.cc
@@ -56,6 +56,19 @@
   return 0;
 }
 
+std::string ImplementationPlatform::GetDownloadPath(std::string& parent_folder,
+                                                    std::string& file_name) {
+  // This should return the <download_path>/parent_folder/file_name. For now we
+  // will just return an empty string, since chrome doesn't call this yet.
+  // TODO(b/223710122): Eventually chrome should implement this method.
+  NOTIMPLEMENTED();
+  return std::string("");
+}
+
+OSName ImplementationPlatform::GetCurrentOS() {
+  return OSName::kChromeOS;
+}
+
 std::unique_ptr<SubmittableExecutor>
 ImplementationPlatform::CreateSingleThreadExecutor() {
   return std::make_unique<chrome::SubmittableExecutor>(
@@ -109,6 +122,7 @@
   return std::make_unique<chrome::AtomicBoolean>(initial_value);
 }
 
+ABSL_DEPRECATED("This interface will be deleted in the near future.")
 std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
     std::int64_t payload_id,
     std::int64_t total_size) {
@@ -117,6 +131,17 @@
       connections.ExtractInputFile(payload_id));
 }
 
+std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
+    absl::string_view file_path,
+    size_t size) {
+  // This constructor is not called by Chrome. Returning nullptr, just in case.
+  // TODO(b/223710122): Eventually chrome should implement and use this
+  // constructor exclusively.
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+ABSL_DEPRECATED("This interface will be deleted in the near future.")
 std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
     std::int64_t payload_id) {
   auto& connections = connections::NearbyConnections::GetInstance();
@@ -124,6 +149,15 @@
       connections.ExtractOutputFile(payload_id));
 }
 
+std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
+    absl::string_view file_path) {
+  // This constructor is not called by Chrome. Returning nullptr, just in case.
+  // TODO(b/223710122): Eventually chrome should implement and use this
+  // constructor exclusively.
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
 std::unique_ptr<LogMessage> ImplementationPlatform::CreateLogMessage(
     const char* file,
     int line,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c9e0a66c..8e73823 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4834,7 +4834,6 @@
     "../browser/content_settings/page_specific_content_settings_unittest.cc",
     "../browser/content_settings/sound_content_setting_observer_unittest.cc",
     "../browser/custom_handlers/chrome_protocol_handler_registry_unittest.cc",
-    "../browser/download/bubble/download_display_controller_unittest.cc",
     "../browser/download/chrome_download_manager_delegate_unittest.cc",
     "../browser/download/deferred_client_wrapper_unittest.cc",
     "../browser/download/download_history_unittest.cc",
@@ -5046,6 +5045,7 @@
     "../browser/resource_coordinator/tab_load_tracker_unittest.cc",
     "../browser/resources_util_unittest.cc",
     "../browser/security_events/security_event_recorder_impl_unittest.cc",
+    "../browser/segmentation_platform/model_provider_factory_impl_unittest.cc",
     "../browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc",
     "../browser/signin/e2e_tests/test_accounts_util_unittest.cc",
 
@@ -5161,7 +5161,6 @@
     "../renderer/chrome_content_renderer_client_unittest.cc",
     "../renderer/chrome_render_frame_observer_unittest.cc",
     "../renderer/instant_restricted_id_cache_unittest.cc",
-    "../renderer/media/chrome_key_systems_provider_unittest.cc",
     "../renderer/media/flash_embed_rewrite_unittest.cc",
     "../renderer/net/net_error_helper_core_unittest.cc",
     "../renderer/plugins/plugin_uma_unittest.cc",
@@ -5326,6 +5325,9 @@
       "../browser/content_settings/generated_cookie_prefs_unittest.cc",
       "../browser/content_settings/generated_notification_pref_unittest.cc",
       "../browser/device_identity/device_oauth2_token_service_unittest.cc",
+      "../browser/download/bubble/download_bubble_controller_unittest.cc",
+      "../browser/download/bubble/download_bubble_prefs_unittest.cc",
+      "../browser/download/bubble/download_display_controller_unittest.cc",
       "../browser/download/download_shelf_context_menu_unittest.cc",
       "../browser/enterprise/signals/client_certificate_fetcher_unittest.cc",
       "../browser/metrics/power/power_metrics_reporter_unittest.cc",
@@ -6622,6 +6624,7 @@
         "//ash/components/disks:test_support",
         "//ash/components/login/auth:test_support",
         "//ash/components/login/session",
+        "//ash/components/multidevice:test_support",
         "//ash/components/proximity_auth",
         "//ash/components/proximity_auth:test_support",
         "//ash/public/cpp:test_support",
@@ -6650,7 +6653,6 @@
         "//chrome/services/sharing/public/cpp",
         "//chrome/services/sharing/public/cpp:unit_tests",
         "//chromeos/components/feature_usage:feature_usage",
-        "//chromeos/components/multidevice:test_support",
         "//chromeos/components/sync_wifi",
         "//chromeos/dbus",
         "//chromeos/dbus/attestation",
@@ -6827,6 +6829,7 @@
       "../browser/lacros/force_installed_tracker_lacros_unittest.cc",
       "../browser/lacros/lacros_memory_pressure_evaluator_unittest.cc",
       "../browser/lacros/lacros_url_handling_unittest.cc",
+      "../browser/lacros/launcher_search/search_util_unittest.cc",
       "../browser/lacros/metrics_reporting_observer_unittest.cc",
       "../browser/lacros/net/network_settings_translation_unittest.cc",
       "../browser/lacros/prefs_ash_observer_unittest.cc",
diff --git a/chrome/test/base/web_ui_test_data_source.cc b/chrome/test/base/web_ui_test_data_source.cc
index e378496..569e3c6 100644
--- a/chrome/test/base/web_ui_test_data_source.cc
+++ b/chrome/test/base/web_ui_test_data_source.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/test/base/web_ui_test_data_source.h"
 
+#include "chrome/common/webui_url_constants.h"
 #include "chrome/test/data/grit/webui_generated_test_resources.h"
 #include "chrome/test/data/grit/webui_generated_test_resources_map.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -12,7 +13,7 @@
 
 content::WebUIDataSource* CreateWebUITestDataSource() {
   content::WebUIDataSource* source =
-      content::WebUIDataSource::Create("webui-test");
+      content::WebUIDataSource::Create(chrome::kChromeUIWebUITestHost);
   source->AddResourcePaths(base::make_span(kWebuiGeneratedTestResources,
                                            kWebuiGeneratedTestResourcesSize));
 
diff --git a/chrome/test/data/browsing_topics/empty_page.html b/chrome/test/data/browsing_topics/empty_page.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/browsing_topics/empty_page.html
diff --git a/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html
diff --git a/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html.mock-http-headers b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html.mock-http-headers
new file mode 100644
index 0000000..c434c36
--- /dev/null
+++ b/chrome/test/data/browsing_topics/empty_page_browsing_topics_none.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Permissions-Policy: browsing-topics=()
diff --git a/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html
diff --git a/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html.mock-http-headers b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html.mock-http-headers
new file mode 100644
index 0000000..d27f17b
--- /dev/null
+++ b/chrome/test/data/browsing_topics/empty_page_interest_cohort_none.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Permissions-Policy: interest-cohort=()
diff --git a/chrome/test/data/browsing_topics/one_iframe_page.html b/chrome/test/data/browsing_topics/one_iframe_page.html
new file mode 100644
index 0000000..67d36c1
--- /dev/null
+++ b/chrome/test/data/browsing_topics/one_iframe_page.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <iframe src="empty_page.html" id="frame"></iframe>
+</body>
+</html>
diff --git a/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html
new file mode 100644
index 0000000..67d36c1
--- /dev/null
+++ b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <iframe src="empty_page.html" id="frame"></iframe>
+</body>
+</html>
diff --git a/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html.mock-http-headers b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html.mock-http-headers
new file mode 100644
index 0000000..584910a2
--- /dev/null
+++ b/chrome/test/data/browsing_topics/one_iframe_page_browsing_topics_allow_certain_origin.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Permissions-Policy: browsing-topics=(self "https://c.test:{{PORT}}")
diff --git a/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js b/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js
index 9dc41af6..b546c0c 100644
--- a/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js
+++ b/chrome/test/data/extensions/api_test/scripting/sub_frames/worker.js
@@ -23,21 +23,28 @@
 
 // Returns all frames in the given tab.
 async function getFramesInTab(tabId) {
-  // TODO(devlin): Update this when webNavigation supports promises directly.
-  const frames = await new Promise(resolve => {
-    chrome.webNavigation.getAllFrames({tabId: tabId}, resolve);
-  });
+  const frames = await chrome.webNavigation.getAllFrames({tabId: tabId});
   chrome.test.assertTrue(frames.length > 0);
   return frames;
 }
 
-// Returns the ID of the frame with the given `hostname`.
-function findFrameIdWithHostname(frames, hostname) {
+// Returns the frame with the given `hostname`.
+function findFrameWithHostname(frames, hostname) {
   const frame = frames.find(frame => {
     return (new URL(frame.url)).hostname == hostname;
   });
   chrome.test.assertTrue(!!frame, 'No frame with hostname: ' + hostname);
-  return frame.frameId;
+  return frame;
+}
+
+// Returns the ID of the frame with the given `hostname`.
+function findFrameIdWithHostname(frames, hostname) {
+  return findFrameWithHostname(frames, hostname).frameId;
+}
+
+// Returns the ID of the document with the given `hostname`.
+function findDocumentIdWithHostname(frames, hostname) {
+  return findFrameWithHostname(frames, hostname).documentId;
 }
 
 chrome.test.runTests([
@@ -87,20 +94,34 @@
     const query = {url: 'http://a.com/*'};
     const tab = await getSingleTab(query);
     const frames = await getFramesInTab(tab.id);
-    const frameId = findFrameIdWithHostname(frames, 'b.com');
+    const frame = findFrameWithHostname(frames, 'b.com');
 
-    const results = await chrome.scripting.executeScript({
+    let results = await chrome.scripting.executeScript({
       target: {
         tabId: tab.id,
-        frameIds: [frameId],
+        frameIds: [frame.frameId],
       },
       func: injectedFunction,
     });
     chrome.test.assertEq(1, results.length);
 
-    const resultUrl = new URL(results[0].result);
+    let resultUrl = new URL(results[0].result);
     chrome.test.assertEq('b.com', resultUrl.hostname);
-    chrome.test.assertEq(frameId, results[0].frameId);
+    chrome.test.assertEq(frame.frameId, results[0].frameId);
+
+    // Now try via documentId.
+    results = await chrome.scripting.executeScript({
+      target: {
+        tabId: tab.id,
+        documentIds: [frame.documentId],
+      },
+      func: injectedFunction,
+    });
+    chrome.test.assertEq(1, results.length);
+
+    resultUrl = new URL(results[0].result);
+    chrome.test.assertEq('b.com', resultUrl.hostname);
+    chrome.test.assertEq(frame.documentId, results[0].documentId);
     chrome.test.succeed();
   },
 
@@ -113,8 +134,12 @@
         findFrameIdWithHostname(frames, 'a.com'),
         findFrameIdWithHostname(frames, 'b.com'),
     ];
+    const documentIds = [
+        findDocumentIdWithHostname(frames, 'a.com'),
+        findDocumentIdWithHostname(frames, 'b.com'),
+    ];
 
-    const results = await chrome.scripting.executeScript({
+    let results = await chrome.scripting.executeScript({
       target: {
         tabId: tab.id,
         frameIds: frameIds,
@@ -125,13 +150,34 @@
 
     // Since we specified frame IDs, there's no guarantee as to the order
     // of the result. Compare a sorted output.
-    const resultUrls = results.map(result => {
+    let resultUrls = results.map(result => {
       return (new URL(result.result)).hostname;
     });
     chrome.test.assertEq(['a.com', 'b.com'], resultUrls.sort());
     chrome.test.assertEq(
         frameIds,
         results.map(result => result.frameId).sort());
+
+    // Now try the via documentId.
+    results = await chrome.scripting.executeScript({
+      target: {
+        tabId: tab.id,
+        documentIds: documentIds,
+      },
+      func: injectedFunction,
+    });
+    chrome.test.assertEq(2, results.length);
+
+    // Since we specified frame IDs, there's no guarantee as to the order
+    // of the result. Compare a sorted output.
+    resultUrls = results.map(result => {
+      return (new URL(result.result)).hostname;
+    });
+    chrome.test.assertEq(['a.com', 'b.com'], resultUrls.sort());
+    chrome.test.assertEq(
+        documentIds.sort(),
+        results.map(result => result.documentId).sort());
+
     chrome.test.succeed();
   },
 
@@ -140,20 +186,34 @@
     const query = {url: 'http://a.com/*'};
     const tab = await getSingleTab(query);
     const frames = await getFramesInTab(tab.id);
-    const frameId = findFrameIdWithHostname(frames, 'b.com');
+    const frame = findFrameWithHostname(frames, 'b.com');
 
-    const results = await chrome.scripting.executeScript({
+    let results = await chrome.scripting.executeScript({
       target: {
         tabId: tab.id,
-        frameIds: [frameId, frameId],
+        frameIds: [frame.frameId, frame.frameId],
       },
       func: injectedFunction,
     });
     chrome.test.assertEq(1, results.length);
 
-    const resultUrl = new URL(results[0].result);
+    let resultUrl = new URL(results[0].result);
     chrome.test.assertEq('b.com', resultUrl.hostname);
-    chrome.test.assertEq(frameId, results[0].frameId);
+    chrome.test.assertEq(frame.frameId, results[0].frameId);
+
+    // Now try the via documentId.
+    results = await chrome.scripting.executeScript({
+      target: {
+        tabId: tab.id,
+        documentIds: [frame.documentId, frame.documentId],
+      },
+      func: injectedFunction,
+    });
+    chrome.test.assertEq(1, results.length);
+
+    resultUrl = new URL(results[0].result);
+    chrome.test.assertEq('b.com', resultUrl.hostname);
+    chrome.test.assertEq(frame.documentId, results[0].documentId);
     chrome.test.succeed();
   },
 
@@ -170,6 +230,10 @@
         findFrameIdWithHostname(frames, 'b.com'),
         findFrameIdWithHostname(frames, 'c.com'),
     ];
+    const documentIds = [
+        findDocumentIdWithHostname(frames, 'b.com'),
+        findDocumentIdWithHostname(frames, 'c.com'),
+    ];
 
     await chrome.test.assertPromiseRejects(
         chrome.scripting.executeScript({
@@ -180,6 +244,17 @@
           func: injectedFunction,
         }),
         getAccessError(deniedFrame.url));
+
+    // Now try the via documentId.
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.executeScript({
+          target: {
+            tabId: tab.id,
+            documentIds: documentIds,
+          },
+          func: injectedFunction,
+        }),
+        getAccessError(deniedFrame.url));
     chrome.test.succeed();
   },
 
@@ -189,10 +264,15 @@
     const tab = await getSingleTab(query);
     const frames = await getFramesInTab(tab.id);
     const nonExistentFrameId = 99999;
+    const nonExistentDocumentId = '0123456789ABCDEF0123456789ABCDEF';
     const frameIds = [
         findFrameIdWithHostname(frames, 'b.com'),
         nonExistentFrameId,
     ];
+    const documentIds = [
+        findDocumentIdWithHostname(frames, 'b.com'),
+        nonExistentDocumentId,
+    ];
 
     await chrome.test.assertPromiseRejects(
         chrome.scripting.executeScript({
@@ -204,6 +284,18 @@
         }),
         `Error: No frame with id ${nonExistentFrameId} in ` +
             `tab with id ${tab.id}`);
+
+    // Now try the via documentId.
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.executeScript({
+          target: {
+            tabId: tab.id,
+            documentIds: documentIds,
+          },
+          func: injectedFunction,
+        }),
+        `Error: No document with id ${nonExistentDocumentId} in ` +
+            `tab with id ${tab.id}`);
     chrome.test.succeed();
   },
 
@@ -215,6 +307,9 @@
     const frameIds = [
         findFrameIdWithHostname(frames, 'b.com'),
     ];
+    const documentIds = [
+        findDocumentIdWithHostname(frames, 'b.com'),
+    ];
 
     await chrome.test.assertPromiseRejects(
         chrome.scripting.executeScript({
@@ -225,7 +320,71 @@
           },
           func: injectedFunction,
         }),
-        `Error: Cannot specify both 'allFrames' and 'frameIds'.`);
+        `Error: Cannot specify 'allFrames' if either 'frameIds' or ` +
+            `'documentIds' is specified.`);
+
+    // Now try the via documentId.
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.executeScript({
+          target: {
+            tabId: tab.id,
+            documentIds: documentIds,
+            allFrames: true,
+          },
+          func: injectedFunction,
+        }),
+        `Error: Cannot specify 'allFrames' if either 'frameIds' or ` +
+            `'documentIds' is specified.`);
     chrome.test.succeed();
   },
+
+  // Test that an extension cannot specify both frameIds and documentIds.
+  async function specifyingBothFrameIdsAndDocumentIdsIsInvalid() {
+    const query = {url: 'http://a.com/*'};
+    const tab = await getSingleTab(query);
+    const frames = await getFramesInTab(tab.id);
+    const frameIds = [
+        findFrameIdWithHostname(frames, 'b.com'),
+    ];
+    const documentIds = [
+        findDocumentIdWithHostname(frames, 'b.com'),
+    ];
+
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.executeScript({
+          target: {
+            tabId: tab.id,
+            documentIds: documentIds,
+            frameIds: frameIds
+          },
+          func: injectedFunction,
+        }),
+        `Error: Cannot specify both 'frameIds' and 'documentIds'.`);
+    chrome.test.succeed();
+  },
+
+  // Test that an extension cannot specify both a documentId from another tab.
+  async function specifyingBothFrameIdsAndDocumentIdsIsInvalid() {
+    const query_a = {url: 'http://a.com/*'};
+    const tab_a = await getSingleTab(query_a);
+    const query_d = {url: 'http://d.com/*'};
+    const tab_d = await getSingleTab(query_d);
+    const frames = await getFramesInTab(tab_d.id);
+    const documentIds = [
+        findDocumentIdWithHostname(frames, 'b.com'),
+    ];
+
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.executeScript({
+          target: {
+            tabId: tab_a.id,
+            documentIds: documentIds
+          },
+          func: injectedFunction,
+        }),
+        `Error: No document with id ${documentIds[0]} in ` +
+            `tab with id ${tab_a.id}`);
+    chrome.test.succeed();
+  },
+
 ]);
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn
new file mode 100644
index 0000000..4c4931d
--- /dev/null
+++ b/chrome/test/data/pdf/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/browser/resources/pdf/pdf.gni")
+import("//pdf/features.gni")
+import("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(enable_pdf, "enable_pdf check failed")
+
+generate_grd("build_grdp") {
+  grd_prefix = "pdf"
+  out_grd = "$target_gen_dir/resources.grdp"
+
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+  resource_path_prefix = "pdf"
+}
+
+ts_library("build_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  path_mappings = [
+    "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/*|" +
+        rebase_path("$root_gen_dir/chrome/browser/resources/pdf/tsc/*",
+                    target_gen_dir),
+    "chrome://webui-test/*|" +
+        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
+                    target_gen_dir),
+  ]
+  in_files = [
+    "basic_plugin_test.js",
+    "basic_test.js",
+    "beep_test.js",
+    "bookmarks_test.js",
+    "download_controls_test.js",
+    "fullscreen_test.js",
+    "gesture_detector_test.js",
+    "layout_test.js",
+    "material_elements_test.js",
+    "metrics_test.js",
+    "navigator_test.js",
+    "nobeep_test.js",
+    "page_change_test.js",
+    "params_parser_test.js",
+    "post_message_proxy_test.js",
+    "scroll_with_form_field_focused_test.js",
+    "test_util.js",
+    "title_test.js",
+    "touch_handling_test.js",
+    "viewer_password_dialog_test.js",
+    "viewer_pdf_sidenav_test.js",
+    "viewer_properties_dialog_test.js",
+    "viewer_thumbnail_bar_test.js",
+    "viewer_thumbnail_test.js",
+    "viewer_toolbar_test.js",
+    "viewport_scroller_test.js",
+    "viewport_test.js",
+    "whitespace_title_test.js",
+    "zoom_manager_test.js",
+  ]
+
+  if (enable_ink) {
+    in_files += [
+      "annotations_feature_enabled_test.js",
+      "annotations_toolbar_test.js",
+      "viewer_toolbar_dropdown_test.js",
+    ]
+  }
+
+  if (is_chromeos_ash) {
+    in_files += [ "printing_icon_test.js" ]
+  }
+
+  definitions = ts_definitions
+  deps = [ "//chrome/browser/resources/pdf:build_ts" ]
+}
diff --git a/chrome/test/data/pdf/tsconfig_base.json b/chrome/test/data/pdf/tsconfig_base.json
new file mode 100644
index 0000000..99a81eca
--- /dev/null
+++ b/chrome/test/data/pdf/tsconfig_base.json
@@ -0,0 +1,6 @@
+{
+  "extends": "../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true
+  }
+}
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 859137c..67dd4b2 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -15391,7 +15391,7 @@
         },
         "prefs": {
           "hsts.policy.upgrade_bypass_list": {
-            "location": "local_state"
+            "location": "user_profile"
           }
         }
       }
diff --git a/chrome/test/data/webui/.eslintrc.js b/chrome/test/data/webui/.eslintrc.js
index d5594eaa..8d6486f 100644
--- a/chrome/test/data/webui/.eslintrc.js
+++ b/chrome/test/data/webui/.eslintrc.js
@@ -6,14 +6,20 @@
   'env': {'browser': true, 'es6': true},
   'rules': {
     'no-restricted-properties': [
-      'error',
-      {
+      'error', {
         'object': 'MockInteractions',
         'property': 'tap',
         'message': 'Do not use on-tap handlers in prod code, and use the ' +
             'native click() method in tests. See more context at ' +
             'crbug.com/812035.',
       },
+      {
+        'object': 'test',
+        'property': 'only',
+        'message': 'test.only() silently disables other tests in the same ' +
+            'suite(). Did you forget deleting it before uploading? Use ' +
+            'test.skip() instead to explicitly disable certain test() cases.',
+      }
     ],
     'no-var': 'off',
     'prefer-const': 'off',
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index b4e276b..eefb01f 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -10,6 +10,7 @@
 import("//components/signin/features.gni")
 import("//crypto/features.gni")
 import("//extensions/buildflags/buildflags.gni")
+import("//pdf/features.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
@@ -659,6 +660,13 @@
     grdp_files += [ "$target_gen_dir/tab_strip/resources.grdp" ]
   }
 
+  if (enable_pdf) {
+    # Include PDF Viewer tests, since they are also served by
+    # chrome://webui-test, even though they reside in chrome/test/data/pdf/.
+    deps += [ "../pdf:build_grdp" ]
+    grdp_files += [ "$target_gen_dir/../pdf/resources.grdp" ]
+  }
+
   if (!is_android) {
     deps += [
       "support_tool:build_grdp",
diff --git a/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS
index 7027ab73..18377141 100644
--- a/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS
+++ b/chrome/test/data/webui/cr_components/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index c5d27a76..a86fddb 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -493,9 +493,12 @@
   }
 };
 
-TEST_F('CrSettingsPrivacyGuidePageTest', 'PrivacyGuidePageTests', function() {
-  runMochaSuite('PrivacyGuidePage');
-});
+// TODO(crbug.com/1307443): disabling due to flakiness on several builders.
+TEST_F(
+    'CrSettingsPrivacyGuidePageTest', 'DISABLED_PrivacyGuidePageTests',
+    function() {
+        runMochaSuite('PrivacyGuidePage');
+    });
 
 TEST_F(
     'CrSettingsPrivacyGuidePageTest', 'PrivacyGuideFragmentMetricsTests',
diff --git a/chrome/updater/constants.cc b/chrome/updater/constants.cc
index 649ad94..c9efb87 100644
--- a/chrome/updater/constants.cc
+++ b/chrome/updater/constants.cc
@@ -42,6 +42,7 @@
 const char kAppVersionSwitch[] = "app-version";
 const char kWakeSwitch[] = "wake";
 const char kTagSwitch[] = "tag";
+const char kInstallerDataSwitch[] = "installerdata";
 
 const char kServerServiceSwitch[] = "service";
 
@@ -86,6 +87,8 @@
 // Specifies that urls that can be cached by proxies are preferred.
 const char kDownloadPreferenceCacheable[] = "cacheable";
 
+const char kUTF8BOM[] = "\xEF\xBB\xBF";
+
 #if BUILDFLAG(IS_MAC)
 // The user defaults suite name.
 const char kUserDefaultsSuiteName[] = MAC_BUNDLE_IDENTIFIER_STRING ".defaults";
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index 032b71d..cb298af3 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -83,6 +83,12 @@
 // currently encoded as a ASCII string.
 extern const char kTagSwitch[];
 
+// The --installerdata=file.dat switch is passed to an installer if an
+// installdataindex is specified in the tag or if installerdata is passed in via
+// --appargs. The corresponding installerdata is written to file.dat with an
+// UTF8 encoding as well as a UTF8 BOM.
+extern const char kInstallerDataSwitch[];
+
 #if BUILDFLAG(IS_WIN)
 // A debug switch to indicate that --install is running from the `out` directory
 // of the build. When this switch is present, the setup picks up the run time
@@ -319,6 +325,10 @@
 
 extern const char kDownloadPreferenceCacheable[];
 
+// UTF8 byte order mark (BOM) used to prefix the contents of the installerdata
+// file.
+extern const char kUTF8BOM[];
+
 constexpr int kPolicyNotSet = -1;
 constexpr int kPolicyDisabled = 0;
 constexpr int kPolicyEnabled = 1;
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc
index de32b95..73101f1 100644
--- a/chrome/updater/installer.cc
+++ b/chrome/updater/installer.cc
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/notreached.h"
@@ -163,9 +164,10 @@
   // the prefs are updated asynchronously with the new |pv| and |fingerprint|.
   // The task sequencing guarantees that the prefs will be updated by the
   // time another CrxDataCallback is invoked, which needs updated values.
-  return RunApplicationInstaller(application_installer,
-                                 install_params->arguments,
-                                 std::move(progress_callback));
+  return RunApplicationInstaller(
+      application_installer, install_params->arguments,
+      WriteInstallerDataToTempFile(install_params->server_install_data),
+      std::move(progress_callback));
 }
 
 void Installer::InstallWithSyncPrimitives(
@@ -232,6 +234,7 @@
 Installer::Result Installer::RunApplicationInstaller(
     const base::FilePath& /*app_installer*/,
     const std::string& /*arguments*/,
+    const absl::optional<base::FilePath>& /*installer_data_file*/,
     ProgressCallback /*progress_callback*/) {
   NOTIMPLEMENTED();
   return Installer::Result(-1);
diff --git a/chrome/updater/installer.h b/chrome/updater/installer.h
index 6cf30ad3..8fd7c22 100644
--- a/chrome/updater/installer.h
+++ b/chrome/updater/installer.h
@@ -96,9 +96,11 @@
   // creating processes, mounting images, running scripts, and collecting
   // exit codes. The install progress, if it can be collected, is reported by
   // invoking the |progress_callback|.
-  Result RunApplicationInstaller(const base::FilePath& app_installer,
-                                 const std::string& arguments,
-                                 ProgressCallback progress_callback);
+  Result RunApplicationInstaller(
+      const base::FilePath& app_installer,
+      const std::string& arguments,
+      const absl::optional<base::FilePath>& installer_data_file,
+      ProgressCallback progress_callback);
 
   // Deletes recursively the install paths not matching the |pv_| version.
   void DeleteOlderInstallPaths();
diff --git a/chrome/updater/installer_mac.cc b/chrome/updater/installer_mac.cc
index 9f0a029..3b36eb0 100644
--- a/chrome/updater/installer_mac.cc
+++ b/chrome/updater/installer_mac.cc
@@ -10,18 +10,21 @@
 #include "base/strings/strcat.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/mac/install_from_archive.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
 Installer::Result Installer::RunApplicationInstaller(
     const base::FilePath& app_installer,
     const std::string& arguments,
+    const absl::optional<base::FilePath>& installer_data_file,
     ProgressCallback /*progress_callback*/) {
   DVLOG(1) << "Running application install from DMG at " << app_installer;
   // InstallFromArchive() returns the exit code of the script. 0 is success and
   // anything else should be an error.
-  const int exit_code = InstallFromArchive(app_installer, checker_path_, ap_,
-                                           updater_scope_, pv_, arguments);
+  const int exit_code =
+      InstallFromArchive(app_installer, checker_path_, ap_, updater_scope_, pv_,
+                         arguments, installer_data_file);
   return exit_code == 0 ? Result()
                         : Result(kErrorApplicationInstallerFailed, exit_code);
 }
diff --git a/chrome/updater/mac/install_from_archive.h b/chrome/updater/mac/install_from_archive.h
index 15d8889..b20e8fb 100644
--- a/chrome/updater/mac/install_from_archive.h
+++ b/chrome/updater/mac/install_from_archive.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace base {
 class FilePath;
 class Version;
@@ -49,12 +51,14 @@
 // Choose which type of archive to install from. Possible types of archives are
 // DMG, Zip and just the App. From there, it calls the archive specific
 // installation method.
-int InstallFromArchive(const base::FilePath& file_path,
-                       const base::FilePath& existence_checker_path,
-                       const std::string& ap,
-                       const UpdaterScope& scope,
-                       const base::Version& pv,
-                       const std::string& arguments);
+int InstallFromArchive(
+    const base::FilePath& file_path,
+    const base::FilePath& existence_checker_path,
+    const std::string& ap,
+    const UpdaterScope& scope,
+    const base::Version& pv,
+    const std::string& arguments,
+    const absl::optional<base::FilePath>& installer_data_file);
 
 }  // namespace updater
 
diff --git a/chrome/updater/mac/install_from_archive.mm b/chrome/updater/mac/install_from_archive.mm
index 70d090ef..498f510 100644
--- a/chrome/updater/mac/install_from_archive.mm
+++ b/chrome/updater/mac/install_from_archive.mm
@@ -26,9 +26,11 @@
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/version.h"
+#include "chrome/updater/constants.h"
 #include "chrome/updater/mac/mac_util.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -121,6 +123,7 @@
 int RunExecutable(const base::FilePath& existence_checker_path,
                   const std::string& ap,
                   const std::string& arguments,
+                  const absl::optional<base::FilePath>& installer_data_file,
                   const UpdaterScope& scope,
                   const base::Version& pv,
                   const base::FilePath& unpacked_path) {
@@ -171,6 +174,11 @@
         {"UPDATE_IS_MACHINE", scope == UpdaterScope::kSystem ? "1" : "0"},
         {"UNPACK_DIR", unpacked_path.value()},
     };
+    if (installer_data_file) {
+      options.environment.emplace(base::ToUpperASCII(kInstallerDataSwitch),
+                                  installer_data_file->value());
+    }
+
     int exit_code = 0;
     VLOG(1) << "Running " << command.GetCommandLineString();
     if (!base::LaunchProcess(command, options).WaitForExit(&exit_code))
@@ -304,12 +312,14 @@
 }
 }  // namespace
 
-int InstallFromArchive(const base::FilePath& file_path,
-                       const base::FilePath& existence_checker_path,
-                       const std::string& ap,
-                       const UpdaterScope& scope,
-                       const base::Version& pv,
-                       const std::string& arguments) {
+int InstallFromArchive(
+    const base::FilePath& file_path,
+    const base::FilePath& existence_checker_path,
+    const std::string& ap,
+    const UpdaterScope& scope,
+    const base::Version& pv,
+    const std::string& arguments,
+    const absl::optional<base::FilePath>& installer_data_file) {
   const std::map<std::string,
                  int (*)(const base::FilePath&,
                          base::OnceCallback<int(const base::FilePath&)>)>
@@ -323,7 +333,7 @@
     if (base::PathExists(new_path)) {
       return entry.second(
           new_path, base::BindOnce(&RunExecutable, existence_checker_path, ap,
-                                   arguments, scope, pv));
+                                   arguments, installer_data_file, scope, pv));
     }
   }
 
diff --git a/chrome/updater/mac/setup/setup_unittest.mm b/chrome/updater/mac/setup/setup_unittest.mm
index 5f01cd1..11fb173f 100644
--- a/chrome/updater/mac/setup/setup_unittest.mm
+++ b/chrome/updater/mac/setup/setup_unittest.mm
@@ -163,7 +163,7 @@
   ASSERT_TRUE(base::PathExists(dmg_file_path));
   ASSERT_NE(updater::InstallFromArchive(dmg_file_path, {}, {},
                                         updater::UpdaterScope::kUser,
-                                        base::Version("0"), {}),
+                                        base::Version("0"), {}, {}),
             0);
 }
 
@@ -175,7 +175,7 @@
   ASSERT_TRUE(base::PathExists(dmg_file_path));
   ASSERT_NE(updater::InstallFromArchive(dmg_file_path, {}, {},
                                         updater::UpdaterScope::kUser,
-                                        base::Version("0"), "arg2"),
+                                        base::Version("0"), "arg2", {}),
             0);
 }
 
@@ -192,7 +192,7 @@
 
   ASSERT_EQ(updater::InstallFromArchive(dmg_file_path, installed_app_path, {},
                                         updater::UpdaterScope::kUser,
-                                        base::Version(kTestAppVersion), {}),
+                                        base::Version(kTestAppVersion), {}, {}),
             0);
 }
 
@@ -211,7 +211,7 @@
   std::string args = base::StrCat({kTestAppVersion, " arg1 arg2"});
   ASSERT_EQ(updater::InstallFromArchive(dmg_file_path, installed_app_path, {},
                                         updater::UpdaterScope::kUser,
-                                        base::Version("0"), args),
+                                        base::Version("0"), args, {}),
             0);
 }
 
@@ -220,28 +220,29 @@
   ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
   test_dir = test_dir.Append("updater");
 
-  ASSERT_EQ(updater::InstallFromArchive(
-                test_dir.Append("setup_test_envcheck").Append("marker.app"),
-                base::FilePath::FromASCII("xc_path"), "ap",
-                updater::UpdaterScope::kUser, base::Version("0"), "arg1 arg2"),
-            0);
+  ASSERT_EQ(
+      updater::InstallFromArchive(
+          test_dir.Append("setup_test_envcheck").Append("marker.app"),
+          base::FilePath::FromASCII("xc_path"), "ap",
+          updater::UpdaterScope::kUser, base::Version("0"), "arg1 arg2", {}),
+      0);
 
   ASSERT_EQ(
       updater::InstallFromArchive(
           test_dir.Append("setup_test_preinstallfailure").Append("marker.app"),
-          {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}),
+          {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}, {}),
       1);
 
   ASSERT_EQ(
       updater::InstallFromArchive(
           test_dir.Append("setup_test_installfailure").Append("marker.app"), {},
-          {}, updater::UpdaterScope::kUser, base::Version("0"), {}),
+          {}, updater::UpdaterScope::kUser, base::Version("0"), {}, {}),
       2);
 
   ASSERT_EQ(
       updater::InstallFromArchive(
           test_dir.Append("setup_test_postinstallfailure").Append("marker.app"),
-          {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}),
+          {}, {}, updater::UpdaterScope::kUser, base::Version("0"), {}, {}),
       3);
 }
 
diff --git a/chrome/updater/util.cc b/chrome/updater/util.cc
index b0af7e1e..90c6856e 100644
--- a/chrome/updater/util.cc
+++ b/chrome/updater/util.cc
@@ -15,6 +15,7 @@
 
 #include "base/base_paths.h"
 #include "base/command_line.h"
+#include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -302,4 +303,30 @@
 
 #endif  // BUILDFLAG(IS_WIN)
 
+absl::optional<base::FilePath> WriteInstallerDataToTempFile(
+    const std::string& installer_data) {
+  VLOG(2) << __func__ << ": " << installer_data;
+  if (installer_data.empty())
+    return absl::nullopt;
+
+  base::FilePath module_dir;
+  if (!base::PathService::Get(base::DIR_MODULE, &module_dir))
+    return absl::nullopt;
+
+  base::FilePath path;
+  base::File file = base::CreateAndOpenTemporaryFileInDir(module_dir, &path);
+  if (!file.IsValid())
+    return absl::nullopt;
+
+  const std::string installer_data_utf8_bom =
+      base::StrCat({kUTF8BOM, installer_data});
+  if (file.Write(0, installer_data_utf8_bom.c_str(),
+                 installer_data_utf8_bom.length()) == -1) {
+    VLOG(2) << __func__ << " file.Write failed";
+    return absl::nullopt;
+  }
+
+  return path;
+}
+
 }  // namespace updater
diff --git a/chrome/updater/util.h b/chrome/updater/util.h
index 8dc15e5..6f0441f 100644
--- a/chrome/updater/util.h
+++ b/chrome/updater/util.h
@@ -195,6 +195,12 @@
 
 #endif  // BUILDFLAG(IS_WIN)
 
+// Writes the provided string prefixed with the UTF8 byte order mark to a
+// temporary file. The temporary file is created in the same directory as the
+// current exe.
+absl::optional<base::FilePath> WriteInstallerDataToTempFile(
+    const std::string& installer_data);
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_UTIL_H_
diff --git a/chrome/updater/util_unittest.cc b/chrome/updater/util_unittest.cc
index 60fad81..9e40e9f 100644
--- a/chrome/updater/util_unittest.cc
+++ b/chrome/updater/util_unittest.cc
@@ -5,6 +5,9 @@
 #include "chrome/updater/util.h"
 
 #include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/strings/strcat.h"
 #include "base/test/scoped_command_line.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/tag.h"
@@ -38,4 +41,20 @@
   }
 }
 
+TEST(Util, WriteInstallerDataToTempFile) {
+  EXPECT_FALSE(WriteInstallerDataToTempFile(""));
+
+  const std::string kInstallerData =
+      R"({"distribution":{"msi":true,"allow_downgrade":false}})";
+  const absl::optional<base::FilePath> installer_data_file =
+      WriteInstallerDataToTempFile(kInstallerData);
+  ASSERT_TRUE(installer_data_file);
+
+  std::string contents;
+  EXPECT_TRUE(base::ReadFileToString(*installer_data_file, &contents));
+  EXPECT_EQ(base::StrCat({kUTF8BOM, kInstallerData}), contents);
+
+  EXPECT_TRUE(base::DeleteFile(*installer_data_file));
+}
+
 }  // namespace updater
diff --git a/chrome/updater/win/installer_api.cc b/chrome/updater/win/installer_api.cc
index 8eadc18..e331030f 100644
--- a/chrome/updater/win/installer_api.cc
+++ b/chrome/updater/win/installer_api.cc
@@ -325,6 +325,7 @@
 Installer::Result Installer::RunApplicationInstaller(
     const base::FilePath& app_installer,
     const std::string& arguments,
+    const absl::optional<base::FilePath>& installer_data_file,
     ProgressCallback progress_callback) {
   if (!app_installer.MatchesExtension(L".exe") &&
       !app_installer.MatchesExtension(L".msi")) {
@@ -337,10 +338,8 @@
   const std::wstring argsw = base::UTF8ToWide(arguments);
   const std::wstring cmdline =
       app_installer.MatchesExtension(L".msi")
-          ? BuildMsiCommandLine(argsw, app_installer)
-          : base::StrCat(
-                {base::CommandLine(app_installer).GetCommandLineString(), L" ",
-                 argsw});
+          ? BuildMsiCommandLine(argsw, installer_data_file, app_installer)
+          : BuildExeCommandLine(argsw, installer_data_file, app_installer);
   VLOG(1) << "Running application installer: " << cmdline;
 
   base::LaunchOptions options;
diff --git a/chrome/updater/win/setup/uninstall.cc b/chrome/updater/win/setup/uninstall.cc
index a77e6e3b..8f7383e 100644
--- a/chrome/updater/win/setup/uninstall.cc
+++ b/chrome/updater/win/setup/uninstall.cc
@@ -161,6 +161,9 @@
       uninstall_list->Rollback();
       return kErrorFailedToDeleteRegistryKeys;
     }
+
+    // TODO(crbug.com/1307528) : Windows Uninstall discrepancies need fixing.
+    DeleteGoogleUpdateEntries(scope, key);
   }
 
   DeleteComInterfaces(key, uninstall_all);
@@ -171,8 +174,6 @@
   if (scope == UpdaterScope::kUser)
     UnregisterUserRunAtStartup(GetTaskNamePrefix(scope));
 
-  DeleteGoogleUpdateEntries(scope, key);
-
   return RunUninstallScript(scope, uninstall_all);
 }
 
diff --git a/chrome/updater/win/win_util.cc b/chrome/updater/win/win_util.cc
index cd02dc4..09262e4d 100644
--- a/chrome/updater/win/win_util.cc
+++ b/chrome/updater/win/win_util.cc
@@ -666,15 +666,45 @@
                            COMGLB_EXCEPTION_DONOT_HANDLE);
 }
 
-std::wstring BuildMsiCommandLine(const std::wstring& arguments,
-                                 const base::FilePath& msi_installer) {
+std::wstring BuildMsiCommandLine(
+    const std::wstring& arguments,
+    const absl::optional<base::FilePath>& installer_data_file,
+    const base::FilePath& msi_installer) {
   if (!msi_installer.MatchesExtension(L".msi")) {
     return std::wstring();
   }
 
   return base::StrCat(
-      {L"msiexec ", arguments, L" REBOOT=ReallySuppress /qn /i \"",
-       msi_installer.value(), L"\" /log \"", msi_installer.value(), L".log\""});
+      {L"msiexec ", arguments,
+       installer_data_file
+           ? base::StrCat(
+                 {L" ",
+                  base::UTF8ToWide(base::ToUpperASCII(kInstallerDataSwitch)),
+                  L"=\"", installer_data_file->value(), L"\""})
+           : L"",
+       L" REBOOT=ReallySuppress /qn /i \"", msi_installer.value(),
+       L"\" /log \"", msi_installer.value(), L".log\""});
+}
+
+std::wstring BuildExeCommandLine(
+    const std::wstring& arguments,
+    const absl::optional<base::FilePath>& installer_data_file,
+    const base::FilePath& exe_installer) {
+  if (!exe_installer.MatchesExtension(L".exe")) {
+    return std::wstring();
+  }
+
+  return base::StrCat({base::CommandLine(exe_installer).GetCommandLineString(),
+                       L" ", arguments, [&installer_data_file]() {
+                         if (!installer_data_file)
+                           return std::wstring();
+
+                         base::CommandLine installer_data_args(
+                             base::CommandLine::NO_PROGRAM);
+                         installer_data_args.AppendSwitchPath(
+                             kInstallerDataSwitch, *installer_data_file);
+                         return installer_data_args.GetCommandLineString();
+                       }()});
 }
 
 bool IsServiceRunning(const std::wstring& service_name) {
diff --git a/chrome/updater/win/win_util.h b/chrome/updater/win/win_util.h
index 55899a0..c9c948d 100644
--- a/chrome/updater/win/win_util.h
+++ b/chrome/updater/win/win_util.h
@@ -214,11 +214,20 @@
 // program state.
 [[nodiscard]] HRESULT DisableCOMExceptionHandling();
 
-// Builds a command line running `MSIExec` on the provided `msi_installer` and
-// `arguments`, with added logging to a log file in the same directory as the
-// MSI installer.
-std::wstring BuildMsiCommandLine(const std::wstring& arguments,
-                                 const base::FilePath& msi_installer);
+// Builds a command line running `MSIExec` on the provided
+// `msi_installer`,`arguments`, and `installer_data_file`, with added logging to
+// a log file in the same directory as the MSI installer.
+std::wstring BuildMsiCommandLine(
+    const std::wstring& arguments,
+    const absl::optional<base::FilePath>& installer_data_file,
+    const base::FilePath& msi_installer);
+
+// Builds a command line running the provided `exe_installer`, `arguments`, and
+// `installer_data_file`.
+std::wstring BuildExeCommandLine(
+    const std::wstring& arguments,
+    const absl::optional<base::FilePath>& installer_data_file,
+    const base::FilePath& exe_installer);
 
 // Returns `true` if the service specified is currently running or starting.
 bool IsServiceRunning(const std::wstring& service_name);
diff --git a/chrome/updater/win/win_util_unittest.cc b/chrome/updater/win/win_util_unittest.cc
index b3eecc1..a33b5bb 100644
--- a/chrome/updater/win/win_util_unittest.cc
+++ b/chrome/updater/win/win_util_unittest.cc
@@ -67,15 +67,42 @@
 }
 
 TEST(WinUtil, BuildMsiCommandLine) {
-  EXPECT_STREQ(L"", BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"),
+  EXPECT_STREQ(L"", BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
                                         base::FilePath(L"NotMsi.exe"))
                         .c_str());
   EXPECT_STREQ(
       L"msiexec arg1 arg2 arg3 REBOOT=ReallySuppress /qn /i \"c:\\my "
       L"path\\YesMsi.msi\" /log \"c:\\my path\\YesMsi.msi.log\"",
-      BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"),
+      BuildMsiCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
                           base::FilePath(L"c:\\my path\\YesMsi.msi"))
           .c_str());
+  EXPECT_STREQ(
+      L"msiexec arg1 arg2 arg3 INSTALLERDATA=\"c:\\my path\\installer data "
+      L"file.dat\" REBOOT=ReallySuppress /qn /i \"c:\\my "
+      L"path\\YesMsi.msi\" /log \"c:\\my path\\YesMsi.msi.log\"",
+      BuildMsiCommandLine(
+          std::wstring(L"arg1 arg2 arg3"),
+          base::FilePath(L"c:\\my path\\installer data file.dat"),
+          base::FilePath(L"c:\\my path\\YesMsi.msi"))
+          .c_str());
+}
+
+TEST(WinUtil, BuildExeCommandLine) {
+  EXPECT_STREQ(L"", BuildExeCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
+                                        base::FilePath(L"NotExe.msi"))
+                        .c_str());
+  EXPECT_STREQ(L"\"c:\\my path\\YesExe.exe\" arg1 arg2 arg3",
+               BuildExeCommandLine(std::wstring(L"arg1 arg2 arg3"), {},
+                                   base::FilePath(L"c:\\my path\\YesExe.exe"))
+                   .c_str());
+  EXPECT_STREQ(
+      L"\"c:\\my path\\YesExe.exe\" arg1 arg2 arg3 --installerdata=\"c:\\my "
+      L"path\\installer data file.dat\"",
+      BuildExeCommandLine(
+          std::wstring(L"arg1 arg2 arg3"),
+          base::FilePath(L"c:\\my path\\installer data file.dat"),
+          base::FilePath(L"c:\\my path\\YesExe.exe"))
+          .c_str());
 }
 
 }  // namespace updater
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
index 947cf5cc..07c0e6a 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -213,6 +213,14 @@
     protected void onStop() {
         if (DEBUG) Log.d(TAG, "onStop");
         mStartedState.reset();
+        // If this device is in "lock task mode," then leaving the Activity will not return to the
+        // Home screen and there will be no affordance for the user to return to this Activity.
+        // When in this mode, leaving the Activity should tear down the Cast app.
+        if (isInLockTaskMode(this)) {
+            CastWebContentsComponent.onComponentClosed(
+                    CastWebContentsIntentUtils.getSessionId(getIntent()));
+            mIsFinishingState.set("User exit while in lock task mode");
+        }
         super.onStop();
     }
 
@@ -260,16 +268,6 @@
     @Override
     public void onUserLeaveHint() {
         if (DEBUG) Log.d(TAG, "onUserLeaveHint");
-        // If this device is in "lock task mode," then leaving the Activity will not return to the
-        // Home screen and there will be no affordance for the user to return to this Activity.
-        // When in this mode, leaving the Activity should tear down the Cast app.
-        Context ctx = getApplicationContext();
-        if (isInLockTaskMode(ctx)) {
-            CastWebContentsComponent.onComponentClosed(
-                    CastWebContentsIntentUtils.getSessionId(getIntent()));
-            mIsFinishingState.set("User exit while in lock task mode");
-            return;
-        }
         if (canUsePictureInPicture() && !canAutoEnterPictureInPicture()) {
             enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
         }
diff --git a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java
index 3617b9d..72c1511e 100644
--- a/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java
+++ b/chromecast/browser/android/junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java
@@ -344,38 +344,35 @@
     }
 
     @Test
-    public void testDoesNotCloseAppWhenUserLeave() {
+    public void testDoesNotCloseAppWhenActivityStops() {
         mShadowActivityManager.setLockTaskModeState(ActivityManager.LOCK_TASK_MODE_NONE);
         mActivityLifecycle.create().start().resume();
         verifyBroadcastedIntent(
                 filterFor(CastWebContentsIntentUtils.ACTION_ACTIVITY_STOPPED), () -> {
-                    mActivity.onUserLeaveHint();
+                    mActivityLifecycle.pause().stop();
                     assertFalse(mShadowActivity.isFinishing());
-                    mActivityLifecycle.pause().stop().destroy();
                 }, false);
     }
 
     @Test
-    public void testClosesWhenUserLeaveInLockTaskMode() {
+    public void testClosesWhenActivityStopsInLockTaskMode() {
         mShadowActivityManager.setLockTaskModeState(ActivityManager.LOCK_TASK_MODE_LOCKED);
         mActivityLifecycle.create().start().resume();
         verifyBroadcastedIntent(
                 filterFor(CastWebContentsIntentUtils.ACTION_ACTIVITY_STOPPED), () -> {
-                    mActivity.onUserLeaveHint();
+                    mActivityLifecycle.pause().stop();
                     assertTrue(mShadowActivity.isFinishing());
-                    mActivityLifecycle.pause().stop().destroy();
                 }, true);
     }
 
     @Test
-    public void testClosesWhenUserLeaveInLockTaskModePinned() {
+    public void testClosesWhenActivityStopsInLockTaskModePinned() {
         mShadowActivityManager.setLockTaskModeState(ActivityManager.LOCK_TASK_MODE_PINNED);
         mActivityLifecycle.create().start().resume();
         verifyBroadcastedIntent(
                 filterFor(CastWebContentsIntentUtils.ACTION_ACTIVITY_STOPPED), () -> {
-                    mActivity.onUserLeaveHint();
+                    mActivityLifecycle.pause().stop();
                     assertTrue(mShadowActivity.isFinishing());
-                    mActivityLifecycle.pause().stop().destroy();
                 }, true);
     }
 
diff --git a/chromecast/browser/cast_download_manager_delegate.cc b/chromecast/browser/cast_download_manager_delegate.cc
index 827b54f4..a9979152 100644
--- a/chromecast/browser/cast_download_manager_delegate.cc
+++ b/chromecast/browser/cast_download_manager_delegate.cc
@@ -32,7 +32,7 @@
   std::move(*callback).Run(
       empty, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
-      download::DownloadItem::MixedContentStatus::UNKNOWN, empty,
+      download::DownloadItem::MixedContentStatus::UNKNOWN, empty, empty,
       absl::nullopt /*download_schedule*/,
       download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
   return true;
diff --git a/chromeos/components/BUILD.gn b/chromeos/components/BUILD.gn
index c41601d7..9f2ee86b9b 100644
--- a/chromeos/components/BUILD.gn
+++ b/chromeos/components/BUILD.gn
@@ -26,7 +26,6 @@
     "//chromeos/components/local_search_service:unit_tests",
     "//chromeos/components/local_search_service/public/mojom:unit_tests",
     "//chromeos/components/mojo_bootstrap:unit_tests",
-    "//chromeos/components/multidevice:unit_tests",
     "//chromeos/components/onc:unit_tests",
     "//chromeos/components/quick_answers:unit_tests",
     "//chromeos/components/sensors:unit_tests",
diff --git a/chromeos/components/multidevice/DEPS b/chromeos/components/multidevice/DEPS
deleted file mode 100644
index 6583fd24..0000000
--- a/chromeos/components/multidevice/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
-  "+ash/services/device_sync/proto",
-  "+device/bluetooth/public/cpp",
-  "+mojo/public/cpp",
-  "+third_party/securemessage",
-]
diff --git a/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc b/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc
index 998b86c8..b845e31 100644
--- a/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc
+++ b/chromeos/crosapi/mojom/app_service_types_traits_unittest.cc
@@ -44,9 +44,8 @@
   input->paused = false;
 
   auto intent_filter = std::make_unique<apps::IntentFilter>();
-  apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "https",
-                                     apps::PatternMatchType::kNone,
-                                     intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https",
+                                         apps::PatternMatchType::kNone);
   intent_filter->activity_name = "activity_name";
   intent_filter->activity_label = "activity_label";
   input->intent_filters.push_back(std::move(intent_filter));
@@ -135,9 +134,8 @@
   input->paused = false;
 
   auto intent_filter = std::make_unique<apps::IntentFilter>();
-  apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "https",
-                                     apps::PatternMatchType::kNone,
-                                     intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https",
+                                         apps::PatternMatchType::kNone);
   input->intent_filters.push_back(std::move(intent_filter));
   input->window_mode = apps::WindowMode::kBrowser;
   input->allow_uninstall = true;
@@ -558,30 +556,22 @@
 TEST(AppServiceTypesTraitsTest, RoundTripIntentFilters) {
   auto input = std::make_unique<apps::App>(apps::AppType::kArc, "abcdefg");
   auto intent_filter = std::make_unique<apps::IntentFilter>();
-  apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "1",
-                                     apps::PatternMatchType::kNone,
-                                     intent_filter);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kHost, "2",
-                                     apps::PatternMatchType::kLiteral,
-                                     intent_filter);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kPattern, "3",
-                                     apps::PatternMatchType::kPrefix,
-                                     intent_filter);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kAction, "4",
-                                     apps::PatternMatchType::kGlob,
-                                     intent_filter);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kMimeType, "5",
-                                     apps::PatternMatchType::kMimeType,
-                                     intent_filter);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "6",
-                                     apps::PatternMatchType::kMimeType,
-                                     intent_filter);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "7",
-                                     apps::PatternMatchType::kFileExtension,
-                                     intent_filter);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kHost, "8",
-                                     apps::PatternMatchType::kSuffix,
-                                     intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "1",
+                                         apps::PatternMatchType::kNone);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, "2",
+                                         apps::PatternMatchType::kLiteral);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern, "3",
+                                         apps::PatternMatchType::kPrefix);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kAction, "4",
+                                         apps::PatternMatchType::kGlob);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kMimeType, "5",
+                                         apps::PatternMatchType::kMimeType);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kFile, "6",
+                                         apps::PatternMatchType::kMimeType);
+  intent_filter->AddSingleValueCondition(
+      apps::ConditionType::kFile, "7", apps::PatternMatchType::kFileExtension);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, "8",
+                                         apps::PatternMatchType::kSuffix);
   input->intent_filters.push_back(std::move(intent_filter));
 
   apps::AppPtr output;
diff --git a/chromeos/crosapi/mojom/launcher_search.mojom b/chromeos/crosapi/mojom/launcher_search.mojom
index 6571d4bb..31dafdc 100644
--- a/chromeos/crosapi/mojom/launcher_search.mojom
+++ b/chromeos/crosapi/mojom/launcher_search.mojom
@@ -22,6 +22,8 @@
   // Search session has been cancelled due to a newer query. No more results
   // will be sent.
   kCancelled = 3,
+  // The AutocompleteController is unavailable. No more results will be sent.
+  [MinVersion=1] kBackendUnavailable = 4,
 };
 
 // Enum represents the result type.
@@ -34,7 +36,7 @@
 };
 
 // Struct represents search result.
-// Next min ID: 14
+// Next min ID: 15
 [Stable]
 struct SearchResult {
   // Type of the result. Used to distinguish between different types of result.
@@ -94,6 +96,7 @@
     kSearch = 5,
     kHistory = 6,
     kCalculator = 7,
+    [MinVersion=1] kOpenTab = 8,
   };
 
   // Enum representing the Omnibox answer subtype.
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index a6274265..6c66e4fc 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-101-4928.0-1647252934-benchmark-101.0.4948.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-101-4928.0-1647252934-benchmark-101.0.4950.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 43d65629..6e3f038 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-101-4928.0-1647252364-benchmark-101.0.4948.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-101-4928.0-1647252364-benchmark-101.0.4950.0-r1-redacted.afdo.xz
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 3b9df0b..1f80dbc 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -498,7 +498,10 @@
   }
 
   if (!is_android) {
-    deps += [ "//components/commerce/core:commerce_heuristics_data_unittests" ]
+    deps += [
+      "//components/commerce/core:commerce_heuristics_data_unittests",
+      "//components/commerce/core:feature_list_unittests",
+    ]
   }
 
   if (enable_library_cdms) {
diff --git a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
index 702dc6fa..f8614ef7 100644
--- a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
@@ -31,7 +31,9 @@
   VIRTUAL_CARD_ENROLLMENT_BUBBLE_NOT_INTERACTED = 3,
   // The bubble lost focus and was deactivated.
   VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS = 4,
-  kMaxValue = VIRTUAL_CARD_ENROLLMENT_BUBBLE_LOST_FOCUS,
+  // The user cancelled the bubble.
+  VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED = 5,
+  kMaxValue = VIRTUAL_CARD_ENROLLMENT_BUBBLE_CANCELLED,
 };
 
 // Metrics to record the source that prompted the virtual card enrollment
diff --git a/components/autofill_assistant/android/BUILD.gn b/components/autofill_assistant/android/BUILD.gn
index 6eac8dd..6b96c0d 100644
--- a/components/autofill_assistant/android/BUILD.gn
+++ b/components/autofill_assistant/android/BUILD.gn
@@ -348,7 +348,6 @@
     "internal/java/res/drawable/autofill_assistant_default_details.xml",
     "internal/java/res/drawable/autofill_assistant_details_bg.xml",
     "internal/java/res/drawable/autofill_assistant_details_list_divider.xml",
-    "internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml",
     "internal/java/res/drawable/autofill_assistant_rounded_corner_background.xml",
     "internal/java/res/drawable/autofill_assistant_swipe_indicator.xml",
     "internal/java/res/drawable/ic_add_outline_white_24dp.xml",
@@ -404,7 +403,6 @@
     "internal/java/res/layout/autofill_assistant_popup_list_section.xml",
     "internal/java/res/layout/autofill_assistant_static_text_section.xml",
     "internal/java/res/menu/profile_icon_menu.xml",
-    "internal/java/res/values-night-v17/colors.xml",
     "internal/java/res/values-v17/attrs.xml",
     "internal/java/res/values-v17/colors.xml",
     "internal/java/res/values-v17/dimens.xml",
diff --git a/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml b/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml
deleted file mode 100644
index 2d0b4a5..0000000
--- a/components/autofill_assistant/android/internal/java/res/drawable/autofill_assistant_lightblue_rect_bg.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<!-- Copyright 2019 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <corners
-        android:radius="4dp" />
-    <solid
-        android:color="@color/autofill_assistant_light_blue" />
-</shape>
\ No newline at end of file
diff --git a/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml b/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml
index 0b7cd3ef..98695c5d 100644
--- a/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml
+++ b/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_payment_request_terms_and_conditions.xml
@@ -28,7 +28,7 @@
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/autofill_assistant_bottombar_horizontal_spacing"
         android:layout_marginEnd="@dimen/autofill_assistant_bottombar_horizontal_spacing"
-        android:background="@drawable/autofill_assistant_lightblue_rect_bg"
         android:padding="8dp"
-        android:textAppearance="@style/TextAppearance.TextSmall.Secondary"/>
+        android:paddingStart="40dp"
+        android:textAppearance="@style/TextAppearance.TextSmall.Secondary.Baseline.Dark"/>
 </LinearLayout>
diff --git a/components/autofill_assistant/android/internal/java/res/values-night-v17/colors.xml b/components/autofill_assistant/android/internal/java/res/values-night-v17/colors.xml
deleted file mode 100644
index ae4be5f..0000000
--- a/components/autofill_assistant/android/internal/java/res/values-night-v17/colors.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2014 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<resources>
-    <!--
-    TODO(crbuc.com/806868): Use Chrome approved colors and remove this.
-
-    Please see src/ui/android/java/res/values/colors.xml for the shared common colors.
-    -->
-    <color name="autofill_assistant_light_blue">@color/modern_grey_900</color>
-</resources>
diff --git a/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml b/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml
index 56c401a..e045d57 100644
--- a/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml
+++ b/components/autofill_assistant/android/internal/java/res/values-v17/colors.xml
@@ -9,7 +9,6 @@
 
     Please see src/ui/android/java/res/values/colors.xml for the shared common colors.
     -->
-    <color name="autofill_assistant_light_blue">@color/modern_blue_600_alpha_10</color>
     <color name="autofill_assistant_actions_shadow_color">@color/modern_grey_100</color>
     <color name="autofill_assistant_details_divider_color">@color/default_chip_outline_color</color>
 </resources>
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 7f315c8..0dfb6c9b 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1662,6 +1662,9 @@
     // Timeout, defaults to 5s.
     // TODO(b/218482826): Consider moving this to settings.
     optional int32 model_timeout_ms = 3 [default = 5000];
+    // If true, ignore the objective and treat it as a wildcard '*' when
+    // matching.
+    optional bool ignore_objective = 4;
   }
   optional SemanticInformation semantic_information = 11;
 
diff --git a/components/autofill_assistant/browser/web/element_finder.cc b/components/autofill_assistant/browser/web/element_finder.cc
index c533269a..a8bd92c 100644
--- a/components/autofill_assistant/browser/web/element_finder.cc
+++ b/components/autofill_assistant/browser/web/element_finder.cc
@@ -284,6 +284,7 @@
   driver->GetAutofillAssistantAgent()->GetSemanticNodes(
       selector_.proto.semantic_information().semantic_role(),
       selector_.proto.semantic_information().objective(),
+      selector_.proto.semantic_information().ignore_objective(),
       base::Milliseconds(
           selector_.proto.semantic_information().model_timeout_ms()),
       base::BindOnce(&ElementFinder::OnRunAnnotateDomModelOnFrame,
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index a067f62..eebcc9a 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -104,6 +104,7 @@
               GetSemanticNodes,
               (int32_t role,
                int32_t objective,
+               bool ignore_objective,
                base::TimeDelta model_timeout,
                base::OnceCallback<void(mojom::NodeDataStatus,
                                        const std::vector<NodeData>&)> callback),
@@ -3396,11 +3397,11 @@
   NodeData node_data;
   node_data.backend_node_id = backend_node_id;
   EXPECT_CALL(autofill_assistant_agent_,
-              GetSemanticNodes(1, 2, base::Milliseconds(5000), _))
-      .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess,
+              GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+      .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
                                    std::vector<NodeData>{node_data}))
       // Capture any other frames.
-      .WillRepeatedly(RunOnceCallback<3>(
+      .WillRepeatedly(RunOnceCallback<4>(
           mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
 
   // We pretend that the button is the correct element.
@@ -3422,11 +3423,11 @@
   NodeData node_data;
   node_data.backend_node_id = backend_node_id;
   EXPECT_CALL(autofill_assistant_agent_,
-              GetSemanticNodes(1, 2, base::Milliseconds(5000), _))
-      .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess,
+              GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+      .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
                                    std::vector<NodeData>{node_data}))
       // Capture any other frames.
-      .WillRepeatedly(RunOnceCallback<3>(
+      .WillRepeatedly(RunOnceCallback<4>(
           mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
 
   // We pretend that the button is the correct element.
@@ -3442,8 +3443,8 @@
 
   // All frames return an empty list as a result.
   EXPECT_CALL(autofill_assistant_agent_,
-              GetSemanticNodes(1, 2, base::Milliseconds(5000), _))
-      .WillRepeatedly(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess,
+              GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+      .WillRepeatedly(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
                                          std::vector<NodeData>{}));
 
   FindElementExpectEmptyResult(Selector(proto));
@@ -3461,13 +3462,13 @@
   NodeData node_data_other;
   node_data_other.backend_node_id = 13;
   EXPECT_CALL(autofill_assistant_agent_,
-              GetSemanticNodes(1, 2, base::Milliseconds(5000), _))
-      .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess,
+              GetSemanticNodes(1, 2, false, base::Milliseconds(5000), _))
+      .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
                                    std::vector<NodeData>{node_data}))
-      .WillOnce(RunOnceCallback<3>(mojom::NodeDataStatus::kSuccess,
+      .WillOnce(RunOnceCallback<4>(mojom::NodeDataStatus::kSuccess,
                                    std::vector<NodeData>{node_data_other}))
       // Capture any other frames.
-      .WillRepeatedly(RunOnceCallback<3>(
+      .WillRepeatedly(RunOnceCallback<4>(
           mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
 
   // Two elements are found in different frames.
@@ -3476,4 +3477,24 @@
   EXPECT_EQ(TOO_MANY_ELEMENTS, status.proto_status());
 }
 
+IN_PROC_BROWSER_TEST_F(
+    WebControllerBrowserTest,
+    ElementExistenceCheckWithSemanticModelUsesIgnoreObjective) {
+  EXPECT_CALL(autofill_assistant_agent_,
+              GetSemanticNodes(1, 2, true, base::Milliseconds(5000), _))
+      .WillRepeatedly(RunOnceCallback<4>(
+          mojom::NodeDataStatus::kUnexpectedError, std::vector<NodeData>()));
+
+  SelectorProto proto;
+  auto* semantic_information = proto.mutable_semantic_information();
+  semantic_information->set_semantic_role(1);
+  semantic_information->set_objective(2);
+  // All we want is this to be propagated to the GetSemanticNodes call as
+  // configured in the previous expectation.
+  semantic_information->set_ignore_objective(true);
+
+  ClientStatus ignore_status;
+  FindElement(Selector(proto), &ignore_status, nullptr);
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/content/common/autofill_assistant_agent.mojom b/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
index 2fbde0d..ff86b393 100644
--- a/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
+++ b/components/autofill_assistant/content/common/autofill_assistant_agent.mojom
@@ -12,6 +12,8 @@
 interface AutofillAssistantAgent {
   // Evaluates all input, textarea and select nodes in the frame's document and
   // returns all that match the given semantic role and objective.
-  GetSemanticNodes(int32 role, int32 objective, mojo_base.mojom.TimeDelta timeout)
-      => (NodeDataStatus status, array<autofill_assistant.mojom.NodeData> nodes);
+  GetSemanticNodes(int32 role, int32 objective, bool ignore_objective,
+                   mojo_base.mojom.TimeDelta timeout)
+      => (NodeDataStatus status,
+          array<autofill_assistant.mojom.NodeData> nodes);
 };
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc b/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
index 7a36388b..f7b2cb0 100644
--- a/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
+++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent.cc
@@ -47,6 +47,7 @@
 void AutofillAssistantAgent::GetSemanticNodes(
     int32_t role,
     int32_t objective,
+    bool ignore_objective,
     base::TimeDelta model_timeout,
     GetSemanticNodesCallback callback) {
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
@@ -60,7 +61,7 @@
       model_timeout,
       base::BindOnce(&AutofillAssistantAgent::OnGetModelFile,
                      weak_ptr_factory_.GetWeakPtr(), base::Time::Now(), frame,
-                     role, objective, std::move(callback)));
+                     role, objective, ignore_objective, std::move(callback)));
 }
 
 void AutofillAssistantAgent::GetAnnotateDomModel(
@@ -80,6 +81,7 @@
                                             blink::WebLocalFrame* frame,
                                             int32_t role,
                                             int32_t objective,
+                                            bool ignore_objective,
                                             GetSemanticNodesCallback callback,
                                             mojom::ModelStatus model_status,
                                             base::File model) {
@@ -122,10 +124,10 @@
   for (const auto& node_signal : node_signals) {
     auto result = model_executor.ExecuteModelWithInput(node_signal);
     DVLOG(3) << "Annotated node with result: role: " << result->first
-             << " and objective: " << result->second;
-    // TODO(mcarlen): Use the objective wildcard here to ignore the second part
-    // of the condition.
-    if (result && result->first == role && result->second == objective) {
+             << " and objective: " << result->second
+             << " (or ignore: " << ignore_objective << ")";
+    if (result && result->first == role &&
+        (result->second == objective || ignore_objective)) {
       NodeData node_data;
       node_data.backend_node_id = node_signal.backend_node_id;
       nodes.push_back(node_data);
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent.h b/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
index 9f53341..b546af6 100644
--- a/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
+++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent.h
@@ -44,6 +44,7 @@
   // mojom::AutofillAssistantAgent:
   void GetSemanticNodes(int32_t role,
                         int32_t objective,
+                        bool ignore_objective,
                         base::TimeDelta model_timeout,
                         GetSemanticNodesCallback callback) override;
 
@@ -61,6 +62,7 @@
                       blink::WebLocalFrame* frame,
                       int32_t role,
                       int32_t objective,
+                      bool ignore_objective,
                       GetSemanticNodesCallback callback,
                       mojom::ModelStatus model_status,
                       base::File model);
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
index 6c09851..36900340 100644
--- a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
+++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
 #include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
@@ -24,6 +25,7 @@
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::SizeIs;
 
 class MockAutofillAssistantDriver : public mojom::AutofillAssistantDriver {
  public:
@@ -95,7 +97,7 @@
   base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus,
                                              const std::vector<NodeData>&)>>
       callback;
-  EXPECT_CALL(callback, Run(mojom::NodeDataStatus::kSuccess, _));
+  EXPECT_CALL(callback, Run(mojom::NodeDataStatus::kSuccess, SizeIs(1)));
 
   LoadHTML(R"(
     <div>
@@ -106,6 +108,7 @@
   autofill_assistant_agent_->GetSemanticNodes(
       /* role= */ 47 /* ADDRESS_LINE1 */,
       /* objective= */ 7 /* FILL_DELIVERY_ADDRESS */,
+      /* ignore_objective= */ false,
       /* model_timeout= */ base::Milliseconds(1000), callback.Get());
 
   base::RunLoop().RunUntilIdle();
@@ -130,6 +133,7 @@
   autofill_assistant_agent_->GetSemanticNodes(
       /* role= */ 47 /* ADDRESS_LINE1 */,
       /* objective= */ 7 /* FILL_DELIVERY_ADDRESS */,
+      /* ignore_objective= */ false,
       /* model_timeout= */ base::Milliseconds(0), callback.Get());
 
   base::RunLoop().RunUntilIdle();
@@ -155,10 +159,35 @@
   autofill_assistant_agent_->GetSemanticNodes(
       /* role= */ 47 /* ADDRESS_LINE1 */,
       /* objective= */ 7 /* FILL_DELIVERY_ADDRESS */,
+      /* ignore_objective= */ false,
       /* model_timeout= */ base::Milliseconds(1000), callback.Get());
 
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(AutofillAssistantAgentBrowserTest, GetSemanticNodesIgnoreObjective) {
+  EXPECT_CALL(autofill_assistant_driver_, GetAnnotateDomModel)
+      .WillOnce(RunOnceCallback<1>(mojom::ModelStatus::kSuccess,
+                                   model_file_.Duplicate()));
+
+  LoadHTML(R"(
+    <div>
+      <h1>Shipping address</h1>
+      <label for="street">Street Address</label><input id="street">
+    </div>)");
+
+  base::MockCallback<base::OnceCallback<void(mojom::NodeDataStatus,
+                                             const std::vector<NodeData>&)>>
+      callback;
+  EXPECT_CALL(callback, Run(mojom::NodeDataStatus::kSuccess, SizeIs(1)));
+
+  autofill_assistant_agent_->GetSemanticNodes(
+      /* role= */ 47 /* ADDRESS_LINE1 */,
+      /* objective= */ 6 /* FILL_BILLING_ADDRESS */,
+      /* ignore_objective= */ true,
+      /* model_timeout= */ base::Milliseconds(1000), callback.Get());
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java
index 7172d91..edcdbd6 100644
--- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java
+++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ForegroundServiceUtils.java
@@ -7,22 +7,16 @@
 import android.app.Notification;
 import android.app.Service;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Build;
-import android.os.Process;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.core.app.ServiceCompat;
 import androidx.core.content.ContextCompat;
 
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.compat.ApiHelperForQ;
 import org.chromium.base.compat.ApiHelperForS;
-import org.chromium.ui.permissions.PermissionConstants;
-import org.chromium.ui.permissions.PermissionPrefs;
 
 /**
  * Utility functions that call into Android foreground service related API, and provides
@@ -98,26 +92,4 @@
             Log.e(TAG, "Failed to stop foreground service, ", e);
         }
     }
-
-    /**
-     * Utility method to check if any foreground service can be started. Starting from android T,
-     * foreground services are not allowed to start if user hasn't been asked for notification
-     * permissions. Media is excluded from this restriction.
-     * @return
-     */
-    public static boolean canStartForegroundServiceExcludingMedia() {
-        if (!BuildInfo.isAtLeastT()) return true;
-        return hasEverRequestedNotificationPermission();
-    }
-
-    private static boolean hasEverRequestedNotificationPermission() {
-        boolean hasPermission =
-                ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationContext(),
-                        PermissionConstants.NOTIFICATION_PERMISSION, Process.myPid(),
-                        Process.myUid())
-                == PackageManager.PERMISSION_GRANTED;
-        if (hasPermission) return true;
-
-        return PermissionPrefs.getAndroidNotificationPermissionRequestTimestamp() != 0;
-    }
 }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java
index 4ce384a..76bef0a 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedIconGenerator.java
@@ -14,6 +14,7 @@
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
@@ -99,7 +100,7 @@
     /**
      * Sets the background color to use when generating icons.
      */
-    public void setBackgroundColor(int color) {
+    public void setBackgroundColor(@ColorInt int color) {
         mBackgroundPaint.setColor(color);
     }
 
diff --git a/components/browsing_topics/BUILD.gn b/components/browsing_topics/BUILD.gn
index 18e1135a..0d028813 100644
--- a/components/browsing_topics/BUILD.gn
+++ b/components/browsing_topics/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("browsing_topics") {
   sources = [
+    "browsing_topics_calculator.cc",
+    "browsing_topics_calculator.h",
     "browsing_topics_service.h",
     "browsing_topics_service_impl.cc",
     "browsing_topics_service_impl.h",
@@ -20,16 +22,33 @@
   deps = [
     "//base",
     "//components/browsing_topics/common:common",
+    "//components/history/content/browser",
+    "//components/history/core/browser",
     "//components/keyed_service/core",
+    "//components/optimization_guide/content/browser",
     "//components/privacy_sandbox",
+    "//content/public/browser",
     "//crypto",
     "//third_party/blink/public/common",
   ]
 }
 
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "test_util.cc",
+    "test_util.h",
+  ]
+
+  public_deps = [ "//base" ]
+
+  deps = [ ":browsing_topics" ]
+}
+
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "browsing_topics_calculator_unittest.cc",
     "browsing_topics_state_unittest.cc",
     "epoch_topics_unittest.cc",
     "topic_and_domains_unittest.cc",
@@ -38,9 +57,21 @@
 
   deps = [
     ":browsing_topics",
+    ":test_support",
     "//base",
     "//base/test:test_support",
+    "//components/content_settings/core/test:test_support",
+    "//components/history/core/browser:browser",
+    "//components/history/core/test",
+    "//components/optimization_guide/content/browser:browser",
+    "//components/optimization_guide/content/browser:test_support",
+    "//components/optimization_guide/core:test_support",
     "//components/prefs:test_support",
+    "//components/privacy_sandbox:privacy_sandbox",
+    "//components/privacy_sandbox:privacy_sandbox_prefs",
+    "//components/privacy_sandbox:test_support",
+    "//components/sync_preferences:test_support",
+    "//content/test:test_support",
     "//testing/gtest",
     "//third_party/blink/public/common",
   ]
diff --git a/components/browsing_topics/DEPS b/components/browsing_topics/DEPS
index dba046b..26ef4ba 100644
--- a/components/browsing_topics/DEPS
+++ b/components/browsing_topics/DEPS
@@ -1,6 +1,17 @@
 include_rules = [
-  "+crypto",
-  "+third_party/blink/public/common",
+  "+components/history",
+  "+components/optimization_guide",
   "+components/keyed_service",
   "+components/privacy_sandbox",
+  "+content/public/browser",
+  "+content/public/test",
+  "+crypto",
+  "+third_party/blink/public/common",
 ]
+
+specific_include_rules = {
+  ".*_unittest.cc": [
+    "+components/content_settings/core",
+    "+components/sync_preferences",
+  ],
+}
diff --git a/components/browsing_topics/browsing_topics_calculator.cc b/components/browsing_topics/browsing_topics_calculator.cc
new file mode 100644
index 0000000..88ade005
--- /dev/null
+++ b/components/browsing_topics/browsing_topics_calculator.cc
@@ -0,0 +1,370 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/browsing_topics/browsing_topics_calculator.h"
+
+#include "base/containers/contains.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/rand_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/browsing_topics/util.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/optimization_guide/content/browser/page_content_annotations_service.h"
+#include "components/privacy_sandbox/canonical_topic.h"
+#include "components/privacy_sandbox/privacy_sandbox_settings.h"
+#include "content/public/browser/browsing_topics_site_data_manager.h"
+#include "third_party/blink/public/common/features.h"
+
+namespace browsing_topics {
+
+namespace {
+
+// Derive the mapping from hosts to topics and the mapping from topics to hosts.
+// Precondition: the annotation didn't fail in general (e.g. `ModelInfo` is
+// valid).
+void DeriveHostTopicsMapAndTopicHostsMap(
+    const std::vector<std::string>& raw_hosts,
+    const std::vector<optimization_guide::BatchAnnotationResult>& results,
+    std::map<HashedHost, std::set<Topic>>& host_topics_map,
+    std::map<Topic, std::set<HashedHost>>& topic_hosts_map) {
+  DCHECK(host_topics_map.empty());
+  DCHECK(topic_hosts_map.empty());
+
+  DCHECK_EQ(raw_hosts.size(), results.size());
+
+  for (size_t i = 0; i < results.size(); ++i) {
+    const optimization_guide::BatchAnnotationResult& result = results[i];
+    const std::string raw_host = raw_hosts[i];
+
+    // As long as the annotation didn't fail in general, the individual
+    // `result.topics()` should always be valid.
+    const std::vector<optimization_guide::WeightedIdentifier>&
+        annotation_result_topics = result.topics().value();
+
+    HashedHost host = HashMainFrameHostForStorage(raw_host);
+
+    for (const optimization_guide::WeightedIdentifier& annotation_result_topic :
+         annotation_result_topics) {
+      // Note that `annotation_result_topic.weight()` is ignored. This is the
+      // intended use of the model for the Topics API.
+      Topic topic = Topic(annotation_result_topic.value());
+
+      topic_hosts_map[topic].insert(host);
+      host_topics_map[host].insert(topic);
+    }
+  }
+}
+
+// For `topic`, derive the context domains that observed it. This is done by
+// first getting the hosts about `topic` from `topic_hosts_map`, and
+// for each site, get the callers (context domains) that were on that site and
+// add the callers to a result set.
+std::set<HashedDomain> GetTopicObservationDomains(
+    const Topic& topic,
+    const std::map<Topic, std::set<HashedHost>>& topic_hosts_map,
+    const std::map<HashedHost, std::vector<HashedDomain>>&
+        host_context_domains_map) {
+  std::set<HashedDomain> topic_observation_domains;
+
+  // If `topic` was padded, it may not exist in `topic_hosts_map`. In this
+  // case, return an empty set.
+  auto it = topic_hosts_map.find(topic);
+  if (it == topic_hosts_map.end())
+    return std::set<HashedDomain>();
+
+  const std::set<HashedHost>& hosts = it->second;
+
+  for (const HashedHost& host : hosts) {
+    // `host` came from the history database, and it may not exist in the
+    // `host_context_domains_map` which came from the usage contexts
+    // database, due to e.g. per-context data deletion, database errors, etc.
+    // In this case, continue checking other hosts.
+    auto it = host_context_domains_map.find(host);
+    if (it == host_context_domains_map.end())
+      continue;
+
+    const std::vector<HashedDomain>& context_domains = it->second;
+
+    for (const HashedDomain& context_domain : context_domains) {
+      topic_observation_domains.insert(context_domain);
+
+      // To limit memory usage, cap the number of context domains to keep
+      // per-topic. The larger `HashedDomain`s will be kept. This is fair, as
+      // the hashing for context domains is per-user, so we are not
+      // prioritizing any domains in general.
+      if (topic_observation_domains.size() >
+          static_cast<size_t>(
+              blink::features::
+                  kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic
+                      .Get())) {
+        topic_observation_domains.erase(topic_observation_domains.begin());
+      }
+    }
+  }
+
+  return topic_observation_domains;
+}
+
+}  // namespace
+
+BrowsingTopicsCalculator::BrowsingTopicsCalculator(
+    privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings,
+    history::HistoryService* history_service,
+    content::BrowsingTopicsSiteDataManager* site_data_manager,
+    optimization_guide::PageContentAnnotationsService* annotations_service,
+    CalculateCompletedCallback callback)
+    : privacy_sandbox_settings_(privacy_sandbox_settings),
+      history_service_(history_service),
+      site_data_manager_(site_data_manager),
+      annotations_service_(annotations_service),
+      calculate_completed_callback_(std::move(callback)),
+      calculation_time_(base::Time::Now()) {
+  // Continue asynchronously so that `calculate_completed_callback_` isn't
+  // called synchronously while `this` is being constructed.
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&BrowsingTopicsCalculator::CheckCanCalculate,
+                                weak_ptr_factory_.GetWeakPtr()));
+}
+
+BrowsingTopicsCalculator::~BrowsingTopicsCalculator() = default;
+
+uint64_t BrowsingTopicsCalculator::GenerateRandUint64() {
+  return base::RandUint64();
+}
+
+void BrowsingTopicsCalculator::DeriveTopTopics(
+    const std::map<HashedHost, size_t>& history_hosts_count,
+    const std::map<HashedHost, std::set<Topic>>& host_topics_map,
+    size_t taxonomy_size,
+    std::vector<Topic>& top_topics,
+    size_t& padded_top_topics_start_index) {
+  DCHECK(top_topics.empty());
+  DCHECK_EQ(padded_top_topics_start_index, 0u);
+
+  // Derive the frequency of each topic, by summing up the frequencies of the
+  // associated hosts. TODO(yaoxia): consider applying inverse frequency of
+  // topics (https://github.com/jkarlin/topics/issues/42).
+  std::map<Topic, size_t> topics_count;
+  for (auto const& [host, host_count] : history_hosts_count) {
+    const std::set<Topic>& topics = host_topics_map.at(host);
+    for (const Topic& topic : topics) {
+      topics_count[topic] += host_count;
+    }
+  }
+
+  DCHECK_LE(
+      static_cast<size_t>(
+          blink::features::kBrowsingTopicsNumberOfTopTopicsPerEpoch.Get()),
+      taxonomy_size);
+
+  // Get the top up to `kBrowsingTopicsNumberOfTopTopicsPerEpoch` topics,
+  // sorted by decreasing count.
+  std::vector<std::pair<Topic, size_t>> top_topics_count(std::min(
+      static_cast<size_t>(
+          blink::features::kBrowsingTopicsNumberOfTopTopicsPerEpoch.Get()),
+      topics_count.size()));
+
+  std::partial_sort_copy(
+      topics_count.begin(), topics_count.end(), top_topics_count.begin(),
+      top_topics_count.end(),
+      [](auto& left, auto& right) { return left.second > right.second; });
+
+  std::transform(top_topics_count.begin(), top_topics_count.end(),
+                 std::back_inserter(top_topics),
+                 [](auto& topic_count) { return topic_count.first; });
+
+  padded_top_topics_start_index = top_topics.size();
+
+  // Pad the top topics with distinct random topics until we have
+  // `kBrowsingTopicsNumberOfTopTopicsPerEpoch` topics.
+  while (top_topics.size() <
+         static_cast<size_t>(
+             blink::features::kBrowsingTopicsNumberOfTopTopicsPerEpoch.Get())) {
+    Topic padded_topic(0);
+
+    do {
+      int padded_topic_index =
+          base::checked_cast<int>(GenerateRandUint64() % taxonomy_size);
+      padded_topic = Topic(padded_topic_index + 1);
+    } while (base::Contains(top_topics, padded_topic));
+
+    top_topics.emplace_back(std::move(padded_topic));
+  }
+}
+
+void BrowsingTopicsCalculator::CheckCanCalculate() {
+  if (!privacy_sandbox_settings_->IsTopicsAllowed()) {
+    OnCalculateCompleted(CalculatorResultStatus::kFailurePermissionDenied);
+    return;
+  }
+
+  // Get the the api usages context map (from the calling context domain to a
+  // set of history hosts) so that we can figure out which topics the APIs were
+  // called on.
+  site_data_manager_->GetBrowsingTopicsApiUsage(
+      /*begin_time=*/DeriveApiUsageContextDataStartTime(
+          calculation_time_,
+          privacy_sandbox_settings_->TopicsDataAccessibleSince()),
+      /*end_time=*/calculation_time_,
+      base::BindOnce(&BrowsingTopicsCalculator::
+                         OnGetRecentBrowsingTopicsApiUsagesCompleted,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BrowsingTopicsCalculator::OnGetRecentBrowsingTopicsApiUsagesCompleted(
+    browsing_topics::ApiUsageContextQueryResult result) {
+  DCHECK(host_context_domains_map_.empty());
+
+  if (!result.success) {
+    OnCalculateCompleted(
+        CalculatorResultStatus::kFailureApiUsageContextQueryError);
+    return;
+  }
+
+  for (const ApiUsageContext& usage_context : result.api_usage_contexts) {
+    host_context_domains_map_[usage_context.hashed_main_frame_host]
+        .emplace_back(usage_context.hashed_context_domain);
+  }
+
+  // `ApiUsageContext::hashed_main_frame_host` is a hashed number. To get the
+  // topic associated with it, we will need to match it against a set of raw
+  // hosts with topics. Thus, here we query the history with the larger time
+  // range (from DeriveApiUsageContextDataStartTime() to `calculation_time_`) to
+  // get the raw hosts.
+  history::QueryOptions options;
+  options.begin_time = DeriveApiUsageContextDataStartTime(
+      calculation_time_,
+      privacy_sandbox_settings_->TopicsDataAccessibleSince());
+  options.end_time = calculation_time_;
+  options.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES;
+
+  history_service_->QueryHistory(
+      std::u16string(), options,
+      base::BindOnce(
+          &BrowsingTopicsCalculator::OnGetRecentlyVisitedURLsCompleted,
+          weak_ptr_factory_.GetWeakPtr()),
+      &history_task_tracker_);
+}
+
+void BrowsingTopicsCalculator::OnGetRecentlyVisitedURLsCompleted(
+    history::QueryResults results) {
+  DCHECK(history_hosts_count_.empty());
+
+  std::set<std::string> raw_hosts;
+
+  for (const history::URLResult& url_result : results) {
+    if (!(url_result.content_annotations().annotation_flags &
+          history::VisitContentAnnotationFlag::kBrowsingTopicsEligible)) {
+      continue;
+    }
+
+    std::string raw_host = url_result.url().host();
+    raw_hosts.insert(raw_host);
+
+    if (url_result.visit_time() >=
+        DeriveHistoryDataStartTime(
+            calculation_time_,
+            privacy_sandbox_settings_->TopicsDataAccessibleSince())) {
+      HashedHost host = HashMainFrameHostForStorage(raw_host);
+      history_hosts_count_[host]++;
+    }
+  }
+
+  base::UmaHistogramCounts1000(
+      "BrowsingTopics.EpochTopicsCalculation.EligibleDistinctHistoryHostsCount",
+      history_hosts_count_.size());
+
+  std::vector<std::string> raw_hosts_vector(raw_hosts.begin(), raw_hosts.end());
+
+  annotations_service_->BatchAnnotatePageTopics(
+      base::BindOnce(&BrowsingTopicsCalculator::OnGetTopicsForHostsCompleted,
+                     weak_ptr_factory_.GetWeakPtr(), raw_hosts_vector),
+      raw_hosts_vector);
+}
+
+void BrowsingTopicsCalculator::OnGetTopicsForHostsCompleted(
+    std::vector<std::string> raw_hosts,
+    const std::vector<optimization_guide::BatchAnnotationResult>& results) {
+  absl::optional<optimization_guide::ModelInfo> model_info =
+      annotations_service_->GetModelInfoForType(
+          optimization_guide::AnnotationType::kPageTopics);
+
+  if (!model_info) {
+    OnCalculateCompleted(
+        CalculatorResultStatus::kFailureAnnotationExecutionError);
+    return;
+  }
+
+  absl::optional<size_t> taxonomy_size = GetTaxonomySize();
+  if (!taxonomy_size) {
+    OnCalculateCompleted(
+        CalculatorResultStatus::kFailureTaxonomyVersionNotSupportedInBinary);
+    return;
+  }
+
+  const int model_version = base::checked_cast<int>(model_info->GetVersion());
+  DCHECK_GT(model_version, 0);
+
+  std::map<HashedHost, std::set<Topic>> host_topics_map;
+  std::map<Topic, std::set<HashedHost>> topic_hosts_map;
+  DeriveHostTopicsMapAndTopicHostsMap(raw_hosts, results, host_topics_map,
+                                      topic_hosts_map);
+
+  std::vector<Topic> top_topics;
+  size_t padded_top_topics_start_index = 0u;
+  DeriveTopTopics(history_hosts_count_, host_topics_map, *taxonomy_size,
+                  top_topics, padded_top_topics_start_index);
+
+  base::UmaHistogramCounts100(
+      "BrowsingTopics.EpochTopicsCalculation.TopTopicsCountBeforePadding",
+      padded_top_topics_start_index);
+
+  // For each top topic, derive the context domains that observed it
+  std::vector<TopicAndDomains> top_topics_and_observing_domains;
+
+  for (const Topic& topic : top_topics) {
+    if (!privacy_sandbox_settings_->IsTopicAllowed(
+            privacy_sandbox::CanonicalTopic(
+                topic,
+                blink::features::kBrowsingTopicsTaxonomyVersion.Get()))) {
+      top_topics_and_observing_domains.emplace_back(TopicAndDomains());
+      continue;
+    }
+
+    std::set<HashedDomain> topic_observation_domains =
+        GetTopicObservationDomains(topic, topic_hosts_map,
+                                   host_context_domains_map_);
+
+    base::UmaHistogramCounts1000(
+        "BrowsingTopics.EpochTopicsCalculation."
+        "ObservationContextDomainsCountPerTopTopic",
+        topic_observation_domains.size());
+
+    top_topics_and_observing_domains.emplace_back(
+        TopicAndDomains(topic, std::move(topic_observation_domains)));
+  }
+
+  OnCalculateCompleted(
+      CalculatorResultStatus::kSuccess,
+      EpochTopics(std::move(top_topics_and_observing_domains),
+                  padded_top_topics_start_index, *taxonomy_size,
+                  blink::features::kBrowsingTopicsTaxonomyVersion.Get(),
+                  model_version, calculation_time_));
+}
+
+void BrowsingTopicsCalculator::OnCalculateCompleted(
+    CalculatorResultStatus status,
+    EpochTopics epoch_topics) {
+  DCHECK(status != CalculatorResultStatus::kSuccess ||
+         epoch_topics.HasValidTopics());
+
+  base::UmaHistogramEnumeration(
+      "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus", status);
+
+  std::move(calculate_completed_callback_).Run(std::move(epoch_topics));
+
+  // Do not add code after this. BrowsingTopicsCalculator has been destroyed.
+}
+
+}  // namespace browsing_topics
diff --git a/components/browsing_topics/browsing_topics_calculator.h b/components/browsing_topics/browsing_topics_calculator.h
new file mode 100644
index 0000000..1232da3
--- /dev/null
+++ b/components/browsing_topics/browsing_topics_calculator.h
@@ -0,0 +1,137 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_CALCULATOR_H_
+#define COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_CALCULATOR_H_
+
+#include <map>
+#include <set>
+
+#include "base/callback.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/browsing_topics/common/common_types.h"
+#include "components/browsing_topics/epoch_topics.h"
+#include "components/history/core/browser/history_types.h"
+
+namespace privacy_sandbox {
+class PrivacySandboxSettings;
+}  // namespace privacy_sandbox
+
+namespace history {
+class HistoryService;
+}  // namespace history
+
+namespace content {
+class BrowsingTopicsSiteDataManager;
+}  // namespace content
+
+namespace optimization_guide {
+class PageContentAnnotationsService;
+class BatchAnnotationResult;
+}  // namespace optimization_guide
+
+namespace browsing_topics {
+
+// Responsible for doing a one-off browsing topics calculation. It will:
+// 1) Check the user settings for calculation permissions.
+// 2) Query the `BrowsingTopicsSiteDataManager` for the contexts where the
+// Topics API was called on.
+// 3) Query the `HistoryService` for the hosts of the pages the API was called
+// on.
+// 4) Query the `PageContentAnnotationsService` with a set of hosts, to get the
+// corresponding topics.
+// 5) Derive `EpochTopics` (i.e. the top topics and the their observed-by
+// contexts), and return it as the final result.
+class BrowsingTopicsCalculator {
+ public:
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class CalculatorResultStatus {
+    kSuccess = 0,
+    kFailurePermissionDenied = 1,
+    kFailureApiUsageContextQueryError = 2,
+    kFailureAnnotationExecutionError = 3,
+    kFailureTaxonomyVersionNotSupportedInBinary = 4,
+
+    kMaxValue = kFailureTaxonomyVersionNotSupportedInBinary,
+  };
+
+  using CalculateCompletedCallback = base::OnceCallback<void(EpochTopics)>;
+
+  BrowsingTopicsCalculator(
+      privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings,
+      history::HistoryService* history_service,
+      content::BrowsingTopicsSiteDataManager* site_data_manager,
+      optimization_guide::PageContentAnnotationsService* annotations_service,
+      CalculateCompletedCallback callback);
+
+  BrowsingTopicsCalculator(const BrowsingTopicsCalculator&) = delete;
+  BrowsingTopicsCalculator& operator=(const BrowsingTopicsCalculator&) = delete;
+  BrowsingTopicsCalculator(BrowsingTopicsCalculator&&) = delete;
+  BrowsingTopicsCalculator& operator=(BrowsingTopicsCalculator&&) = delete;
+
+  virtual ~BrowsingTopicsCalculator();
+
+ protected:
+  // This method exists for the purposes of overriding in tests.
+  virtual uint64_t GenerateRandUint64();
+
+ private:
+  // Get the top `kBrowsingTopicsNumberOfTopTopicsPerEpoch` topics. If there
+  // aren't enough topics, pad with random ones. Return the result topics, and
+  // the starting index of the padded topics (or
+  // `kBrowsingTopicsNumberOfTopTopicsPerEpoch` if there's no padded topics).
+  // Precondition: the hosts in `history_hosts_count` should exist in
+  // `host_topics_map`.
+  void DeriveTopTopics(
+      const std::map<HashedHost, size_t>& history_hosts_count,
+      const std::map<HashedHost, std::set<Topic>>& host_topics_map,
+      size_t taxonomy_size,
+      std::vector<Topic>& top_topics,
+      size_t& padded_top_topics_start_index);
+
+  void CheckCanCalculate();
+
+  void OnGetRecentBrowsingTopicsApiUsagesCompleted(
+      browsing_topics::ApiUsageContextQueryResult result);
+
+  void OnGetRecentlyVisitedURLsCompleted(history::QueryResults results);
+
+  void OnGetTopicsForHostsCompleted(
+      std::vector<std::string> raw_hosts,
+      const std::vector<optimization_guide::BatchAnnotationResult>& results);
+
+  void OnCalculateCompleted(CalculatorResultStatus status,
+                            EpochTopics epoch_topics = EpochTopics());
+
+  // Those pointers are safe to hold and use throughout the lifetime of
+  // `BrowsingTopicsService`, which owns this object.
+  privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings_;
+  history::HistoryService* history_service_;
+  content::BrowsingTopicsSiteDataManager* site_data_manager_;
+  optimization_guide::PageContentAnnotationsService* annotations_service_;
+
+  CalculateCompletedCallback calculate_completed_callback_;
+
+  // The calculation start time.
+  base::Time calculation_time_;
+
+  // The history hosts over
+  // `kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering` epochs,
+  // and the calling context domains that used the Topics API in each main frame
+  // host.
+  std::map<HashedHost, std::vector<HashedDomain>> host_context_domains_map_;
+
+  // The hashed history hosts and their count over the last epoch.
+  std::map<HashedHost, size_t> history_hosts_count_;
+
+  // Used for the async tasks querying the HistoryService.
+  base::CancelableTaskTracker history_task_tracker_;
+
+  base::WeakPtrFactory<BrowsingTopicsCalculator> weak_ptr_factory_{this};
+};
+
+}  // namespace browsing_topics
+
+#endif  // COMPONENTS_BROWSING_TOPICS_BROWSING_TOPICS_CALCULATOR_H_
diff --git a/components/browsing_topics/browsing_topics_calculator_unittest.cc b/components/browsing_topics/browsing_topics_calculator_unittest.cc
new file mode 100644
index 0000000..79fcfb0
--- /dev/null
+++ b/components/browsing_topics/browsing_topics_calculator_unittest.cc
@@ -0,0 +1,789 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/browsing_topics/browsing_topics_calculator.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/test/bind.h"
+#include "base/test/gtest_util.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/browsing_topics/test_util.h"
+#include "components/browsing_topics/util.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/history/core/browser/history_database_params.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/test/test_history_database.h"
+#include "components/optimization_guide/content/browser/page_content_annotations_service.h"
+#include "components/optimization_guide/content/browser/test_page_content_annotator.h"
+#include "components/optimization_guide/core/test_model_info_builder.h"
+#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
+#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
+#include "components/privacy_sandbox/privacy_sandbox_settings.h"
+#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/browsing_topics_test_util.h"
+#include "third_party/blink/public/common/features.h"
+
+namespace browsing_topics {
+
+namespace {
+
+const size_t kTaxonomySize = 349;
+const int kTaxonomyVersion = 1;
+
+const std::string kHost1 = "www.foo1.com";
+const std::string kHost2 = "www.foo2.com";
+const std::string kHost3 = "www.foo3.com";
+const std::string kHost4 = "www.foo4.com";
+const std::string kHost5 = "www.foo5.com";
+const std::string kHost6 = "www.foo6.com";
+
+const std::string kTokenizedHost1 = "foo1 com";
+const std::string kTokenizedHost2 = "foo2 com";
+const std::string kTokenizedHost3 = "foo3 com";
+const std::string kTokenizedHost4 = "foo4 com";
+const std::string kTokenizedHost5 = "foo5 com";
+const std::string kTokenizedHost6 = "foo6 com";
+
+}  // namespace
+
+class BrowsingTopicsCalculatorTest : public testing::Test {
+ public:
+  BrowsingTopicsCalculatorTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry());
+    HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry());
+    privacy_sandbox::RegisterProfilePrefs(prefs_.registry());
+
+    host_content_settings_map_ = new HostContentSettingsMap(
+        &prefs_, /*is_off_the_record=*/false, /*store_last_modified=*/false,
+        /*restore_session=*/false);
+    cookie_settings_ = new content_settings::CookieSettings(
+        host_content_settings_map_.get(), &prefs_, false, "chrome-extension");
+    privacy_sandbox_settings_ = std::make_unique<
+        privacy_sandbox::PrivacySandboxSettings>(
+        std::make_unique<
+            privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>(),
+        host_content_settings_map_.get(), cookie_settings_, &prefs_,
+        /*incognito_profile=*/false);
+
+    topics_site_data_manager_ =
+        std::make_unique<content::TesterBrowsingTopicsSiteDataManager>(
+            temp_dir_.GetPath());
+
+    history_service_ = std::make_unique<history::HistoryService>();
+    history_service_->Init(
+        history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath()));
+
+    optimization_guide_model_provider_ = std::make_unique<
+        optimization_guide::TestOptimizationGuideModelProvider>();
+    page_content_annotations_service_ =
+        std::make_unique<optimization_guide::PageContentAnnotationsService>(
+            "en-US", optimization_guide_model_provider_.get(),
+            history_service_.get(), nullptr, base::FilePath(), nullptr);
+
+    page_content_annotations_service_->OverridePageContentAnnotatorForTesting(
+        &test_page_content_annotator_);
+
+    task_environment_.RunUntilIdle();
+  }
+
+  ~BrowsingTopicsCalculatorTest() override {
+    host_content_settings_map_->ShutdownOnUIThread();
+  }
+
+  EpochTopics CalculateTopics() {
+    EpochTopics result;
+
+    base::RunLoop run_loop;
+
+    TesterBrowsingTopicsCalculator topics_calculator =
+        TesterBrowsingTopicsCalculator(
+            privacy_sandbox_settings_.get(), history_service_.get(),
+            topics_site_data_manager_.get(),
+            page_content_annotations_service_.get(),
+            base::BindLambdaForTesting([&](EpochTopics epoch_topics) {
+              result = std::move(epoch_topics);
+              run_loop.Quit();
+            }),
+            /*rand_uint64_queue=*/
+            base::queue<uint64_t>{{100, 101, 102, 103, 104}});
+
+    run_loop.Run();
+
+    return result;
+  }
+
+  void AddHistoryEntries(const std::vector<std::string>& hosts,
+                         base::Time time) {
+    history::HistoryAddPageArgs add_page_args;
+    add_page_args.time = time;
+    add_page_args.context_id = reinterpret_cast<history::ContextID>(1);
+
+    for (const std::string& host : hosts) {
+      static int nav_entry_id = 0;
+      ++nav_entry_id;
+
+      add_page_args.url = GURL(base::StrCat({"https://", host}));
+      add_page_args.nav_entry_id = nav_entry_id;
+
+      history_service_->AddPage(add_page_args);
+      history_service_->SetBrowsingTopicsAllowed(
+          add_page_args.context_id, nav_entry_id, add_page_args.url);
+    }
+
+    task_environment_.RunUntilIdle();
+  }
+
+  void AddApiUsageContextEntries(
+      std::vector<std::pair<std::string, std::set<HashedDomain>>>
+          main_frame_hosts_with_context_domains) {
+    for (auto& [main_frame_host, context_domains] :
+         main_frame_hosts_with_context_domains) {
+      topics_site_data_manager_->OnBrowsingTopicsApiUsed(
+          HashMainFrameHostForStorage(main_frame_host),
+          base::flat_set<HashedDomain>(context_domains.begin(),
+                                       context_domains.end()));
+    }
+
+    task_environment_.RunUntilIdle();
+  }
+
+  std::vector<optimization_guide::WeightedIdentifier> TopicsAndWeight(
+      std::vector<int32_t> topics,
+      double weight) {
+    std::vector<optimization_guide::WeightedIdentifier> result;
+    for (int32_t topic : topics) {
+      result.emplace_back(
+          optimization_guide::WeightedIdentifier(topic, weight));
+    }
+
+    return result;
+  }
+
+  void ExpectResultTopicsEqual(
+      const std::vector<TopicAndDomains>& result,
+      std::vector<std::pair<Topic, std::set<HashedDomain>>> expected) {
+    DCHECK_EQ(expected.size(), 5u);
+    EXPECT_EQ(result.size(), 5u);
+
+    for (int i = 0; i < 5; ++i) {
+      EXPECT_EQ(result[i].topic(), expected[i].first);
+      EXPECT_EQ(result[i].hashed_domains(), expected[i].second);
+    }
+  }
+
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+
+  sync_preferences::TestingPrefServiceSyncable prefs_;
+  scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
+  scoped_refptr<content_settings::CookieSettings> cookie_settings_;
+  std::unique_ptr<privacy_sandbox::PrivacySandboxSettings>
+      privacy_sandbox_settings_;
+
+  std::unique_ptr<content::TesterBrowsingTopicsSiteDataManager>
+      topics_site_data_manager_;
+
+  std::unique_ptr<history::HistoryService> history_service_;
+
+  std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider>
+      optimization_guide_model_provider_;
+  std::unique_ptr<optimization_guide::PageContentAnnotationsService>
+      page_content_annotations_service_;
+
+  optimization_guide::TestPageContentAnnotator test_page_content_annotator_;
+
+  base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(BrowsingTopicsCalculatorTest, PermissionDenied) {
+  base::HistogramTester histograms;
+
+  privacy_sandbox_settings_->SetPrivacySandboxEnabled(false);
+
+  EpochTopics result = CalculateTopics();
+  EXPECT_FALSE(result.HasValidTopics());
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
+      /*kFailurePermissionDenied*/ 1,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, ApiUsageContextQueryError) {
+  base::HistogramTester histograms;
+
+  topics_site_data_manager_->SetQueryFailureOverride();
+
+  EpochTopics result = CalculateTopics();
+  EXPECT_FALSE(result.HasValidTopics());
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
+      /*kFailureApiUsageContextQueryError*/ 2,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, AnnotationExecutionError) {
+  base::HistogramTester histograms;
+
+  EpochTopics result = CalculateTopics();
+  EXPECT_FALSE(result.HasValidTopics());
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
+      /*kFailureAnnotationExecutionError*/ 3,
+      /*expected_bucket_count=*/1);
+}
+
+class BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest
+    : public BrowsingTopicsCalculatorTest {
+ public:
+  BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kBrowsingTopics, {{"taxonomy_version", "999"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(BrowsingTopicsCalculatorUnsupporedTaxonomyVersionTest,
+       TaxonomyVersionNotSupportedInBinary) {
+  base::HistogramTester histograms;
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), {});
+
+  EpochTopics result = CalculateTopics();
+  EXPECT_FALSE(result.HasValidTopics());
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
+      /*kFailureTaxonomyVersionNotSupportedInBinary*/ 4,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, TopicsMetadata) {
+  base::HistogramTester histograms;
+  base::Time begin_time = base::Time::Now();
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(), {});
+
+  EpochTopics result1 = CalculateTopics();
+  EXPECT_TRUE(result1.HasValidTopics());
+  EXPECT_EQ(result1.taxonomy_size(), kTaxonomySize);
+  EXPECT_EQ(result1.taxonomy_version(), kTaxonomyVersion);
+  EXPECT_EQ(result1.model_version(), 1);
+  EXPECT_EQ(result1.calculation_time(), begin_time);
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
+      /*kSuccess*/ 0,
+      /*expected_bucket_count=*/1);
+
+  task_environment_.AdvanceClock(base::Seconds(2));
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(50).Build(), {});
+
+  EpochTopics result2 = CalculateTopics();
+  EXPECT_TRUE(result2.HasValidTopics());
+  EXPECT_EQ(result2.taxonomy_size(), kTaxonomySize);
+  EXPECT_EQ(result2.taxonomy_version(), kTaxonomyVersion);
+  EXPECT_EQ(result2.model_version(), 50);
+  EXPECT_EQ(result2.calculation_time(), begin_time + base::Seconds(2));
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus",
+      /*kSuccess*/ 0,
+      /*expected_bucket_count=*/2);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, TopTopicsRankedByFrequency) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
+                    begin_time);
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(6), {}},
+                           {Topic(5), {}},
+                           {Topic(4), {}},
+                           {Topic(3), {}},
+                           {Topic(2), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest,
+       TopTopicsRankedByFrequency_AlsoAffectedByHostsCount) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost1, kHost1, kHost1, kHost1, kHost1, kHost1, kHost2,
+                     kHost3, kHost4, kHost5, kHost6},
+                    begin_time);
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(2), {}},
+                           {Topic(1), {}},
+                           {Topic(6), {}},
+                           {Topic(5), {}},
+                           {Topic(4), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest,
+       TopTopicsRankingNotAffectedByAnnotationWeight) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
+                    begin_time);
+
+  // Setting the weight for Topic(1) and Topic(2) to 0.9. This weight shouldn't
+  // affect the top topics ordering.
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2}, 0.9)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(6), {}},
+                           {Topic(5), {}},
+                           {Topic(4), {}},
+                           {Topic(3), {}},
+                           {Topic(2), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, AllTopTopicsRandomlyPadded) {
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(101), {}},
+                           {Topic(102), {}},
+                           {Topic(103), {}},
+                           {Topic(104), {}},
+                           {Topic(105), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 0u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, TopTopicsPartiallyPadded) {
+  base::HistogramTester histograms;
+
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time);
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(6), {}},
+                           {Topic(5), {}},
+                           {Topic(4), {}},
+                           {Topic(101), {}},
+                           {Topic(102), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 3u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, TopTopicsAndObservingDomains) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
+                    begin_time);
+
+  AddApiUsageContextEntries(
+      {{kHost1, {}},
+       {kHost2, {}},
+       {kHost3, {HashedDomain(2)}},
+       {kHost4, {HashedDomain(3)}},
+       {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(
+      result.top_topics_and_observing_domains(),
+      {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(4), {HashedDomain(2), HashedDomain(3)}},
+       {Topic(3), {HashedDomain(2)}},
+       {Topic(2), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
+}
+
+TEST_F(
+    BrowsingTopicsCalculatorTest,
+    HistoryHostsBefore21DaysAgo_IgnoredForTopTopicsDecision_IgnoredForObservingDomainsDecision) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
+                    begin_time - base::Days(21));
+
+  AddApiUsageContextEntries(
+      {{kHost1, {}},
+       {kHost2, {}},
+       {kHost3, {HashedDomain(2)}},
+       {kHost4, {HashedDomain(3)}},
+       {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(101), {}},
+                           {Topic(102), {}},
+                           {Topic(103), {}},
+                           {Topic(104), {}},
+                           {Topic(105), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 0u);
+}
+
+TEST_F(
+    BrowsingTopicsCalculatorTest,
+    HistoryHostsBetween7And21Days_IgnoredForTopTopicsDecision_ConsideredForObservingDomainsDecision) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
+                    begin_time - base::Days(20));
+
+  AddApiUsageContextEntries(
+      {{kHost1, {}},
+       {kHost2, {}},
+       {kHost3, {HashedDomain(2)}},
+       {kHost4, {HashedDomain(3)}},
+       {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 103, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 103, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({103, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(101), {}},
+                           {Topic(102), {}},
+                           {Topic(103), {HashedDomain(2)}},
+                           {Topic(104), {}},
+                           {Topic(105), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 0u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest,
+       DataQueryBoundedByTopicsDataAccessibleSince) {
+  base::Time begin_time = base::Time::Now();
+
+  prefs_.SetTime(prefs::kPrivacySandboxTopicsDataAccessibleSince,
+                 begin_time + base::Days(6));
+
+  AddHistoryEntries({kHost1, kHost2}, begin_time);
+  AddApiUsageContextEntries({{kHost1, {}}, {kHost2, {}}});
+
+  task_environment_.AdvanceClock(base::Days(6));
+
+  AddHistoryEntries({kHost3, kHost4, kHost5, kHost6},
+                    begin_time + base::Days(6));
+  AddApiUsageContextEntries(
+      {{kHost3, {HashedDomain(2)}},
+       {kHost4, {HashedDomain(3)}},
+       {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(
+      result.top_topics_and_observing_domains(),
+      {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(4), {HashedDomain(2), HashedDomain(3)}},
+       {Topic(3), {HashedDomain(2)}},
+       {Topic(101), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 4u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest,
+       TopTopicsAndObservingDomains_DomainsSizeExceedsLimit) {
+  base::Time begin_time = base::Time::Now();
+
+  std::set<HashedDomain> large_size_domains;
+  for (int i = 1; i <= 1001; ++i) {
+    large_size_domains.insert(HashedDomain(i));
+  }
+
+  AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
+                    begin_time);
+
+  AddApiUsageContextEntries({{kHost1, {}},
+                             {kHost2, {}},
+                             {kHost3, {HashedDomain(2)}},
+                             {kHost4, {HashedDomain(3)}},
+                             {kHost5, large_size_domains}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  std::set<HashedDomain> expected_domains_after_capping = large_size_domains;
+  expected_domains_after_capping.erase(HashedDomain(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(result.top_topics_and_observing_domains(),
+                          {{Topic(6), expected_domains_after_capping},
+                           {Topic(5), expected_domains_after_capping},
+                           {Topic(4), {HashedDomain(2), HashedDomain(3)}},
+                           {Topic(3), {HashedDomain(2)}},
+                           {Topic(2), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, TopicBlocked) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost1, kHost2, kHost3, kHost4, kHost5, kHost6},
+                    begin_time);
+
+  AddApiUsageContextEntries(
+      {{kHost1, {}},
+       {kHost2, {}},
+       {kHost3, {HashedDomain(2)}},
+       {kHost4, {HashedDomain(3)}},
+       {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  privacy_sandbox_settings_->SetTopicAllowed(
+      privacy_sandbox::CanonicalTopic(Topic(6), kTaxonomyVersion),
+      /*allowed=*/false);
+  privacy_sandbox_settings_->SetTopicAllowed(
+      privacy_sandbox::CanonicalTopic(Topic(4), kTaxonomyVersion),
+      /*allowed=*/false);
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(
+      result.top_topics_and_observing_domains(),
+      {{Topic(0), {}},
+       {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(0), {}},
+       {Topic(3), {HashedDomain(2)}},
+       {Topic(2), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 5u);
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, PaddedTopicsDoNotDuplicate) {
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time);
+
+  AddApiUsageContextEntries(
+      {{kHost1, {}},
+       {kHost2, {}},
+       {kHost3, {HashedDomain(2)}},
+       {kHost4, {HashedDomain(3)}},
+       {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 102}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 102}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 102}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 102}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 102}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({102}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(
+      result.top_topics_and_observing_domains(),
+      {{Topic(102), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(4), {HashedDomain(3)}},
+       {Topic(101), {}},
+       {Topic(103), {}}});
+}
+
+TEST_F(BrowsingTopicsCalculatorTest, Metrics) {
+  base::HistogramTester histograms;
+
+  base::Time begin_time = base::Time::Now();
+
+  AddHistoryEntries({kHost4, kHost5, kHost6}, begin_time);
+
+  AddApiUsageContextEntries(
+      {{kHost1, {}},
+       {kHost2, {}},
+       {kHost3, {HashedDomain(2)}},
+       {kHost4, {HashedDomain(3)}},
+       {kHost5, {HashedDomain(1), HashedDomain(2), HashedDomain(3)}}});
+
+  test_page_content_annotator_.UsePageTopics(
+      *optimization_guide::TestModelInfoBuilder().SetVersion(1).Build(),
+      {{kTokenizedHost1, TopicsAndWeight({1, 2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost2, TopicsAndWeight({2, 3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost3, TopicsAndWeight({3, 4, 5, 6}, 0.1)},
+       {kTokenizedHost4, TopicsAndWeight({4, 5, 6}, 0.1)},
+       {kTokenizedHost5, TopicsAndWeight({5, 6}, 0.1)},
+       {kTokenizedHost6, TopicsAndWeight({6}, 0.1)}});
+
+  task_environment_.AdvanceClock(base::Seconds(1));
+
+  EpochTopics result = CalculateTopics();
+  ExpectResultTopicsEqual(
+      result.top_topics_and_observing_domains(),
+      {{Topic(6), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(5), {HashedDomain(1), HashedDomain(2), HashedDomain(3)}},
+       {Topic(4), {HashedDomain(3)}},
+       {Topic(101), {}},
+       {Topic(102), {}}});
+
+  EXPECT_EQ(result.padded_top_topics_start_index(), 3u);
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.EligibleDistinctHistoryHostsCount",
+      /*sample=*/3,
+      /*expected_bucket_count=*/1);
+
+  histograms.ExpectUniqueSample(
+      "BrowsingTopics.EpochTopicsCalculation.TopTopicsCountBeforePadding",
+      /*sample=*/3,
+      /*expected_bucket_count=*/1);
+
+  histograms.ExpectTotalCount(
+      "BrowsingTopics.EpochTopicsCalculation."
+      "ObservationContextDomainsCountPerTopTopic",
+      /*count=*/5);
+  histograms.ExpectBucketCount(
+      "BrowsingTopics.EpochTopicsCalculation."
+      "ObservationContextDomainsCountPerTopTopic",
+      /*sample=*/0,
+      /*expected_count=*/2);
+  histograms.ExpectBucketCount(
+      "BrowsingTopics.EpochTopicsCalculation."
+      "ObservationContextDomainsCountPerTopTopic",
+      /*sample=*/1,
+      /*expected_count=*/1);
+  histograms.ExpectBucketCount(
+      "BrowsingTopics.EpochTopicsCalculation."
+      "ObservationContextDomainsCountPerTopTopic",
+      /*sample=*/3,
+      /*expected_count=*/2);
+}
+
+}  // namespace browsing_topics
diff --git a/components/browsing_topics/common/common_types.h b/components/browsing_topics/common/common_types.h
index ed47152..4a59150 100644
--- a/components/browsing_topics/common/common_types.h
+++ b/components/browsing_topics/common/common_types.h
@@ -19,7 +19,7 @@
 
 struct COMPONENT_EXPORT(BROWSING_TOPICS_COMMON) ApiUsageContext {
   HashedDomain hashed_context_domain;
-  HashedHost hashed_top_host;
+  HashedHost hashed_main_frame_host;
   base::Time time;
 };
 
diff --git a/components/browsing_topics/epoch_topics.h b/components/browsing_topics/epoch_topics.h
index f16e0307..d38a3c2 100644
--- a/components/browsing_topics/epoch_topics.h
+++ b/components/browsing_topics/epoch_topics.h
@@ -57,6 +57,16 @@
   // reset `padded_top_topics_start_index_` to 0.
   void ClearTopics();
 
+  const std::vector<TopicAndDomains>& top_topics_and_observing_domains() const {
+    return top_topics_and_observing_domains_;
+  }
+
+  size_t padded_top_topics_start_index() const {
+    return padded_top_topics_start_index_;
+  }
+
+  size_t taxonomy_size() const { return taxonomy_size_; }
+
   int taxonomy_version() const { return taxonomy_version_; }
 
   int model_version() const { return model_version_; }
diff --git a/components/browsing_topics/test_util.cc b/components/browsing_topics/test_util.cc
new file mode 100644
index 0000000..e163029
--- /dev/null
+++ b/components/browsing_topics/test_util.cc
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/browsing_topics/test_util.h"
+
+namespace browsing_topics {
+
+TesterBrowsingTopicsCalculator::TesterBrowsingTopicsCalculator(
+    privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings,
+    history::HistoryService* history_service,
+    content::BrowsingTopicsSiteDataManager* site_data_manager,
+    optimization_guide::PageContentAnnotationsService* annotations_service,
+    CalculateCompletedCallback callback,
+    base::queue<uint64_t> rand_uint64_queue)
+    : BrowsingTopicsCalculator(privacy_sandbox_settings,
+                               history_service,
+                               site_data_manager,
+                               annotations_service,
+                               std::move(callback)),
+      rand_uint64_queue_(std::move(rand_uint64_queue)) {}
+
+TesterBrowsingTopicsCalculator::~TesterBrowsingTopicsCalculator() = default;
+
+uint64_t TesterBrowsingTopicsCalculator::GenerateRandUint64() {
+  DCHECK(!rand_uint64_queue_.empty());
+
+  uint64_t next_rand_uint64 = rand_uint64_queue_.front();
+  rand_uint64_queue_.pop();
+
+  return next_rand_uint64;
+}
+
+}  // namespace browsing_topics
diff --git a/components/browsing_topics/test_util.h b/components/browsing_topics/test_util.h
new file mode 100644
index 0000000..cc9c235
--- /dev/null
+++ b/components/browsing_topics/test_util.h
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_BROWSING_TOPICS_TEST_UTIL_H_
+#define COMPONENTS_BROWSING_TOPICS_TEST_UTIL_H_
+
+#include "base/containers/queue.h"
+#include "components/browsing_topics/browsing_topics_calculator.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace browsing_topics {
+
+// A tester class that allows mocking the generated random numbers.
+class TesterBrowsingTopicsCalculator : public BrowsingTopicsCalculator {
+ public:
+  // Initialize a regular `BrowsingTopicsCalculator` with an additional
+  // `rand_uint64_queue` member for generating random numbers.
+  TesterBrowsingTopicsCalculator(
+      privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings,
+      history::HistoryService* history_service,
+      content::BrowsingTopicsSiteDataManager* site_data_manager,
+      optimization_guide::PageContentAnnotationsService* annotations_service,
+      CalculateCompletedCallback callback,
+      base::queue<uint64_t> rand_uint64_queue);
+
+  ~TesterBrowsingTopicsCalculator() override;
+
+  TesterBrowsingTopicsCalculator(const TesterBrowsingTopicsCalculator&) =
+      delete;
+  TesterBrowsingTopicsCalculator& operator=(
+      const TesterBrowsingTopicsCalculator&) = delete;
+  TesterBrowsingTopicsCalculator(TesterBrowsingTopicsCalculator&&) = delete;
+  TesterBrowsingTopicsCalculator& operator=(TesterBrowsingTopicsCalculator&&) =
+      delete;
+
+  // Pop and return the next number in `rand_uint64_queue_`. Precondition:
+  // `rand_uint64_queue_` is not empty.
+  uint64_t GenerateRandUint64() override;
+
+ private:
+  base::queue<uint64_t> rand_uint64_queue_;
+};
+
+}  // namespace browsing_topics
+
+#endif  // COMPONENTS_BROWSING_TOPICS_TEST_UTIL_H_
diff --git a/components/browsing_topics/topic_and_domains.h b/components/browsing_topics/topic_and_domains.h
index 1663378..dd335ef 100644
--- a/components/browsing_topics/topic_and_domains.h
+++ b/components/browsing_topics/topic_and_domains.h
@@ -33,6 +33,8 @@
   static TopicAndDomains FromDictValue(const base::Value::Dict& dict_value);
   base::Value::Dict ToDictValue() const;
 
+  bool IsValid() const { return topic_ != Topic(0); }
+
   const Topic& topic() const { return topic_; }
 
   const std::set<HashedDomain>& hashed_domains() const {
diff --git a/components/browsing_topics/topic_and_domains_unittest.cc b/components/browsing_topics/topic_and_domains_unittest.cc
index c89bc19..8424d97a 100644
--- a/components/browsing_topics/topic_and_domains_unittest.cc
+++ b/components/browsing_topics/topic_and_domains_unittest.cc
@@ -14,6 +14,7 @@
   TopicAndDomains read_topic_and_domains =
       TopicAndDomains::FromDictValue(base::Value::Dict());
 
+  EXPECT_FALSE(read_topic_and_domains.IsValid());
   EXPECT_EQ(read_topic_and_domains.topic(), Topic(0));
   EXPECT_TRUE(read_topic_and_domains.hashed_domains().empty());
 }
@@ -25,6 +26,7 @@
   TopicAndDomains read_topic_and_domains =
       TopicAndDomains::FromDictValue(dict_value);
 
+  EXPECT_FALSE(read_topic_and_domains.IsValid());
   EXPECT_EQ(read_topic_and_domains.topic(), Topic(0));
   EXPECT_TRUE(read_topic_and_domains.hashed_domains().empty());
 }
@@ -38,6 +40,7 @@
   TopicAndDomains read_topic_and_domains =
       TopicAndDomains::FromDictValue(dict_value);
 
+  EXPECT_TRUE(read_topic_and_domains.IsValid());
   EXPECT_EQ(read_topic_and_domains.topic(), Topic(2));
   EXPECT_EQ(read_topic_and_domains.hashed_domains(),
             std::set({HashedDomain(123), HashedDomain(456)}));
diff --git a/components/browsing_topics/util.cc b/components/browsing_topics/util.cc
index 7de6513b..998fb09 100644
--- a/components/browsing_topics/util.cc
+++ b/components/browsing_topics/util.cc
@@ -10,6 +10,7 @@
 #include "base/rand_util.h"
 #include "crypto/hmac.h"
 #include "crypto/sha2.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace browsing_topics {
 
@@ -28,7 +29,7 @@
 const char kEpochSwitchTimeDecisionPrefix[] =
     "TopicsV1_EpochSwitchTimeDecision|";
 const char kContextDomainStoragePrefix[] = "TopicsV1_ContextDomainStorage|";
-const char kTopHostStoragePrefix[] = "TopicsV1_TopHostStorage|";
+const char kMainFrameHostStoragePrefix[] = "TopicsV1_MainFrameHostStorage|";
 
 uint64_t HmacHash(ReadOnlyHmacKey hmac_key,
                   const std::string& use_case_prefix,
@@ -48,6 +49,16 @@
 
 }  // namespace
 
+absl::optional<size_t> GetTaxonomySize() {
+  if (blink::features::kBrowsingTopicsTaxonomyVersion.Get() == 1) {
+    // Taxonomy version 1 has 349 topics.
+    // https://github.com/jkarlin/topics/blob/main/taxonomy_v1.md
+    return 349;
+  }
+
+  return absl::nullopt;
+}
+
 HmacKey GenerateRandomHmacKey() {
   if (g_hmac_key_override_for_testing.IsCreated())
     return g_hmac_key_override_for_testing.Get();
@@ -112,13 +123,32 @@
       HmacHash(hmac_key, kContextDomainStoragePrefix, context_domain));
 }
 
-HashedHost HashTopHostForStorage(const std::string& top_host) {
+HashedHost HashMainFrameHostForStorage(const std::string& main_frame_host) {
   int64_t result;
-  crypto::SHA256HashString(kTopHostStoragePrefix + top_host, &result,
-                           sizeof(result));
+  crypto::SHA256HashString(kMainFrameHostStoragePrefix + main_frame_host,
+                           &result, sizeof(result));
   return HashedHost(result);
 }
 
+base::Time DeriveHistoryDataStartTime(base::Time calculation_time,
+                                      base::Time data_accessible_since) {
+  return std::max(data_accessible_since,
+                  calculation_time -
+                      blink::features::kBrowsingTopicsTimePeriodPerEpoch.Get());
+}
+
+base::Time DeriveApiUsageContextDataStartTime(
+    base::Time calculation_time,
+    base::Time data_accessible_since) {
+  return std::max(
+      data_accessible_since,
+      calculation_time -
+          blink::features::
+                  kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering
+                      .Get() *
+              blink::features::kBrowsingTopicsTimePeriodPerEpoch.Get());
+}
+
 void OverrideHmacKeyForTesting(ReadOnlyHmacKey hmac_key) {
   std::copy(hmac_key.begin(), hmac_key.end(),
             g_hmac_key_override_for_testing.Get().begin());
diff --git a/components/browsing_topics/util.h b/components/browsing_topics/util.h
index 943ddd5..b8f7e84 100644
--- a/components/browsing_topics/util.h
+++ b/components/browsing_topics/util.h
@@ -8,12 +8,22 @@
 #include "base/containers/span.h"
 #include "base/time/time.h"
 #include "components/browsing_topics/common/common_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace browsing_topics {
 
 using HmacKey = std::array<uint8_t, 32>;
 using ReadOnlyHmacKey = base::span<const uint8_t, 32>;
 
+// Get the size of the taxonomy. This is used for generating random topics from
+// [1, `GetTaxonomySize()`]. It returns nullopt if this Chrome binary does not
+// support the finch configured taxonomy version
+// `kBrowsingTopicsTaxonomyVersion`.
+//
+// TODO(yaoxia): this should be maintained by UX along with the string mappings.
+// Consider moving to a UX component.
+absl::optional<size_t> GetTaxonomySize();
+
 // Generate a 256 bit random hmac key.
 HmacKey GenerateRandomHmacKey();
 
@@ -53,9 +63,21 @@
 HashedDomain HashContextDomainForStorage(ReadOnlyHmacKey hmac_key,
                                          const std::string& context_domain);
 
-// Returns a hash of `top_host` to be stored more efficiently in disk and
+// Returns a hash of `main_frame_host` to be stored more efficiently in disk and
 // memory.
-HashedHost HashTopHostForStorage(const std::string& top_host);
+HashedHost HashMainFrameHostForStorage(const std::string& main_frame_host);
+
+// Returns the maximum of |`calculation_time` - history data time range|, and
+// |data_accessible_since|.
+base::Time DeriveHistoryDataStartTime(
+    base::Time calculation_time,
+    base::Time data_accessible_since = base::Time());
+
+// Returns the maximum of |`calculation_time` - api usage data time range|,
+// and |data_accessible_since|.
+base::Time DeriveApiUsageContextDataStartTime(
+    base::Time calculation_time,
+    base::Time data_accessible_since = base::Time());
 
 // Override the key to be returned for subsequent invocations of
 // `GenerateRandomHmacKey()`.
diff --git a/components/browsing_topics/util_unittest.cc b/components/browsing_topics/util_unittest.cc
index ff8e2580..569ab81 100644
--- a/components/browsing_topics/util_unittest.cc
+++ b/components/browsing_topics/util_unittest.cc
@@ -247,11 +247,12 @@
   }));
 }
 
-TEST_F(BrowsingTopicsUtilTest, HashTopHostForStorage) {
+TEST_F(BrowsingTopicsUtilTest, HashMainFrameHostForStorage) {
   CheckUniformRandom(base::BindLambdaForTesting([&]() {
-    std::string top_host = GenerateRandomDomainOrHost();
+    std::string main_frame_host = GenerateRandomDomainOrHost();
 
-    return static_cast<uint64_t>(HashTopHostForStorage(top_host).value());
+    return static_cast<uint64_t>(
+        HashMainFrameHostForStorage(main_frame_host).value());
   }));
 }
 
diff --git a/components/commerce/core/BUILD.gn b/components/commerce/core/BUILD.gn
index 4d499db..aeee6873 100644
--- a/components/commerce/core/BUILD.gn
+++ b/components/commerce/core/BUILD.gn
@@ -19,9 +19,30 @@
   deps = [
     "//base",
     "//components/flags_ui",
+    "//components/search",
     "//third_party/re2:re2",
     "//url:url",
   ]
+
+  if (!is_android) {
+    deps += [ ":commerce_heuristics_data" ]
+  }
+}
+
+source_set("feature_list_unittests") {
+  testonly = true
+  sources = [ "commerce_feature_list_unittest.cc" ]
+  deps = [
+    ":feature_list",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//third_party/re2:re2",
+  ]
+
+  if (!is_android) {
+    deps += [ ":commerce_heuristics_data" ]
+  }
 }
 
 static_library("metrics") {
diff --git a/components/commerce/core/DEPS b/components/commerce/core/DEPS
index d2e100a..d4d4a89 100644
--- a/components/commerce/core/DEPS
+++ b/components/commerce/core/DEPS
@@ -3,4 +3,5 @@
   "+components/optimization_guide",
   "+components/prefs",
   "+third_party/re2",
+  "+components/search",
 ]
diff --git a/components/commerce/core/commerce_feature_list.cc b/components/commerce/core/commerce_feature_list.cc
index 7b0f698..77e84be 100644
--- a/components/commerce/core/commerce_feature_list.cc
+++ b/components/commerce/core/commerce_feature_list.cc
@@ -7,22 +7,58 @@
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
+#include "build/buildflag.h"
+#if !BUILDFLAG(IS_ANDROID)
+#include "components/commerce/core/commerce_heuristics_data.h"
+#endif  // !BUILDFLAG(IS_ANDROID)
 #include "third_party/re2/src/re2/re2.h"
 
 namespace commerce {
 
 namespace {
 
-constexpr base::FeatureParam<std::string> kPartnerMerchantPattern{
+constexpr base::FeatureParam<std::string> kRulePartnerMerchantPattern{
+    &ntp_features::kNtpChromeCartModule, "partner-merchant-pattern",
+    // This regex does not match anything.
+    "\\b\\B"};
+
+constexpr base::FeatureParam<std::string> kCouponPartnerMerchantPattern{
     &commerce::kRetailCoupons, "coupon-partner-merchant-pattern",
     // This regex does not match anything.
     "\\b\\B"};
 
-const re2::RE2& GetPartnerMerchantPattern() {
+const re2::RE2& GetRulePartnerMerchantPattern() {
+#if !BUILDFLAG(IS_ANDROID)
+  auto* pattern_from_component =
+      commerce_heuristics::CommerceHeuristicsData::GetInstance()
+          .GetRuleDiscountPartnerMerchantPattern();
+  if (pattern_from_component && kRulePartnerMerchantPattern.Get() ==
+                                    kRulePartnerMerchantPattern.default_value) {
+    return *pattern_from_component;
+  }
+#endif  // !BUILDFLAG(IS_ANDROID)
   re2::RE2::Options options;
   options.set_case_sensitive(false);
-  static base::NoDestructor<re2::RE2> instance(kPartnerMerchantPattern.Get(),
-                                               options);
+  static base::NoDestructor<re2::RE2> instance(
+      kRulePartnerMerchantPattern.Get(), options);
+  return *instance;
+}
+
+const re2::RE2& GetCouponPartnerMerchantPattern() {
+#if !BUILDFLAG(IS_ANDROID)
+  auto* pattern_from_component =
+      commerce_heuristics::CommerceHeuristicsData::GetInstance()
+          .GetCouponDiscountPartnerMerchantPattern();
+  if (pattern_from_component &&
+      kCouponPartnerMerchantPattern.Get() ==
+          kCouponPartnerMerchantPattern.default_value) {
+    return *pattern_from_component;
+  }
+#endif  // !BUILDFLAG(IS_ANDROID)
+  re2::RE2::Options options;
+  options.set_case_sensitive(false);
+  static base::NoDestructor<re2::RE2> instance(
+      kCouponPartnerMerchantPattern.Get(), options);
   return *instance;
 }
 
@@ -55,11 +91,121 @@
 const base::Feature kDiscountConsentV2{"DiscountConsentV2",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Params for Discount Consent V2 in the NTP Cart module.
+const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[] =
+    "discount-consent-ntp-variation";
+const base::FeatureParam<int> kNtpChromeCartModuleDiscountConsentNtpVariation{
+    &commerce::kDiscountConsentV2,
+    kNtpChromeCartModuleDiscountConsentNtpVariationParam, 0};
+const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[] =
+    "discount-consent-ntp-reshow-time";
+const base::FeatureParam<base::TimeDelta>
+    kNtpChromeCartModuleDiscountConsentReshowTime{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentReshowTimeParam, base::Days(28)};
+const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[] =
+    "discount-consent-ntp-max-dismiss-count";
+const base::FeatureParam<int>
+    kNtpChromeCartModuleDiscountConsentMaxDismissalCount{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam, 1};
+
+// String change variation params.
+const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[] =
+    "string-change-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentStringChangeContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentStringChangeContentParam, ""};
+
+const char kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[] =
+    "inline-card-show-button";
+const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentInlineShowCloseButton{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentStringChangeContentParam, true};
+
+// Discount consent v2 step 1 params.
+const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[] =
+        "step-one-use-static-content";
+const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam,
+        false};
+const char kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[] =
+    "step-one-static-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam, ""};
+const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[] =
+    "step-one-one-cart-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam, ""};
+const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[] =
+    "step-one-two-carts-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam, ""};
+const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[] =
+        "step-one-three-carts-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam,
+        ""};
+
+// Discount consent v2 step 2 params.
+const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[] =
+    "step-two-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepTwoContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam, ""};
+const char
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[] =
+        "step-two-different-color";
+const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam,
+        false};
+const char kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[] =
+    "dialog-content-title";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam, ""};
+
+bool IsPartnerMerchant(const GURL& url) {
+  return commerce::IsCouponDiscountPartnerMerchant(url) ||
+         IsRuleDiscountPartnerMerchant(url);
+}
+
+bool IsRuleDiscountPartnerMerchant(const GURL& url) {
+  const std::string& url_string = url.spec();
+  return RE2::PartialMatch(
+      re2::StringPiece(url_string.data(), url_string.size()),
+      GetRulePartnerMerchantPattern());
+}
+
 bool IsCouponDiscountPartnerMerchant(const GURL& url) {
   const std::string& url_string = url.spec();
   return RE2::PartialMatch(
       re2::StringPiece(url_string.data(), url_string.size()),
-      GetPartnerMerchantPattern());
+      GetCouponPartnerMerchantPattern());
+}
+
+bool IsCartDiscountFeatureEnabled() {
+  return base::GetFieldTrialParamByFeatureAsBool(
+      ntp_features::kNtpChromeCartModule,
+      ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam, false);
 }
 
 bool IsCouponWithCodeEnabled() {
@@ -67,4 +213,10 @@
       kRetailCoupons, kRetailCouponsWithCodeParam, true);
 }
 
+bool IsFakeDataEnabled() {
+  return base::GetFieldTrialParamValueByFeature(
+             ntp_features::kNtpChromeCartModule,
+             ntp_features::kNtpChromeCartModuleDataParam) == "fake";
+}
+
 }  // namespace commerce
diff --git a/components/commerce/core/commerce_feature_list.h b/components/commerce/core/commerce_feature_list.h
index 40042c268..4edbe38 100644
--- a/components/commerce/core/commerce_feature_list.h
+++ b/components/commerce/core/commerce_feature_list.h
@@ -8,6 +8,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "components/flags_ui/feature_entry.h"
+#include "components/search/ntp_features.h"
 #include "url/gurl.h"
 
 namespace commerce {
@@ -75,14 +76,122 @@
 // Feature flag for Discount user consent v2.
 extern const base::Feature kDiscountConsentV2;
 
+// Feature parameters for ChromeCart on Desktop.
+
+// Whether to use OptimizationGuide to optimize renderer signal collection.
+constexpr base::FeatureParam<bool> kOptimizeRendererSignal(
+    &ntp_features::kNtpChromeCartModule,
+    "optimize-renderer-signal",
+    false);
+
+constexpr base::FeatureParam<base::TimeDelta> kDiscountFetchDelayParam(
+    &ntp_features::kNtpChromeCartModule,
+    "discount-fetch-delay",
+    base::Hours(6));
+
 // Interval that controls the frequency of showing coupons in infobar bubbles.
 constexpr base::FeatureParam<base::TimeDelta> kCouponDisplayInterval{
     &commerce::kRetailCoupons, "coupon_display_interval", base::Hours(18)};
 
+// The following are Feature params for Discount user consent v2.
+// This indicates the Discount Consent v2 variation on the NTP Cart module.
+enum class DiscountConsentNtpVariation {
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  kDefault = 0,
+  kStringChange = 1,
+  kInline = 2,
+  kDialog = 3,
+  kMaxValue = kDialog
+};
+
+// Param indicates the ConsentV2 variation. See DiscountConsentNtpVariation
+// enum.
+extern const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[];
+extern const base::FeatureParam<int>
+    kNtpChromeCartModuleDiscountConsentNtpVariation;
+// The time interval, after the last dismissal, before reshowing the consent.
+extern const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[];
+extern const base::FeatureParam<base::TimeDelta>
+    kNtpChromeCartModuleDiscountConsentReshowTime;
+// The max number of dismisses allowed.
+extern const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[];
+extern const base::FeatureParam<int>
+    kNtpChromeCartModuleDiscountConsentMaxDismissalCount;
+
+// String change variation params. This string is replacing the content string
+// of the v1 consent.
+extern const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentStringChangeContent;
+
+// DiscountConsentNtpVariation::kInline and DiscountConsentNtpVariation::kDialog
+// params. This indicate whether the 'x' button should show.
+extern const char
+    kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[];
+extern const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentInlineShowCloseButton;
+
+// The following are discount consent step 1 params.
+// This indicates whether the content in step 1 is a static string that does not
+// contain any merchant names.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[];
+extern const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent;
+// This the content string use in step 1 if
+// kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent.Get() is true.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent;
+// This is a string template that takes in one merchant name, and it's used when
+// there is only 1 Chrome Cart.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart;
+// This is a string template that takes in two merchant names, and it's used
+// when there are only 2 Chrome Carts.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts;
+// This is a string template that takes in two merchant names, and it's used
+// when there are 3 or more Chrome Carts.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts;
+
+// The following are discount consent step 2 params.
+// This is the content string used in step 2. This is the actual consent string.
+extern const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepTwoContent;
+// This is used to indicate whether the backgound-color of step 2 should change.
+extern const char
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[];
+extern const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor;
+// This is the content title use in the dialog consent.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle;
+
+// Check if a URL belongs to a partner merchant of any type of discount.
+bool IsPartnerMerchant(const GURL& url);
+// Check if a URL belongs to a partner merchant of rule discount.
+bool IsRuleDiscountPartnerMerchant(const GURL& url);
 // Check if a URL belongs to a partner merchant of coupon discount.
 bool IsCouponDiscountPartnerMerchant(const GURL& url);
+// Check if cart discount feature is enabled.
+bool IsCartDiscountFeatureEnabled();
 // Check if the feature variation of coupons with code is enabled.
 bool IsCouponWithCodeEnabled();
+// Check if the variation with fake data is enabled.
+bool IsFakeDataEnabled();
 }  // namespace commerce
 
 #endif  // COMPONENTS_COMMERCE_CORE_COMMERCE_FEATURE_LIST_H_
diff --git a/components/commerce/core/commerce_feature_list_unittest.cc b/components/commerce/core/commerce_feature_list_unittest.cc
new file mode 100644
index 0000000..d3225a1b
--- /dev/null
+++ b/components/commerce/core/commerce_feature_list_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/commerce/core/commerce_feature_list.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/buildflag.h"
+#include "components/commerce/core/commerce_heuristics_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+#if !BUILDFLAG(IS_ANDROID)
+std::map<std::string, std::string> kRulePartnerMerchantParams = {
+    {"partner-merchant-pattern", "foo"}};
+std::map<std::string, std::string> kCouponPartnerMerchantParams = {
+    {"coupon-partner-merchant-pattern", "bar"}};
+const char kGlobalHeuristicsJSONData[] = R"###(
+      {
+        "rule_discount_partner_merchant_regex": "baz",
+        "coupon_discount_partner_merchant_regex": "qux"
+      }
+  )###";
+const char kRuleFeatureParamPartnerMerchantURL[] = "https://www.foo.com";
+const char kCouponFeatureParamPartnerMerchantURL[] = "https://www.bar.com";
+const char kRuleComponentPartnerMerchantURL[] = "https://www.baz.com";
+const char kCouponComponentPartnerMerchantURL[] = "https://www.qux.com";
+#endif  //! BUILDFLAG(IS_ANDROID)
+}  // namespace
+
+class CommerceFeatureListTest : public testing::Test {
+ public:
+  void TearDown() override { features_.Reset(); }
+
+ protected:
+  base::test::ScopedFeatureList features_;
+};
+
+#if !BUILDFLAG(IS_ANDROID)
+TEST_F(CommerceFeatureListTest, TestRulePartnerMerchant_FromFeatureParam) {
+  features_.InitWithFeaturesAndParameters(
+      {{ntp_features::kNtpChromeCartModule, kRulePartnerMerchantParams},
+       {commerce::kRetailCoupons, kCouponPartnerMerchantParams}},
+      {});
+
+  ASSERT_TRUE(commerce::IsRuleDiscountPartnerMerchant(
+      GURL(kRuleFeatureParamPartnerMerchantURL)));
+  ASSERT_FALSE(commerce::IsRuleDiscountPartnerMerchant(
+      GURL(kCouponFeatureParamPartnerMerchantURL)));
+  ASSERT_TRUE(commerce::IsCouponDiscountPartnerMerchant(
+      GURL(kCouponFeatureParamPartnerMerchantURL)));
+  ASSERT_FALSE(commerce::IsCouponDiscountPartnerMerchant(
+      GURL(kRuleFeatureParamPartnerMerchantURL)));
+}
+
+TEST_F(CommerceFeatureListTest, TestRulePartnerMerchant_FromComponent) {
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+  ASSERT_TRUE(
+      data.PopulateDataFromComponent("{}", kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_TRUE(commerce::IsRuleDiscountPartnerMerchant(
+      GURL(kRuleComponentPartnerMerchantURL)));
+  ASSERT_FALSE(commerce::IsRuleDiscountPartnerMerchant(
+      GURL(kCouponComponentPartnerMerchantURL)));
+  ASSERT_TRUE(commerce::IsCouponDiscountPartnerMerchant(
+      GURL(kCouponComponentPartnerMerchantURL)));
+  ASSERT_FALSE(commerce::IsCouponDiscountPartnerMerchant(
+      GURL(kRuleComponentPartnerMerchantURL)));
+}
+
+TEST_F(CommerceFeatureListTest, TestCouponPartnerMerchant_Priority) {
+  features_.InitWithFeaturesAndParameters(
+      {{ntp_features::kNtpChromeCartModule, kRulePartnerMerchantParams},
+       {commerce::kRetailCoupons, kCouponPartnerMerchantParams}},
+      {});
+
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+  ASSERT_TRUE(
+      data.PopulateDataFromComponent("{}", kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_TRUE(commerce::IsRuleDiscountPartnerMerchant(
+      GURL(kRuleFeatureParamPartnerMerchantURL)));
+  ASSERT_FALSE(commerce::IsRuleDiscountPartnerMerchant(
+      GURL(kRuleComponentPartnerMerchantURL)));
+  ASSERT_TRUE(commerce::IsCouponDiscountPartnerMerchant(
+      GURL(kCouponFeatureParamPartnerMerchantURL)));
+  ASSERT_FALSE(commerce::IsCouponDiscountPartnerMerchant(
+      GURL(kCouponComponentPartnerMerchantURL)));
+}
+#endif  //! BUILDFLAG(IS_ANDROID)
diff --git a/components/commerce/core/commerce_heuristics_data.cc b/components/commerce/core/commerce_heuristics_data.cc
index 9942770..15aba3d 100644
--- a/components/commerce/core/commerce_heuristics_data.cc
+++ b/components/commerce/core/commerce_heuristics_data.cc
@@ -16,6 +16,15 @@
 
 // CommerceGlobalHeuristics types.
 constexpr char kSkipProductPatternType[] = "sensitive_product_regex";
+constexpr char kRuleDiscountPartnerMerchantPatternType[] =
+    "rule_discount_partner_merchant_regex";
+constexpr char kCouponDiscountPartnerMerchantPatternType[] =
+    "coupon_discount_partner_merchant_regex";
+constexpr char kCartPagetURLPatternType[] = "cart_page_url_regex";
+constexpr char kCheckoutPageURLPatternType[] = "checkout_page_url_regex";
+constexpr char kPurchaseButtonTextPatternType[] = "purchase_button_text_regex";
+constexpr char kAddToCartRequestPatternType[] = "add_to_cart_request_regex";
+
 }  // namespace
 
 // static
@@ -44,7 +53,18 @@
   }
   hint_heuristics_ = std::move(*hint_json_value->GetIfDict());
   global_heuristics_ = std::move(*global_json_value->GetIfDict());
+  // Global regex patterns.
   product_skip_pattern_ = ConstructGlobalRegex(kSkipProductPatternType);
+  rule_discount_partner_merchant_pattern_ =
+      ConstructGlobalRegex(kRuleDiscountPartnerMerchantPatternType);
+  coupon_discount_partner_merchant_pattern_ =
+      ConstructGlobalRegex(kCouponDiscountPartnerMerchantPatternType);
+  cart_url_pattern_ = ConstructGlobalRegex(kCartPagetURLPatternType);
+  checkout_url_pattern_ = ConstructGlobalRegex(kCheckoutPageURLPatternType);
+  purchase_button_pattern_ =
+      ConstructGlobalRegex(kPurchaseButtonTextPatternType);
+  add_to_cart_request_pattern_ =
+      ConstructGlobalRegex(kAddToCartRequestPatternType);
   return true;
 }
 
@@ -62,6 +82,32 @@
   return product_skip_pattern_.get();
 }
 
+const re2::RE2*
+CommerceHeuristicsData::GetRuleDiscountPartnerMerchantPattern() {
+  return rule_discount_partner_merchant_pattern_.get();
+}
+
+const re2::RE2*
+CommerceHeuristicsData::GetCouponDiscountPartnerMerchantPattern() {
+  return coupon_discount_partner_merchant_pattern_.get();
+}
+
+const re2::RE2* CommerceHeuristicsData::GetCartPageURLPattern() {
+  return cart_url_pattern_.get();
+}
+
+const re2::RE2* CommerceHeuristicsData::GetCheckoutPageURLPattern() {
+  return checkout_url_pattern_.get();
+}
+
+const re2::RE2* CommerceHeuristicsData::GetPurchaseButtonTextPattern() {
+  return purchase_button_pattern_.get();
+}
+
+const re2::RE2* CommerceHeuristicsData::GetAddToCartRequestPattern() {
+  return add_to_cart_request_pattern_.get();
+}
+
 absl::optional<std::string> CommerceHeuristicsData::GetCommerceHintHeuristics(
     const std::string& type,
     const std::string& domain) {
diff --git a/components/commerce/core/commerce_heuristics_data.h b/components/commerce/core/commerce_heuristics_data.h
index 3cd7391..1c3f7519 100644
--- a/components/commerce/core/commerce_heuristics_data.h
+++ b/components/commerce/core/commerce_heuristics_data.h
@@ -34,6 +34,27 @@
   // Try to get the product skip pattern.
   const re2::RE2* GetProductSkipPattern();
 
+  // Try to get the pattern regex to decide if a merchant is a partner merchant
+  // for rule discount.
+  const re2::RE2* GetRuleDiscountPartnerMerchantPattern();
+
+  // Try to get the pattern regex to decide if a merchant is a partner merchant
+  // for coupon discount.
+  const re2::RE2* GetCouponDiscountPartnerMerchantPattern();
+
+  // Try to get the pattern regex to decide if a URL is cart page URL.
+  const re2::RE2* GetCartPageURLPattern();
+
+  // Try to get the pattern regex to decide if a URL is checkout page URL.
+  const re2::RE2* GetCheckoutPageURLPattern();
+
+  // Try to get the pattern regex to decide if a button is a purchase button.
+  const re2::RE2* GetPurchaseButtonTextPattern();
+
+  // Try to get the pattern regex to decide if a request is a add-to-cart
+  // request.
+  const re2::RE2* GetAddToCartRequestPattern();
+
  private:
   friend class CommerceHeuristicsDataTest;
 
@@ -49,6 +70,12 @@
   base::Value::Dict hint_heuristics_;
   base::Value::Dict global_heuristics_;
   std::unique_ptr<re2::RE2> product_skip_pattern_;
+  std::unique_ptr<re2::RE2> rule_discount_partner_merchant_pattern_;
+  std::unique_ptr<re2::RE2> coupon_discount_partner_merchant_pattern_;
+  std::unique_ptr<re2::RE2> cart_url_pattern_;
+  std::unique_ptr<re2::RE2> checkout_url_pattern_;
+  std::unique_ptr<re2::RE2> purchase_button_pattern_;
+  std::unique_ptr<re2::RE2> add_to_cart_request_pattern_;
 };
 
 }  // namespace commerce_heuristics
diff --git a/components/commerce/core/commerce_heuristics_data_unittest.cc b/components/commerce/core/commerce_heuristics_data_unittest.cc
index f660e7c..7d90958 100644
--- a/components/commerce/core/commerce_heuristics_data_unittest.cc
+++ b/components/commerce/core/commerce_heuristics_data_unittest.cc
@@ -22,7 +22,13 @@
   )###";
 const char kGlobalHeuristicsJSONData[] = R"###(
       {
-        "sensitive_product_regex": "\\b\\B"
+        "sensitive_product_regex": "\\b\\B",
+        "rule_discount_partner_merchant_regex": "foo",
+        "coupon_discount_partner_merchant_regex": "bar",
+        "cart_page_url_regex": "cart",
+        "checkout_page_url_regex": "checkout",
+        "purchase_button_text_regex": "purchase",
+        "add_to_cart_request_regex": "add_to_cart"
       }
   )###";
 }  // namespace
@@ -59,10 +65,31 @@
   ASSERT_EQ(*hint_heuristics->FindDict("bar.com")->FindString("merchant_name"),
             "Bar");
   auto* global_heuristics = GetGlobalHeuristics();
-  ASSERT_EQ(global_heuristics->size(), 1u);
+  ASSERT_EQ(global_heuristics->size(), 7u);
   ASSERT_TRUE(global_heuristics->contains("sensitive_product_regex"));
   ASSERT_EQ(*global_heuristics->FindString("sensitive_product_regex"),
             "\\b\\B");
+  ASSERT_TRUE(
+      global_heuristics->contains("rule_discount_partner_merchant_regex"));
+  ASSERT_EQ(
+      *global_heuristics->FindString("rule_discount_partner_merchant_regex"),
+      "foo");
+  ASSERT_TRUE(
+      global_heuristics->contains("coupon_discount_partner_merchant_regex"));
+  ASSERT_EQ(
+      *global_heuristics->FindString("coupon_discount_partner_merchant_regex"),
+      "bar");
+  ASSERT_TRUE(global_heuristics->contains("cart_page_url_regex"));
+  ASSERT_EQ(*global_heuristics->FindString("cart_page_url_regex"), "cart");
+  ASSERT_TRUE(global_heuristics->contains("checkout_page_url_regex"));
+  ASSERT_EQ(*global_heuristics->FindString("checkout_page_url_regex"),
+            "checkout");
+  ASSERT_TRUE(global_heuristics->contains("purchase_button_text_regex"));
+  ASSERT_EQ(*global_heuristics->FindString("purchase_button_text_regex"),
+            "purchase");
+  ASSERT_TRUE(global_heuristics->contains("add_to_cart_request_regex"));
+  ASSERT_EQ(*global_heuristics->FindString("add_to_cart_request_regex"),
+            "add_to_cart");
 }
 
 TEST_F(CommerceHeuristicsDataTest, TestPopulateHeuristics_Failure) {
@@ -114,4 +141,59 @@
 
   ASSERT_EQ(data.GetProductSkipPattern()->pattern(), "\\b\\B");
 }
+
+TEST_F(CommerceHeuristicsDataTest, TestGetRuleDiscountPartnerMerchantPattern) {
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+
+  ASSERT_TRUE(data.PopulateDataFromComponent(
+      kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_EQ(data.GetRuleDiscountPartnerMerchantPattern()->pattern(), "foo");
+}
+
+TEST_F(CommerceHeuristicsDataTest,
+       TestGetCouponDiscountPartnerMerchantPattern) {
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+
+  ASSERT_TRUE(data.PopulateDataFromComponent(
+      kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_EQ(data.GetCouponDiscountPartnerMerchantPattern()->pattern(), "bar");
+}
+
+TEST_F(CommerceHeuristicsDataTest, TestGetCartPageURLPattern) {
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+
+  ASSERT_TRUE(data.PopulateDataFromComponent(
+      kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_EQ(data.GetCartPageURLPattern()->pattern(), "cart");
+}
+
+TEST_F(CommerceHeuristicsDataTest, TestGetCheckoutPageURLPattern) {
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+
+  ASSERT_TRUE(data.PopulateDataFromComponent(
+      kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_EQ(data.GetCheckoutPageURLPattern()->pattern(), "checkout");
+}
+
+TEST_F(CommerceHeuristicsDataTest, TestGetPurchaseButtonTextPattern) {
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+
+  ASSERT_TRUE(data.PopulateDataFromComponent(
+      kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_EQ(data.GetPurchaseButtonTextPattern()->pattern(), "purchase");
+}
+
+TEST_F(CommerceHeuristicsDataTest, TestGetAddToCartRequestPattern) {
+  auto& data = commerce_heuristics::CommerceHeuristicsData::GetInstance();
+
+  ASSERT_TRUE(data.PopulateDataFromComponent(
+      kHintHeuristicsJSONData, kGlobalHeuristicsJSONData, "", ""));
+
+  ASSERT_EQ(data.GetAddToCartRequestPattern()->pattern(), "add_to_cart");
+}
 }  // namespace commerce_heuristics
diff --git a/components/content_creation/notes/core/BUILD.gn b/components/content_creation/notes/core/BUILD.gn
index f93488f3..4c5dadba 100644
--- a/components/content_creation/notes/core/BUILD.gn
+++ b/components/content_creation/notes/core/BUILD.gn
@@ -57,6 +57,7 @@
     "//base/test:test_support",
     "//components/content_creation/notes/core/server",
     "//components/content_creation/notes/core/templates",
+    "//components/content_creation/notes/core/templates:template_storage_proto",
     "//components/content_creation/notes/core/templates:types",
     "//components/content_creation/notes/core/test:test_support",
     "//components/prefs:test_support",
diff --git a/components/content_creation/notes/core/templates/BUILD.gn b/components/content_creation/notes/core/templates/BUILD.gn
index 9679f04..6ee2083a 100644
--- a/components/content_creation/notes/core/templates/BUILD.gn
+++ b/components/content_creation/notes/core/templates/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//third_party/protobuf/proto_library.gni")
+
 static_library("templates") {
   sources = [
     "note_template.cc",
@@ -15,6 +17,7 @@
   ]
 
   deps = [
+    ":template_storage_proto",
     ":types",
     "//base",
     "//components/content_creation/notes/core:features",
@@ -33,5 +36,13 @@
     "template_types.h",
   ]
 
-  deps = [ "//base" ]
+  deps = [
+    ":template_storage_proto",
+    "//base",
+  ]
+}
+
+proto_library("template_storage_proto") {
+  sources = [ "template_storage.proto" ]
+  generate_python = false
 }
diff --git a/components/content_creation/notes/core/templates/note_template.cc b/components/content_creation/notes/core/templates/note_template.cc
index cb64210..3d5ae543 100644
--- a/components/content_creation/notes/core/templates/note_template.cc
+++ b/components/content_creation/notes/core/templates/note_template.cc
@@ -30,6 +30,18 @@
       text_style_(text_style),
       footer_style_(footer_style) {}
 
+NoteTemplate::NoteTemplate(const proto::NoteTemplate& note_template)
+    : id_(static_cast<NoteTemplateIds>(note_template.id())),
+      // TODO(graysonlafleur): remove localized_name UI element.
+      localized_name_(""),
+      main_background_(Background::Init(note_template.mainbackground())),
+      text_style_(note_template.textstyle()),
+      footer_style_(note_template.footerstyle()) {
+  if (note_template.has_contentbackground()) {
+    content_background_ = Background::Init(note_template.contentbackground());
+  }
+}
+
 NoteTemplate::NoteTemplate(const NoteTemplate& other)
     : id_(other.id()),
       localized_name_(other.localized_name()),
diff --git a/components/content_creation/notes/core/templates/note_template.h b/components/content_creation/notes/core/templates/note_template.h
index 126d2267..026559b 100644
--- a/components/content_creation/notes/core/templates/note_template.h
+++ b/components/content_creation/notes/core/templates/note_template.h
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "components/content_creation/notes/core/templates/template_storage.pb.h"
 #include "components/content_creation/notes/core/templates/template_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -29,6 +30,9 @@
                         const TextStyle& text_style,
                         const FooterStyle& footer_style);
 
+  // Created a NoteTemplate object based on a protobuf NoteTemplate object.
+  explicit NoteTemplate(const proto::NoteTemplate& note_template);
+
   NoteTemplate(const NoteTemplate& other);
 
   ~NoteTemplate();
diff --git a/components/content_creation/notes/core/templates/template_storage.proto b/components/content_creation/notes/core/templates/template_storage.proto
new file mode 100644
index 0000000..9109e17
--- /dev/null
+++ b/components/content_creation/notes/core/templates/template_storage.proto
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto3";
+
+option optimize_for = LITE_RUNTIME;
+
+package content_creation.proto;
+
+// Specifies linear gradient chracteristics such as orientation
+// and color for background.
+message Gradient {
+  int32 orientation = 1;
+  repeated uint32 colors = 2;
+}
+// Specifies background information which can be solid color, a gradient
+// or an image.
+message Background {
+  oneof constructor_options {
+    uint32 color = 1;
+    Gradient gradient = 2;
+    string url = 3;
+  }
+}
+// Specifies text display characteristics.
+message TextStyle {
+  string name = 1;
+  uint32 color = 2;
+  uint32 weight = 3;
+  bool allCaps = 4;
+  int32 alignment = 5;
+  int32 minTextSize = 6;
+  int32 maxTextSize = 7;
+  uint32 highlightColor = 8;
+  int32 highlightStyle = 9;
+}
+// Specifies the appearance of the elements in a note's footer.
+message FooterStyle {
+  uint32 textColor = 1;
+  uint32 logoColor = 2;
+}
+// Specifies day, month, year used for template activation and expiration.
+message Date {
+  int32 month = 1;
+  int32 day = 2;
+  int32 year = 3;
+}
+// Specifies the collection of all templates in order in which they should
+// be displayed.
+message Collection {
+  repeated CollectionItem templates = 1;
+  int32 max_template_number = 2;
+}
+// Specifies activation/expiration and GEO for which the given template should
+// be valid.
+message CollectionItem {
+  NoteTemplate templateId = 1;
+  Date activation = 2;
+  Date expiration = 3;
+  repeated int32 geo = 4;
+}
+// Specifies all visual characteristics for displaying a note.
+message NoteTemplate {
+  // ID used to identify a given template.
+  int32 Id = 1;
+  // Main background for the Note.
+  Background mainBackground = 2;
+  // background for text area. For example, see "Lovely" template
+  Background contentBackground = 3;
+  // Targets the text that was highlighted
+  TextStyle textStyle = 4;
+  // Used to control the styling of elements in the note’s footer (domain, page
+  // title, Chrome logo).
+  FooterStyle footerStyle = 5;
+}
diff --git a/components/content_creation/notes/core/templates/template_store.cc b/components/content_creation/notes/core/templates/template_store.cc
index 3efc755..a9f896b 100644
--- a/components/content_creation/notes/core/templates/template_store.cc
+++ b/components/content_creation/notes/core/templates/template_store.cc
@@ -11,6 +11,7 @@
 #include "base/task/task_runner_util.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/time/time.h"
 #include "components/content_creation/notes/core/note_features.h"
 #include "components/content_creation/notes/core/note_prefs.h"
 #include "components/content_creation/notes/core/templates/note_template.h"
@@ -22,6 +23,25 @@
 
 namespace content_creation {
 
+namespace {
+
+bool ConvertProtoDateToTime(proto::Date date, base::Time& time_date) {
+  base::Time::Exploded exploded_date = {
+      /*year=*/date.year(),
+      /*month=*/date.month(),
+      /*day_of_week=*/0,
+      /*day_of_month=*/date.day(),
+      /*hour=*/0,
+      /*minute=*/0,
+      /*second=*/0,
+      /*millisecond=*/0,
+  };
+
+  return base::Time::FromLocalExploded(exploded_date, &time_date);
+}
+
+}  // namespace
+
 TemplateStore::TemplateStore(
     PrefService* pref_service,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader)
@@ -67,10 +87,56 @@
   return templates;
 }
 
+bool TemplateStore::TemplateAvailable(proto::CollectionItem current_template,
+                                      base::Time today) {
+  base::Time activation;
+  base::Time expiration;
+
+  if (current_template.has_activation() &&
+      (!ConvertProtoDateToTime(current_template.activation(), activation) ||
+       today < activation)) {
+    return false;
+  }
+
+  if (current_template.has_expiration() &&
+      (!ConvertProtoDateToTime(current_template.expiration(), expiration) ||
+       today >= expiration)) {
+    return false;
+  }
+
+  return true;
+}
+
 std::vector<NoteTemplate> TemplateStore::ParseTemplatesFromString(
     std::string response_body) {
-  // TODO(graysonlafleur): implement dynamic templates here
-  return BuildDefaultTemplates();
+  std::vector<NoteTemplate> templates = {};
+  proto::Collection collection;
+
+  if (!collection.ParseFromString(response_body)) {
+    return BuildDefaultTemplates();
+  }
+
+  int numTemplates = 0;
+  base::Time today = base::Time::NowFromSystemTime();
+
+  for (int i = 0; i < collection.templates_size() &&
+                  numTemplates < collection.max_template_number();
+       i++) {
+    proto::CollectionItem current_template = collection.templates(i);
+
+    if (!TemplateAvailable(current_template, today)) {
+      continue;
+    }
+
+    templates.push_back(NoteTemplate(current_template.templateid()));
+    numTemplates++;
+  }
+
+  if (templates.size() == 0) {
+    return BuildDefaultTemplates();
+  }
+
+  return templates;
 }
 
 void TemplateStore::OnTemplatesReceived(
diff --git a/components/content_creation/notes/core/templates/template_store.h b/components/content_creation/notes/core/templates/template_store.h
index 157d058..2640c0c 100644
--- a/components/content_creation/notes/core/templates/template_store.h
+++ b/components/content_creation/notes/core/templates/template_store.h
@@ -12,10 +12,15 @@
 #include "base/memory/weak_ptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "components/content_creation/notes/core/templates/template_fetcher.h"
+#include "components/content_creation/notes/core/templates/template_storage.pb.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 class PrefService;
 
+namespace base {
+class Time;
+}
+
 namespace content_creation {
 
 class NoteTemplate;
@@ -36,6 +41,11 @@
   TemplateStore(const TemplateStore&) = delete;
   TemplateStore& operator=(const TemplateStore&) = delete;
 
+  // Checks whether given template should be available based on activation and
+  // expiration dates.
+  bool TemplateAvailable(proto::CollectionItem current_template,
+                         base::Time today);
+
   // Calls Start() in TemplateFetcher to do a GET request and send the
   // data from the URL to OnFetchTemplateComplete.
   void FetchTemplates(GetTemplatesCallback callback);
diff --git a/components/content_creation/notes/core/templates/template_store_unittest.cc b/components/content_creation/notes/core/templates/template_store_unittest.cc
index 8c7ee0b..fc08dd6 100644
--- a/components/content_creation/notes/core/templates/template_store_unittest.cc
+++ b/components/content_creation/notes/core/templates/template_store_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/task_environment.h"
 #include "components/content_creation/notes/core/note_features.h"
 #include "components/content_creation/notes/core/templates/note_template.h"
+#include "components/content_creation/notes/core/templates/template_storage.pb.h"
 #include "components/content_creation/notes/core/templates/template_types.h"
 #include "components/prefs/testing_pref_service.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -25,6 +26,17 @@
   void SetUp() override {
     template_store_ = std::make_unique<TemplateStore>(
         &testing_pref_service_, test_url_loader_factory());
+
+    // This sets it to Linux Epoch due since it is a test.
+    jan_01_1970_ = base::Time::NowFromSystemTime();
+
+    jan_10_1960_.set_day(10);
+    jan_10_1960_.set_month(1);
+    jan_10_1960_.set_year(1960);
+
+    jan_10_2001_.set_day(10);
+    jan_10_2001_.set_month(1);
+    jan_10_2001_.set_year(2001);
   }
 
  protected:
@@ -37,7 +49,6 @@
     for (const NoteTemplate& note_template : note_templates) {
       EXPECT_LT(NoteTemplateIds::kUnknown, note_template.id());
       EXPECT_GE(NoteTemplateIds::kMaxValue, note_template.id());
-      EXPECT_FALSE(note_template.localized_name().empty());
       EXPECT_FALSE(note_template.text_style().font_name().empty());
 
       // There should be no duplicated IDs.
@@ -46,6 +57,171 @@
     }
   }
 
+  proto::NoteTemplate GetClassicTemplate() {
+    // Background
+    proto::Background background;
+
+    background.set_color(0xFF202124);
+
+    /*===========================*/
+
+    // TextStyle
+    proto::TextStyle text_style;
+
+    text_style.set_name("Source Serif Pro");
+    text_style.set_color(0xFFFFFFFF);
+    text_style.set_weight(700);
+    text_style.set_allcaps(false);
+    text_style.set_alignment(1);
+    text_style.set_mintextsize(14);
+    text_style.set_maxtextsize(48);
+
+    /*===========================*/
+
+    // FooterStyle
+    proto::FooterStyle footer_style;
+
+    footer_style.set_textcolor(0xB3FFFFFF);
+    footer_style.set_logocolor(0x33FFFFFF);
+
+    /*===========================*/
+
+    // NoteTemplate
+    proto::NoteTemplate classic;
+
+    classic.set_id(1);
+    classic.set_allocated_mainbackground(new proto::Background(background));
+    classic.set_allocated_textstyle(new proto::TextStyle(text_style));
+    classic.set_allocated_footerstyle(new proto::FooterStyle(footer_style));
+
+    return classic;
+  }
+
+  proto::NoteTemplate GetFriendlyTemplate() {
+    proto::Background background;
+
+    background.set_url(
+        "https://www.gstatic.com/chrome/content/"
+        "templates/FriendlyBackground@2x.png");
+
+    /*===========================*/
+
+    proto::TextStyle text_style;
+
+    text_style.set_name("Rock Salt");
+    text_style.set_color(0xFF202124);
+    text_style.set_weight(400);
+    text_style.set_allcaps(false);
+    text_style.set_alignment(1);
+    text_style.set_mintextsize(14);
+    text_style.set_maxtextsize(48);
+
+    /*===========================*/
+
+    proto::FooterStyle footer_style;
+
+    footer_style.set_textcolor(0xCC000000);
+    footer_style.set_logocolor(0x1A000000);
+
+    /*===========================*/
+
+    proto::NoteTemplate friendly;
+
+    friendly.set_id(2);
+    friendly.set_allocated_mainbackground(new proto::Background(background));
+    friendly.set_allocated_textstyle(new proto::TextStyle(text_style));
+    friendly.set_allocated_footerstyle(new proto::FooterStyle(footer_style));
+
+    return friendly;
+  }
+
+  proto::NoteTemplate GetLovelyTemplate() {
+    proto::Background background;
+    proto::Gradient gradient;
+
+    gradient.set_orientation(2);
+    gradient.add_colors(0xFFCEF9FF);
+    gradient.add_colors(0xFFF1DFFF);
+
+    background.set_allocated_gradient(new proto::Gradient(gradient));
+
+    proto::Background content_background;
+
+    content_background.set_color(0xFFFFFFFF);
+
+    /*===========================*/
+
+    proto::TextStyle text_style;
+
+    text_style.set_name("Source Serif Pro");
+    text_style.set_color(0xFF000000);
+    text_style.set_weight(400);
+    text_style.set_allcaps(false);
+    text_style.set_alignment(2);
+    text_style.set_mintextsize(14);
+    text_style.set_maxtextsize(48);
+
+    /*===========================*/
+
+    proto::FooterStyle footer_style;
+
+    footer_style.set_textcolor(0xCC000000);
+    footer_style.set_logocolor(0x1A000000);
+
+    /*===========================*/
+
+    proto::NoteTemplate lovely;
+
+    lovely.set_id(6);
+    lovely.set_allocated_mainbackground(new proto::Background(background));
+    lovely.set_allocated_contentbackground(
+        new proto::Background(content_background));
+    lovely.set_allocated_textstyle(new proto::TextStyle(text_style));
+    lovely.set_allocated_footerstyle(new proto::FooterStyle(footer_style));
+
+    return lovely;
+  }
+
+  std::string GetOneMaxTemplatesValidProtoString() {
+    std::string data;
+
+    proto::Collection collection;
+    collection.set_max_template_number(1);
+
+    proto::NoteTemplate classic = GetClassicTemplate();
+    proto::NoteTemplate friendly = GetFriendlyTemplate();
+
+    proto::CollectionItem* classic_template = collection.add_templates();
+    classic_template->set_allocated_templateid(
+        new proto::NoteTemplate(classic));
+    proto::CollectionItem* friendly_template = collection.add_templates();
+    friendly_template->set_allocated_templateid(
+        new proto::NoteTemplate(friendly));
+
+    collection.SerializeToString(&data);
+    return data;
+  }
+
+  std::string GetInvalidProtoString() {
+    std::string data;
+
+    proto::Collection collection;
+    // collection.set_max_template_number() is missing which is required.
+
+    proto::NoteTemplate classic = GetClassicTemplate();
+    proto::NoteTemplate friendly = GetFriendlyTemplate();
+
+    proto::CollectionItem* classic_template = collection.add_templates();
+    classic_template->set_allocated_templateid(
+        new proto::NoteTemplate(classic));
+    proto::CollectionItem* friendly_template = collection.add_templates();
+    friendly_template->set_allocated_templateid(
+        new proto::NoteTemplate(friendly));
+
+    collection.SerializeToString(&data);
+    return data;
+  }
+
   // Have to use TaskEnvironment since the TemplateStore posts tasks to the
   // thread pool.
   base::test::TaskEnvironment task_environment_{
@@ -55,11 +231,17 @@
   TestingPrefServiceSimple testing_pref_service_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   std::unique_ptr<TemplateStore> template_store_;
+
+  base::Time jan_01_1970_;
+  proto::Date jan_10_1960_;
+  proto::Date jan_10_2001_;
+  proto::Date invalid_date_;
+  proto::Collection collection_;
 };
 
 // Tests that the store does return templates, and also validates the
 // templates' information.
-TEST_F(TemplateStoreTest, GetTemplatesSuccessWithDefaultTemplates) {
+TEST_F(TemplateStoreTest, DefaultTemplates) {
   scoped_feature_list_.InitWithFeatures({kWebNotesStylizeEnabled},
                                         {kWebNotesDynamicTemplates});
   base::RunLoop run_loop;
@@ -76,7 +258,154 @@
   run_loop.Run();
 }
 
-TEST_F(TemplateStoreTest, GetTemplatesSuccessWithDynamicTemplates) {
+// Test that templates without any dates will still be shown to the user.
+TEST_F(TemplateStoreTest, NoDates) {
+  proto::CollectionItem* no_dates = collection_.add_templates();
+  no_dates->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  EXPECT_TRUE(template_store_->TemplateAvailable(*no_dates, jan_01_1970_));
+}
+
+// Tests that templates with expiration dates that have not expired yet will be
+// shown to the user.
+TEST_F(TemplateStoreTest, ActiveExpiration) {
+  proto::CollectionItem* active_expiration = collection_.add_templates();
+  active_expiration->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  active_expiration->set_allocated_expiration(new proto::Date(jan_10_2001_));
+  EXPECT_TRUE(
+      template_store_->TemplateAvailable(*active_expiration, jan_01_1970_));
+}
+
+// Tests that templates with expired expiration dates will not be shown to the
+// user.
+TEST_F(TemplateStoreTest, InactiveExpiration) {
+  proto::CollectionItem* inactive_expiration = collection_.add_templates();
+  inactive_expiration->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  inactive_expiration->set_allocated_expiration(new proto::Date(jan_10_1960_));
+  EXPECT_FALSE(
+      template_store_->TemplateAvailable(*inactive_expiration, jan_01_1970_));
+}
+
+// Tests that templates with proto::Date objects that are missing fields will
+// not be shown to the user.
+TEST_F(TemplateStoreTest, InvalidDates) {
+  proto::CollectionItem* invalid_expiration = collection_.add_templates();
+  invalid_expiration->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  invalid_expiration->set_allocated_expiration(new proto::Date(invalid_date_));
+  EXPECT_FALSE(
+      template_store_->TemplateAvailable(*invalid_expiration, jan_01_1970_));
+
+  proto::CollectionItem* invalid_activation = collection_.add_templates();
+  invalid_activation->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  invalid_activation->set_allocated_activation(new proto::Date(invalid_date_));
+  EXPECT_FALSE(
+      template_store_->TemplateAvailable(*invalid_activation, jan_01_1970_));
+}
+
+// Tests that templates with activation dates that have passed will be shown to
+// the user.
+TEST_F(TemplateStoreTest, ActiveActivation) {
+  proto::CollectionItem* active_activation = collection_.add_templates();
+  active_activation->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  active_activation->set_allocated_activation(new proto::Date(jan_10_1960_));
+  EXPECT_TRUE(
+      template_store_->TemplateAvailable(*active_activation, jan_01_1970_));
+}
+
+// Tests that templates with activation dates that have yet to pass will not be
+// shown to the user.
+TEST_F(TemplateStoreTest, InactiveActivation) {
+  proto::CollectionItem* inactive_activation = collection_.add_templates();
+  inactive_activation->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  inactive_activation->set_allocated_activation(new proto::Date(jan_10_2001_));
+  EXPECT_FALSE(
+      template_store_->TemplateAvailable(*inactive_activation, jan_01_1970_));
+}
+
+// Tests that if today's date is within activation and expiration date, it will
+// be passed to the user.
+TEST_F(TemplateStoreTest, BothDatesActive) {
+  proto::CollectionItem* both_dates_active = collection_.add_templates();
+  both_dates_active->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  both_dates_active->set_allocated_activation(new proto::Date(jan_10_1960_));
+  both_dates_active->set_allocated_expiration(new proto::Date(jan_10_2001_));
+  EXPECT_TRUE(
+      template_store_->TemplateAvailable(*both_dates_active, jan_01_1970_));
+}
+
+// Tests that if the activation and expiration dates are in the wrong order
+// (activation comes after the expiration here), it will not be shown to the
+// user.
+TEST_F(TemplateStoreTest, BothDatesInactive) {
+  proto::CollectionItem* both_dates_inactive = collection_.add_templates();
+  both_dates_inactive->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  both_dates_inactive->set_allocated_activation(new proto::Date(jan_10_2001_));
+  both_dates_inactive->set_allocated_expiration(new proto::Date(jan_10_1960_));
+  EXPECT_FALSE(
+      template_store_->TemplateAvailable(*both_dates_inactive, jan_01_1970_));
+}
+
+// Tests that if a date is impossible, such as the 40th day, 13th month, or
+// 1000000th year (base::Time::exploded requires year be 4 digits) it will not
+// be shown to the user.
+TEST_F(TemplateStoreTest, ImpossibleDates) {
+  proto::CollectionItem* invalid_expiration = collection_.add_templates();
+  invalid_expiration->set_allocated_templateid(
+      new proto::NoteTemplate(GetFriendlyTemplate()));
+
+  invalid_date_.set_day(40);
+  invalid_date_.set_month(13);
+  invalid_date_.set_year(1000000);
+
+  invalid_expiration->set_allocated_expiration(new proto::Date(invalid_date_));
+  EXPECT_FALSE(
+      template_store_->TemplateAvailable(*invalid_expiration, jan_01_1970_));
+}
+
+// Tests that it will stop at the set maximum number of templates even if there
+// are more templates that are available.
+TEST_F(TemplateStoreTest, OneMaxTemplates) {
+  test_url_loader_factory_.AddResponse(
+      kTemplateUrl, GetOneMaxTemplatesValidProtoString(), net::HTTP_OK);
+  scoped_feature_list_.InitWithFeatures(
+      {kWebNotesStylizeEnabled, kWebNotesDynamicTemplates}, {});
+  base::RunLoop run_loop;
+
+  template_store_->GetTemplates(base::BindLambdaForTesting(
+      [&run_loop, this](std::vector<NoteTemplate> templates) {
+        EXPECT_EQ(1U, templates.size());
+        // Tests to make sure it got the first template and not the second one.
+        EXPECT_EQ(static_cast<int>(templates.at(0).id()), 1);
+
+        ValidateTemplates(templates);
+
+        run_loop.Quit();
+      }));
+
+  run_loop.Run();
+}
+
+// Tests that it will give the user default templates if it is given invalid
+// Protobuf data.
+TEST_F(TemplateStoreTest, EmptyString) {
+  test_url_loader_factory_.AddResponse(kTemplateUrl, "", net::HTTP_OK);
   scoped_feature_list_.InitWithFeatures(
       {kWebNotesStylizeEnabled, kWebNotesDynamicTemplates}, {});
   base::RunLoop run_loop;
diff --git a/components/content_creation/notes/core/templates/template_types.cc b/components/content_creation/notes/core/templates/template_types.cc
index a6f3d79..3813250a 100644
--- a/components/content_creation/notes/core/templates/template_types.cc
+++ b/components/content_creation/notes/core/templates/template_types.cc
@@ -38,6 +38,22 @@
   DCHECK(image_url.size() > 0);
 }
 
+Background Background::Init(const proto::Background& background) {
+  if (background.color() != 0) {
+    return Background(static_cast<ARGBColor>(background.color()));
+  }
+
+  if (!background.url().empty()) {
+    return Background(background.url());
+  }
+
+  return Background(
+      std::vector<ARGBColor>(background.gradient().colors().begin(),
+                             background.gradient().colors().end()),
+      static_cast<LinearGradientDirection>(
+          background.gradient().orientation()));
+}
+
 Background::Background(const Background& other) {
   color_ = other.color();
   colors_ = other.colors();
@@ -85,6 +101,20 @@
       highlight_color_(highlight_color),
       highlight_style_(highlight_style) {}
 
+TextStyle::TextStyle(const proto::TextStyle& textstyle)
+    : TextStyle(textstyle.name(),
+                textstyle.color(),
+                textstyle.weight(),
+                textstyle.allcaps(),
+                static_cast<TextAlignment>(textstyle.alignment()),
+                textstyle.mintextsize(),
+                textstyle.maxtextsize()) {
+  if (textstyle.highlightcolor() != 0) {
+    highlight_color_ = textstyle.highlightcolor();
+    highlight_style_ = static_cast<HighlightStyle>(textstyle.highlightstyle());
+  }
+}
+
 TextStyle::TextStyle(const TextStyle& text_style) = default;
 
 TextStyle& TextStyle::operator=(const TextStyle& text_style) = default;
@@ -92,4 +122,7 @@
 FooterStyle::FooterStyle(ARGBColor text_color, ARGBColor logo_color)
     : text_color_(text_color), logo_color_(logo_color) {}
 
+FooterStyle::FooterStyle(const proto::FooterStyle& footerstyle)
+    : FooterStyle(footerstyle.textcolor(), footerstyle.logocolor()) {}
+
 }  // namespace content_creation
diff --git a/components/content_creation/notes/core/templates/template_types.h b/components/content_creation/notes/core/templates/template_types.h
index f62b6bf..2c4bcb8 100644
--- a/components/content_creation/notes/core/templates/template_types.h
+++ b/components/content_creation/notes/core/templates/template_types.h
@@ -7,6 +7,8 @@
 #include <string>
 #include <vector>
 
+#include "components/content_creation/notes/core/templates/template_storage.pb.h"
+
 namespace content_creation {
 
 using ARGBColor = uint32_t;
@@ -52,6 +54,10 @@
   // Creates an image background based on a remotely hosted image's URL.
   explicit Background(const std::string& image_url);
 
+  // Creates a Background from a protobuf Background object while still
+  // ensuring the DChecks().
+  static Background Init(const proto::Background& background);
+
   Background(const Background& other);
 
   ~Background();
@@ -107,6 +113,9 @@
                      ARGBColor highlight_color,
                      HighlightStyle highlight_style);
 
+  // Creates a TextStyle from a protobuf TextStyle object.
+  explicit TextStyle(const proto::TextStyle& textstyle);
+
   TextStyle(const TextStyle& text_style);
   TextStyle& operator=(const TextStyle& text_style);
 
@@ -137,6 +146,9 @@
  public:
   explicit FooterStyle(ARGBColor text_color, ARGBColor logo_color);
 
+  // Creates a FooterStyle from a protobuf FooterStyle object.
+  explicit FooterStyle(const proto::FooterStyle& footerstyle);
+
   ARGBColor text_color() const { return text_color_; }
   ARGBColor logo_color() const { return logo_color_; }
 
diff --git a/components/custom_handlers/protocol_handler.cc b/components/custom_handlers/protocol_handler.cc
index 625f4529..aedebf6 100644
--- a/components/custom_handlers/protocol_handler.cc
+++ b/components/custom_handlers/protocol_handler.cc
@@ -73,24 +73,11 @@
 }
 
 bool ProtocolHandler::IsValid() const {
-  // This matches VerifyCustomHandlerURLSecurity() in blink's
-  // NavigatorContentUtils.
-  // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  bool has_valid_scheme =
-      url_.SchemeIsHTTPOrHTTPS() ||
-      (security_level_ ==
-           blink::ProtocolHandlerSecurityLevel::kExtensionFeatures &&
-       blink::CommonSchemeRegistry::IsExtensionScheme(url_.scheme()));
-  if (!has_valid_scheme || !network::IsUrlPotentiallyTrustworthy(url_))
+  if (!blink::IsAllowedCustomHandlerURL(url_, security_level_))
     return false;
 
-  bool has_custom_scheme_prefix = false;
-  bool allow_ext_plus_prefix =
-      (security_level_ >=
-       blink::ProtocolHandlerSecurityLevel::kExtensionFeatures);
-  return blink::IsValidCustomHandlerScheme(protocol_, allow_ext_plus_prefix,
-                                           has_custom_scheme_prefix);
+  return blink::IsValidCustomHandlerScheme(protocol_, security_level_);
 }
 
 bool ProtocolHandler::IsSameOrigin(const ProtocolHandler& handler) const {
diff --git a/components/custom_handlers/protocol_handler.h b/components/custom_handlers/protocol_handler.h
index 3df097d..31895949 100644
--- a/components/custom_handlers/protocol_handler.h
+++ b/components/custom_handlers/protocol_handler.h
@@ -56,6 +56,9 @@
   static bool IsValidDict(const base::Value::Dict& value);
 
   // Return true if the protocol handler meets security constraints.
+  // Verify custom handler URLs security and syntax as well as the schemes
+  // safelist as described in steps 1, 2, 6 and 7 (except same origin).
+  // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers.
   bool IsValid() const;
 
   // Returns true if this handler's url has the same origin as the given one.
diff --git a/components/download/internal/background_service/controller_impl.cc b/components/download/internal/background_service/controller_impl.cc
index 8b99916..4a655b85 100644
--- a/components/download/internal/background_service/controller_impl.cc
+++ b/components/download/internal/background_service/controller_impl.cc
@@ -1201,6 +1201,7 @@
                                    entry->url_chain, entry->response_headers);
     completion_info.blob_handle = driver_entry->blob_handle;
     completion_info.hash256 = driver_entry->hash256;
+    completion_info.custom_data = entry->custom_data;
 
     entry->last_cleanup_check_time = driver_entry->completion_time;
     base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -1213,6 +1214,7 @@
     CompletionInfo completion_info;
     completion_info.url_chain = entry->url_chain;
     completion_info.response_headers = entry->response_headers;
+    completion_info.custom_data = entry->custom_data;
 
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
diff --git a/components/download/internal/background_service/controller_impl_unittest.cc b/components/download/internal/background_service/controller_impl_unittest.cc
index 40a2e41..b8d27375 100644
--- a/components/download/internal/background_service/controller_impl_unittest.cc
+++ b/components/download/internal/background_service/controller_impl_unittest.cc
@@ -51,6 +51,8 @@
 
 const base::FilePath::CharType kDownloadDirPath[] =
     FILE_PATH_LITERAL("/test/downloads");
+constexpr char kKey[] = "k";
+constexpr char kValue[] = "v";
 
 bool GuidInEntryList(const std::vector<Entry>& entries,
                      const std::string& guid) {
@@ -793,16 +795,18 @@
 TEST_F(DownloadServiceControllerImplTest, OnDownloadFailed) {
   // Setup download service test data.
   Entry entry = test::BuildBasicEntry(Entry::State::ACTIVE);
+  entry.custom_data = {{kKey, kValue}};
   std::vector<Entry> entries = {entry};
 
   // Setup download driver test data.
   DriverEntry dentry = BuildDriverEntry(entry, DriverEntry::State::IN_PROGRESS);
   driver_->AddTestData(std::vector<DriverEntry>{dentry});
 
+  CompletionInfo completion_info;
   EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
   EXPECT_CALL(*client_,
               OnDownloadFailed(entry.guid, _, Client::FailureReason::NETWORK))
-      .Times(1);
+      .WillOnce(SaveArg<1>(&completion_info));
 
   device_status_listener_->SetDeviceStatus(
       DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
@@ -815,6 +819,9 @@
   EXPECT_EQ(nullptr, model_->Get(entry.guid));
 
   task_runner_->RunUntilIdle();
+
+  EXPECT_EQ(1u, completion_info.custom_data.size());
+  EXPECT_EQ(kValue, completion_info.custom_data[kKey]);
 }
 
 TEST_F(DownloadServiceControllerImplTest, OnDownloadFailedFromDriverCancel) {
@@ -978,6 +985,7 @@
 TEST_F(DownloadServiceControllerImplTest, OnDownloadSucceeded) {
   // Setup download service test data.
   Entry entry = test::BuildBasicEntry(Entry::State::ACTIVE);
+  entry.custom_data[kKey] = kValue;
   std::vector<Entry> entries = {entry};
 
   // Setup download driver test data.
@@ -988,9 +996,9 @@
                                  dentry.bytes_downloaded, entry.url_chain,
                                  entry.response_headers);
   completion_info.hash256 = "01234567ABCDEF";
+  completion_info.custom_data[kKey] = kValue;
   EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
-  EXPECT_CALL(*client_, OnDownloadSucceeded(entry.guid, completion_info))
-      .Times(1);
+  EXPECT_CALL(*client_, OnDownloadSucceeded(entry.guid, completion_info));
 
   device_status_listener_->SetDeviceStatus(
       DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
diff --git a/components/download/internal/background_service/entry.cc b/components/download/internal/background_service/entry.cc
index f893f9d..1028938 100644
--- a/components/download/internal/background_service/entry.cc
+++ b/components/download/internal/background_service/entry.cc
@@ -35,6 +35,7 @@
       create_time(base::Time::Now()),
       scheduling_params(params.scheduling_params),
       request_params(params.request_params),
+      custom_data(params.custom_data),
       bytes_downloaded(0u),
       bytes_uploaded(0u),
       attempt_count(0),
@@ -72,7 +73,8 @@
          AreHeadersEqual(response_headers.get(),
                          other.response_headers.get()) &&
          did_received_response == other.did_received_response &&
-         require_response_headers == other.require_response_headers;
+         require_response_headers == other.require_response_headers &&
+         custom_data == other.custom_data;
 }
 
 size_t Entry::EstimateMemoryUsage() const {
@@ -82,7 +84,8 @@
          base::trace_event::EstimateMemoryUsage(request_params.method) +
          base::trace_event::EstimateMemoryUsage(
              request_params.request_headers.ToString()) +
-         base::trace_event::EstimateMemoryUsage(target_file_path.value());
+         base::trace_event::EstimateMemoryUsage(target_file_path.value()) +
+         base::trace_event::EstimateMemoryUsage(custom_data);
 }
 
 }  // namespace download
diff --git a/components/download/internal/background_service/entry.h b/components/download/internal/background_service/entry.h
index 3b37f0b..13b3945 100644
--- a/components/download/internal/background_service/entry.h
+++ b/components/download/internal/background_service/entry.h
@@ -73,6 +73,10 @@
   // The parameters that define the actual download request to make.
   RequestParams request_params;
 
+  // Custom key value pair provided by client and will sent back to client. See
+  // |custom_data| in DownloadParams for more details.
+  DownloadParams::CustomData custom_data;
+
   // The state of the download to help the scheduler and loggers make the right
   // decisions about the download object.
   State state = State::NEW;
diff --git a/components/download/internal/background_service/entry_utils.cc b/components/download/internal/background_service/entry_utils.cc
index 7bb6cbc3..9e8466d 100644
--- a/components/download/internal/background_service/entry_utils.cc
+++ b/components/download/internal/background_service/entry_utils.cc
@@ -97,6 +97,8 @@
     meta_data.completion_info =
         CompletionInfo(entry->target_file_path, entry->bytes_downloaded,
                        entry->url_chain, entry->response_headers);
+    meta_data.completion_info->custom_data = entry->custom_data;
+
     // If the download is completed, the |current_size| needs to pull from entry
     // since the history db record has been deleted.
     meta_data.current_size = entry->bytes_downloaded;
diff --git a/components/download/internal/background_service/entry_utils_unittest.cc b/components/download/internal/background_service/entry_utils_unittest.cc
index 75f36af..a022250 100644
--- a/components/download/internal/background_service/entry_utils_unittest.cc
+++ b/components/download/internal/background_service/entry_utils_unittest.cc
@@ -14,6 +14,11 @@
 
 namespace download {
 
+namespace {
+constexpr char kKey[] = "k";
+constexpr char kValue[] = "v";
+}  // namespace
+
 TEST(DownloadServiceEntryUtilsTest, TestGetNumberOfLiveEntriesForClient) {
   Entry entry1 = test::BuildBasicEntry();
   Entry entry2 = test::BuildBasicEntry();
@@ -155,10 +160,13 @@
   entry = test::BuildBasicEntry(Entry::State::COMPLETE);
   entry.target_file_path = base::FilePath::FromUTF8Unsafe("123");
   entry.bytes_downloaded = 100u;
+  entry.custom_data = {{kKey, kValue}};
   meta_data = util::BuildDownloadMetaData(&entry, &driver);
   EXPECT_EQ(entry.guid, meta_data.guid);
   EXPECT_TRUE(meta_data.completion_info.has_value());
   EXPECT_EQ(entry.target_file_path, meta_data.completion_info->path);
+  EXPECT_EQ(1u, meta_data.completion_info->custom_data.size());
+  EXPECT_EQ(kValue, meta_data.completion_info->custom_data[kKey]);
   EXPECT_EQ(entry.bytes_downloaded,
             meta_data.completion_info->bytes_downloaded);
 }
diff --git a/components/download/internal/background_service/proto/entry.proto b/components/download/internal/background_service/proto/entry.proto
index f699afc..e918538 100644
--- a/components/download/internal/background_service/proto/entry.proto
+++ b/components/download/internal/background_service/proto/entry.proto
@@ -29,8 +29,17 @@
   BOUNDARY = 10;
 }
 
+// Custom key value pair provided by the client and will be sent back to client
+// when download is completed.
+// Next tag: 3
+message CustomData {
+  optional string key = 1;
+  optional string value = 2;
+}
+
 // Stores the request params, internal state, metrics and metadata associated
 // with a download request.
+// Next tag: 21
 message Entry {
   // This should stay in sync with the State enum
   // (components/download/internal/background_service/entry.h).
@@ -76,4 +85,5 @@
   optional string response_headers = 17;
   optional bool did_received_response = 18;
   optional bool require_response_headers = 19;
+  repeated CustomData custom_data = 20;
 }
diff --git a/components/download/internal/background_service/proto_conversions.cc b/components/download/internal/background_service/proto_conversions.cc
index 1768714..6070dae 100644
--- a/components/download/internal/background_service/proto_conversions.cc
+++ b/components/download/internal/background_service/proto_conversions.cc
@@ -319,6 +319,10 @@
   entry.did_received_response = proto.did_received_response();
   entry.require_response_headers = proto.require_response_headers();
 
+  for (const auto& kv : proto.custom_data()) {
+    entry.custom_data[kv.key()] = kv.value();
+  }
+
   return entry;
 }
 
@@ -347,6 +351,11 @@
     proto.set_response_headers(entry.response_headers->raw_headers());
   proto.set_did_received_response(entry.did_received_response);
   proto.set_require_response_headers(entry.require_response_headers);
+  for (const auto& kv : entry.custom_data) {
+    auto* custom_data_proto = proto.add_custom_data();
+    custom_data_proto->set_key(kv.first);
+    custom_data_proto->set_value(kv.second);
+  }
 
   return proto;
 }
diff --git a/components/download/internal/background_service/proto_conversions_unittest.cc b/components/download/internal/background_service/proto_conversions_unittest.cc
index ef4b561b..bb37472b 100644
--- a/components/download/internal/background_service/proto_conversions_unittest.cc
+++ b/components/download/internal/background_service/proto_conversions_unittest.cc
@@ -13,8 +13,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
-std::string TEST_URL = "https://google.com";
-
+constexpr char kTestUrl[] = "https://www.example.com";
+constexpr char kKey[] = "k";
+constexpr char kValue[] = "v";
 }  // namespace
 
 namespace download {
@@ -96,7 +97,7 @@
 
 TEST_F(ProtoConversionsTest, RequestParamsWithHeadersConversion) {
   RequestParams expected;
-  expected.url = GURL(TEST_URL);
+  expected.url = GURL(kTestUrl);
   expected.method = "GET";
   expected.fetch_error_body = true;
   expected.require_safety_checks = false;
@@ -125,7 +126,7 @@
 
 TEST_F(ProtoConversionsTest, RequestParamsWithMissingCredentialsMode) {
   RequestParams expected;
-  expected.url = GURL(TEST_URL);
+  expected.url = GURL(kTestUrl);
   expected.method = "GET";
 
   protodb::RequestParams proto;
@@ -145,9 +146,10 @@
       DownloadClient::TEST, base::GenerateGUID(), base::Time::Now(),
       SchedulingParams::NetworkRequirements::OPTIMISTIC,
       SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE,
-      SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET",
+      SchedulingParams::Priority::HIGH, GURL(kTestUrl), "GET",
       Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")),
       base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 3, 8);
+  expected.custom_data = {{kKey, kValue}};
   actual = EntryFromProto(EntryToProto(expected));
   EXPECT_TRUE(test::CompareEntry(&expected, &actual));
 }
@@ -162,7 +164,7 @@
       DownloadClient::TEST, base::GenerateGUID(), base::Time::Now(),
       SchedulingParams::NetworkRequirements::OPTIMISTIC,
       SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE,
-      SchedulingParams::Priority::HIGH, GURL(TEST_URL), "GET",
+      SchedulingParams::Priority::HIGH, GURL(kTestUrl), "GET",
       Entry::State::ACTIVE, base::FilePath(FILE_PATH_LITERAL("/test/xyz")),
       base::Time::Now(), base::Time::Now(), base::Time::Now(), 1024u, 2, 8));
 
diff --git a/components/download/internal/common/download_file_impl.cc b/components/download/internal/common/download_file_impl.cc
index 6dd78c24..fd951fbc 100644
--- a/components/download/internal/common/download_file_impl.cc
+++ b/components/download/internal/common/download_file_impl.cc
@@ -337,6 +337,13 @@
 
 void DownloadFileImpl::RenameAndUniquify(const base::FilePath& full_path,
                                          RenameCompletionCallback callback) {
+#if BUILDFLAG(IS_ANDROID)
+  if (full_path.IsContentUri()) {
+    DownloadInterruptReason reason = file_.Rename(full_path);
+    OnRenameComplete(full_path, std::move(callback), reason);
+    return;
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
   std::unique_ptr<RenameParameters> parameters(
       new RenameParameters(UNIQUIFY, full_path, std::move(callback)));
   RenameWithRetryInternal(std::move(parameters));
@@ -359,39 +366,10 @@
 }
 
 #if BUILDFLAG(IS_ANDROID)
-void DownloadFileImpl::RenameToIntermediateUri(
-    const GURL& original_url,
-    const GURL& referrer_url,
-    const base::FilePath& file_name,
-    const std::string& mime_type,
-    const base::FilePath& current_path,
-    RenameCompletionCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Create new content URI if |current_path| is not content URI
-  // or if it is already deleted.
-  base::FilePath content_path =
-      current_path.IsContentUri() && base::ContentUriExists(current_path)
-          ? current_path
-          : DownloadCollectionBridge::CreateIntermediateUriForPublish(
-                original_url, referrer_url, file_name, mime_type);
-  DownloadInterruptReason reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED;
-  if (!content_path.empty()) {
-    reason = file_.Rename(content_path);
-    display_name_ = DownloadCollectionBridge::GetDisplayName(content_path);
-  }
-  if (display_name_.empty())
-    display_name_ = file_name;
-  OnRenameComplete(content_path, std::move(callback), reason);
-}
-
 void DownloadFileImpl::PublishDownload(RenameCompletionCallback callback) {
   DownloadInterruptReason reason = file_.PublishDownload();
   OnRenameComplete(file_.full_path(), std::move(callback), reason);
 }
-
-base::FilePath DownloadFileImpl::GetDisplayName() {
-  return display_name_;
-}
 #endif  // BUILDFLAG(IS_ANDROID)
 
 base::TimeDelta DownloadFileImpl::GetRetryDelayForFailedRename(
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index e044cb05..1ab2c5c 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -1758,6 +1758,7 @@
     DownloadDangerType danger_type,
     MixedContentStatus mixed_content_status,
     const base::FilePath& intermediate_path,
+    const base::FilePath& display_name,
     absl::optional<DownloadSchedule> download_schedule,
     DownloadInterruptReason interrupt_reason) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -1802,6 +1803,8 @@
   destination_info_.target_disposition = disposition;
   SetDangerType(danger_type);
   mixed_content_status_ = mixed_content_status;
+  if (!display_name.empty())
+    SetDisplayName(display_name);
 
   // This was an interrupted download that was looking for a filename. Resolve
   // early without performing the intermediate rename. If there is a
@@ -1836,31 +1839,15 @@
   //               filename. Unnecessary renames may cause bugs like
   //               http://crbug.com/74187.
   DCHECK(!IsSavePackageDownload());
-  DownloadFile::RenameCompletionCallback callback =
-      base::BindOnce(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
-                     weak_ptr_factory_.GetWeakPtr());
-#if BUILDFLAG(IS_ANDROID)
-  if ((download_type_ == TYPE_ACTIVE_DOWNLOAD && !transient_ &&
-       DownloadCollectionBridge::ShouldPublishDownload(GetTargetFilePath())) ||
-      GetTargetFilePath().IsContentUri()) {
-    GetDownloadTaskRunner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&DownloadFile::RenameToIntermediateUri,
-                       // Safe because we control download file lifetime.
-                       base::Unretained(download_file_.get()), GetOriginalUrl(),
-                       GetReferrerUrl(), GetFileNameToReportUser(),
-                       GetMimeType(), GetTargetFilePath(),
-                       std::move(callback)));
-    return;
-  }
-#endif  // BUILDFLAG(IS_ANDROID)
 
   GetDownloadTaskRunner()->PostTask(
       FROM_HERE,
-      base::BindOnce(&DownloadFile::RenameAndUniquify,
-                     // Safe because we control download file lifetime.
-                     base::Unretained(download_file_.get()), intermediate_path,
-                     std::move(callback)));
+      base::BindOnce(
+          &DownloadFile::RenameAndUniquify,
+          // Safe because we control download file lifetime.
+          base::Unretained(download_file_.get()), intermediate_path,
+          base::BindOnce(&DownloadItemImpl::OnDownloadRenamedToIntermediateName,
+                         weak_ptr_factory_.GetWeakPtr())));
 }
 
 void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
@@ -1874,14 +1861,6 @@
 
   if (DOWNLOAD_INTERRUPT_REASON_NONE == reason) {
     SetFullPath(full_path);
-#if BUILDFLAG(IS_ANDROID)
-    // For content URIs, target file path is the same as the current path.
-    if (full_path.IsContentUri()) {
-      destination_info_.target_path = full_path;
-      if (display_name_.empty())
-        SetDisplayName(download_file_->GetDisplayName());
-    }
-#endif  // BUILDFLAG(IS_ANDROID)
   } else {
     // TODO(asanka): Even though the rename failed, it may still be possible to
     // recover the partial state from the 'before' name.
diff --git a/components/download/internal/common/download_item_impl_delegate.cc b/components/download/internal/common/download_item_impl_delegate.cc
index 1c36aee..3a2f6e4 100644
--- a/components/download/internal/common/download_item_impl_delegate.cc
+++ b/components/download/internal/common/download_item_impl_delegate.cc
@@ -37,7 +37,7 @@
   std::move(callback).Run(
       target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-      DownloadItem::MixedContentStatus::UNKNOWN, target_path,
+      DownloadItem::MixedContentStatus::UNKNOWN, target_path, base::FilePath(),
       absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
 }
 
diff --git a/components/download/internal/common/download_item_impl_unittest.cc b/components/download/internal/common/download_item_impl_unittest.cc
index 0eae1df..0420d31 100644
--- a/components/download/internal/common/download_item_impl_unittest.cc
+++ b/components/download/internal/common/download_item_impl_unittest.cc
@@ -40,10 +40,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_ANDROID)
-#include "components/download/internal/common/android/download_collection_bridge.h"
-#endif  // BUILDFLAG(IS_ANDROID)
-
 using ::testing::_;
 using ::testing::ByMove;
 using ::testing::DoAll;
@@ -376,7 +372,7 @@
     std::move(callback).Run(
         target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, danger_type,
         DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-        download_schedule, DOWNLOAD_INTERRUPT_REASON_NONE);
+        base::FilePath(), download_schedule, DOWNLOAD_INTERRUPT_REASON_NONE);
     task_environment_.RunUntilIdle();
   }
 
@@ -385,28 +381,12 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       const base::FilePath& new_file_path,
       DownloadInterruptReason reason) {
-    bool use_download_collection = false;
-#if BUILDFLAG(IS_ANDROID)
-    if (DownloadCollectionBridge::ShouldPublishDownload(new_file_path)) {
-      use_download_collection = true;
-      EXPECT_CALL(*download_file, RenameToIntermediateUri(_, _, _, _, _, _))
-          .WillOnce(WithArg<5>([task_runner, new_file_path, reason](
-                                   DownloadFile::RenameCompletionCallback cb) {
-            task_runner->PostTask(
-                FROM_HERE,
-                base::BindOnce(std::move(cb), reason, new_file_path));
-          }));
-    }
-#endif
-    if (!use_download_collection) {
-      EXPECT_CALL(*download_file, RenameAndUniquify(_, _))
-          .WillOnce(WithArg<1>([task_runner, new_file_path, reason](
-                                   DownloadFile::RenameCompletionCallback cb) {
-            task_runner->PostTask(
-                FROM_HERE,
-                base::BindOnce(std::move(cb), reason, new_file_path));
-          }));
-    }
+    EXPECT_CALL(*download_file, RenameAndUniquify(_, _))
+        .WillOnce(WithArg<1>([task_runner, new_file_path, reason](
+                                 DownloadFile::RenameCompletionCallback cb) {
+          task_runner->PostTask(
+              FROM_HERE, base::BindOnce(std::move(cb), reason, new_file_path));
+        }));
   }
 
   void DoDestinationComplete(DownloadItemImpl* item,
@@ -653,7 +633,8 @@
       target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   EXPECT_FALSE(observer.CheckAndResetDownloadUpdated());
   task_environment_.RunUntilIdle();
   EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
@@ -942,7 +923,8 @@
         target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
         DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
         DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-        absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+        base::FilePath(), absl::nullopt /*download_schedule*/,
+        DOWNLOAD_INTERRUPT_REASON_NONE);
     task_environment_.RunUntilIdle();
 
     // Use a continuable interrupt.
@@ -1035,7 +1017,7 @@
            DownloadItem::TARGET_DISPOSITION_OVERWRITE,
            DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
            DownloadItem::MixedContentStatus::UNKNOWN,
-           base::FilePath(kDummyIntermediatePath),
+           base::FilePath(kDummyIntermediatePath), base::FilePath(),
            absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
 
@@ -1238,7 +1220,8 @@
       target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
   EXPECT_EQ(FILE_PATH_LITERAL("foo.bar"),
             item->GetFileNameToReportUser().value());
@@ -1291,7 +1274,7 @@
            DownloadItem::TARGET_DISPOSITION_OVERWRITE,
            DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
            DownloadItem::MixedContentStatus::UNKNOWN,
-           base::FilePath(kDummyIntermediatePath),
+           base::FilePath(kDummyIntermediatePath), base::FilePath(),
            absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
 
@@ -1335,7 +1318,8 @@
       .Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
            DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
            DownloadItem::MixedContentStatus::UNKNOWN, target_path,
-           absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+           base::FilePath(), absl::nullopt /*download_schedule*/,
+           DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
 
   // Interrupt reason carried in create info should be recorded.
@@ -1366,7 +1350,8 @@
       final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
   // All the callbacks should have happened by now.
   ::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1416,7 +1401,8 @@
       final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
   // All the callbacks should have happened by now.
   ::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1481,7 +1467,8 @@
       final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
   // All the callbacks should have happened by now.
   ::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1527,7 +1514,8 @@
       final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
   // All the callbacks should have happened by now.
   ::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1568,7 +1556,8 @@
       final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, intermediate_path,
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   task_environment_.RunUntilIdle();
   // All the callbacks should have happened by now.
   ::testing::Mock::VerifyAndClearExpectations(download_file);
@@ -1611,7 +1600,7 @@
                           DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
                           DownloadItem::MixedContentStatus::UNKNOWN,
                           base::FilePath(FILE_PATH_LITERAL("bar")),
-                          absl::nullopt /*download_schedule*/,
+                          base::FilePath(), absl::nullopt /*download_schedule*/,
                           DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
   EXPECT_EQ(DownloadItem::CANCELLED, item->GetState());
 }
@@ -1626,7 +1615,8 @@
       base::FilePath(), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       DownloadItem::MixedContentStatus::UNKNOWN, base::FilePath(),
-      absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_NONE);
   EXPECT_EQ(DownloadItem::CANCELLED, item->GetState());
 }
 
@@ -1637,12 +1627,12 @@
   base::FilePath target_path(FILE_PATH_LITERAL("/foo/bar"));
 
   EXPECT_CALL(*download_file, Cancel());
-  std::move(callback).Run(target_path,
-                          DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-                          DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-                          DownloadItem::MixedContentStatus::UNKNOWN,
-                          target_path, absl::nullopt /*download_schedule*/,
-                          DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE);
+  std::move(callback).Run(
+      target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+      DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadItem::MixedContentStatus::UNKNOWN, target_path, base::FilePath(),
+      absl::nullopt /*download_schedule*/,
+      DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE);
   EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE,
             item->GetLastReason());
@@ -2421,19 +2411,8 @@
 DownloadFile::RenameCompletionCallback GetRenameCompletionCallback(
     MockDownloadFile* download_file) {
   DownloadFile::RenameCompletionCallback intermediate_rename_callback;
-  bool use_download_collection = false;
-#if BUILDFLAG(IS_ANDROID)
-  if (DownloadCollectionBridge::ShouldPublishDownload(
-          base::FilePath(kDummyIntermediatePath))) {
-    use_download_collection = true;
-    EXPECT_CALL(*download_file, RenameToIntermediateUri(_, _, _, _, _, _))
-        .WillOnce(MoveArg<5>(&intermediate_rename_callback));
-  }
-#endif
-  if (!use_download_collection) {
-    EXPECT_CALL(*download_file, RenameAndUniquify(_, _))
-        .WillOnce(MoveArg<1>(&intermediate_rename_callback));
-  }
+  EXPECT_CALL(*download_file, RenameAndUniquify(_, _))
+      .WillOnce(MoveArg<1>(&intermediate_rename_callback));
   return intermediate_rename_callback;
 }
 
@@ -2521,7 +2500,8 @@
       .Run(base::FilePath(), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
            DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
            DownloadItem::MixedContentStatus::UNKNOWN, base::FilePath(),
-           absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
+           base::FilePath(), absl::nullopt /*download_schedule*/,
+           DOWNLOAD_INTERRUPT_REASON_NONE);
   EXPECT_EQ(DownloadItem::CANCELLED, item_->GetState());
   EXPECT_TRUE(canceled());
   task_environment_.RunUntilIdle();
@@ -2570,7 +2550,7 @@
            DownloadItem::TARGET_DISPOSITION_OVERWRITE,
            DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
            DownloadItem::MixedContentStatus::UNKNOWN,
-           base::FilePath(kDummyIntermediatePath),
+           base::FilePath(kDummyIntermediatePath), base::FilePath(),
            absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
 
   task_environment_.RunUntilIdle();
@@ -2635,7 +2615,7 @@
            DownloadItem::TARGET_DISPOSITION_OVERWRITE,
            DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
            DownloadItem::MixedContentStatus::UNKNOWN,
-           base::FilePath(kDummyIntermediatePath),
+           base::FilePath(kDummyIntermediatePath), base::FilePath(),
            absl::nullopt /*download_schedule*/, DOWNLOAD_INTERRUPT_REASON_NONE);
 
   task_environment_.RunUntilIdle();
diff --git a/components/download/internal/common/download_utils.cc b/components/download/internal/common/download_utils.cc
index e5eb9aa..21c34f13 100644
--- a/components/download/internal/common/download_utils.cc
+++ b/components/download/internal/common/download_utils.cc
@@ -131,6 +131,43 @@
   headers->SetHeader(net::HttpRequestHeaders::kRange, range_header);
 }
 
+#if BUILDFLAG(IS_ANDROID)
+struct CreateIntermediateUriResult {
+ public:
+  CreateIntermediateUriResult(const base::FilePath& content_uri,
+                              const base::FilePath& file_name)
+      : content_uri(content_uri), file_name(file_name) {}
+
+  base::FilePath content_uri;
+  base::FilePath file_name;
+};
+
+CreateIntermediateUriResult CreateIntermediateUri(
+    const GURL& original_url,
+    const GURL& referrer_url,
+    const base::FilePath& current_path,
+    const base::FilePath& suggested_name,
+    const std::string& mime_type) {
+  base::FilePath content_path =
+      current_path.IsContentUri() && base::ContentUriExists(current_path)
+          ? current_path
+          : DownloadCollectionBridge::CreateIntermediateUriForPublish(
+                original_url, referrer_url, suggested_name, mime_type);
+  base::FilePath file_name;
+  if (!content_path.empty()) {
+    file_name = DownloadCollectionBridge::GetDisplayName(content_path);
+  }
+  if (file_name.empty())
+    file_name = suggested_name;
+  return CreateIntermediateUriResult(content_path, file_name);
+}
+
+void OnInterMediateUriCreated(LocalPathCallback callback,
+                              const CreateIntermediateUriResult& result) {
+  std::move(callback).Run(result.content_uri, result.file_name);
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
 }  // namespace
 
 const uint32_t DownloadItem::kInvalidId = 0;
@@ -736,4 +773,28 @@
       kDefaultDownloadFileBufferSize);
 }
 
+void DetermineLocalPath(DownloadItem* download,
+                        const base::FilePath& virtual_path,
+                        LocalPathCallback callback) {
+#if BUILDFLAG(IS_ANDROID)
+  if ((!download->IsTransient() &&
+       DownloadCollectionBridge::ShouldPublishDownload(virtual_path)) ||
+      virtual_path.IsContentUri()) {
+    GetDownloadTaskRunner()->PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(&CreateIntermediateUri,
+                       // Safe because we control download file lifetime.
+                       download->GetOriginalUrl(), download->GetReferrerUrl(),
+                       virtual_path,
+                       virtual_path.IsContentUri()
+                           ? download->GetFileNameToReportUser()
+                           : virtual_path.BaseName(),
+                       download->GetMimeType()),
+        base::BindOnce(&OnInterMediateUriCreated, std::move(callback)));
+    return;
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
+  std::move(callback).Run(virtual_path, base::FilePath());
+}
+
 }  // namespace download
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index 58daaba8..33440613 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -184,7 +184,8 @@
           : BackgroudTargetDeterminationResultTypes::kSuccess);
   std::move(callback).Run(
       target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, danger_type,
-      mixed_content_status, intermediate_path, std::move(download_schedule),
+      mixed_content_status, intermediate_path, base::FilePath(),
+      std::move(download_schedule),
       intermediate_path.empty() ? DOWNLOAD_INTERRUPT_REASON_FILE_FAILED
                                 : DOWNLOAD_INTERRUPT_REASON_NONE);
 }
@@ -404,7 +405,7 @@
     std::move(callback).Run(
         target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
         download->GetDangerType(), download->GetMixedContentStatus(),
-        target_path, download->GetDownloadSchedule(),
+        target_path, base::FilePath(), download->GetDownloadSchedule(),
         DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
     RecordBackgroundTargetDeterminationResult(
         BackgroudTargetDeterminationResultTypes::kTargetPathMissing);
@@ -417,7 +418,7 @@
     std::move(callback).Run(
         target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
         download->GetDangerType(), download->GetMixedContentStatus(),
-        target_path, download->GetDownloadSchedule(),
+        target_path, base::FilePath(), download->GetDownloadSchedule(),
         DOWNLOAD_INTERRUPT_REASON_NONE);
     RecordBackgroundTargetDeterminationResult(
         BackgroudTargetDeterminationResultTypes::kSuccess);
@@ -441,7 +442,7 @@
   std::move(callback).Run(
       target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
       download->GetDangerType(), download->GetMixedContentStatus(),
-      intermediate_path, download->GetDownloadSchedule(),
+      intermediate_path, base::FilePath(), download->GetDownloadSchedule(),
       DOWNLOAD_INTERRUPT_REASON_NONE);
 #endif  // BUILDFLAG(IS_ANDROID)
 }
diff --git a/components/download/public/background_service/download_metadata.cc b/components/download/public/background_service/download_metadata.cc
index 100a49d..ba9c2add 100644
--- a/components/download/public/background_service/download_metadata.cc
+++ b/components/download/public/background_service/download_metadata.cc
@@ -43,7 +43,7 @@
          url_chain == other.url_chain &&
          AreResponseHeadersEqual(response_headers.get(),
                                  other.response_headers.get()) &&
-         hash256 == other.hash256;
+         hash256 == other.hash256 && custom_data == other.custom_data;
 }
 
 DownloadMetaData::DownloadMetaData() : current_size(0u), paused(false) {}
diff --git a/components/download/public/common/download_file.h b/components/download/public/common/download_file.h
index e6cb459e..05196e4 100644
--- a/components/download/public/common/download_file.h
+++ b/components/download/public/common/download_file.h
@@ -115,23 +115,9 @@
   virtual void Resume() = 0;
 
 #if BUILDFLAG(IS_ANDROID)
-  // Renames the download file to an intermediate URI. If current_path is a
-  // content URI, it will be used for the renaming. Otherwise, A new
-  // intermediate URI will be created to write the download file. Once
-  // completes, |callback| is called with a content URI to be written into.
-  virtual void RenameToIntermediateUri(const GURL& original_url,
-                                       const GURL& referrer_url,
-                                       const base::FilePath& file_name,
-                                       const std::string& mime_type,
-                                       const base::FilePath& current_path,
-                                       RenameCompletionCallback callback) = 0;
-
   // Publishes the download to public. Once completes, |callback| is called with
   // the final content URI.
   virtual void PublishDownload(RenameCompletionCallback callback) = 0;
-
-  // Returns the suggested file path from the system.
-  virtual base::FilePath GetDisplayName() = 0;
 #endif  // BUILDFLAG(IS_ANDROID)
 };
 
diff --git a/components/download/public/common/download_file_impl.h b/components/download/public/common/download_file_impl.h
index d793270..a43cc5721 100644
--- a/components/download/public/common/download_file_impl.h
+++ b/components/download/public/common/download_file_impl.h
@@ -80,14 +80,7 @@
   void Resume() override;
 
 #if BUILDFLAG(IS_ANDROID)
-  void RenameToIntermediateUri(const GURL& original_url,
-                               const GURL& referrer_url,
-                               const base::FilePath& file_name,
-                               const std::string& mime_type,
-                               const base::FilePath& current_path,
-                               RenameCompletionCallback callback) override;
   void PublishDownload(RenameCompletionCallback callback) override;
-  base::FilePath GetDisplayName() override;
 #endif  // BUILDFLAG(IS_ANDROID)
 
   // Wrapper of a ByteStreamReader or ScopedDataPipeConsumerHandle, and the meta
@@ -387,10 +380,6 @@
   // TaskRunner this object lives on after initialization.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
-#if BUILDFLAG(IS_ANDROID)
-  base::FilePath display_name_;
-#endif  // BUILDFLAG(IS_ANDROID)
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtr<DownloadDestinationObserver> observer_;
diff --git a/components/download/public/common/download_item_impl.h b/components/download/public/common/download_item_impl.h
index 88a52cb..d82d11f43 100644
--- a/components/download/public/common/download_item_impl.h
+++ b/components/download/public/common/download_item_impl.h
@@ -596,12 +596,15 @@
   // be used (see TargetDisposition). |danger_type| is the danger level of
   // |target_path| as determined by the caller. |intermediate_path| is the path
   // to use to store the download until OnDownloadCompleting() is called.
+  // If |display_name| is empty, the download should keep its current display
+  // name. Otherwise, the new display name should be used.
   virtual void OnDownloadTargetDetermined(
       const base::FilePath& target_path,
       TargetDisposition disposition,
       DownloadDangerType danger_type,
       MixedContentStatus mixed_content_status,
       const base::FilePath& intermediate_path,
+      const base::FilePath& display_name,
       absl::optional<DownloadSchedule> download_schedule,
       DownloadInterruptReason interrupt_reason);
 
diff --git a/components/download/public/common/download_item_impl_delegate.h b/components/download/public/common/download_item_impl_delegate.h
index 87da60e..37df534 100644
--- a/components/download/public/common/download_item_impl_delegate.h
+++ b/components/download/public/common/download_item_impl_delegate.h
@@ -52,6 +52,7 @@
       DownloadDangerType danger_type,
       DownloadItem::MixedContentStatus mixed_content_status,
       const base::FilePath& intermediate_path,
+      const base::FilePath& display_name,
       absl::optional<DownloadSchedule> download_schedule,
       DownloadInterruptReason interrupt_reason)>;
   // Request determination of the download target from the delegate.
diff --git a/components/download/public/common/download_utils.h b/components/download/public/common/download_utils.h
index 454274b..7d8e49f 100644
--- a/components/download/public/common/download_utils.h
+++ b/components/download/public/common/download_utils.h
@@ -106,6 +106,26 @@
 RenameDownloadedFile(const base::FilePath& from_path,
                      const base::FilePath& display_name);
 
+// Callback to be invoked when DetermineLocalPath() completes. The argument
+// |file_path| should be the determined local path. It should be non-empty
+// on success.
+// On Android, |file_path| could be a content Uri (e.g. content://media/1234).
+// In such cases, |file_name| is provided for displaying the file to the user
+// (e.g. test.apk). If |file_path| is not a content Uri, file name could
+// be empty and should be ignored.
+using LocalPathCallback =
+    base::OnceCallback<void(const base::FilePath& file_path,
+                            const base::FilePath& file_name)>;
+
+// If |virtual_path| is not a local path, should return a possibly temporary
+// local path to use for storing the downloaded file. If |virtual_path| is
+// already local, then it should return the same path. |callback| should be
+// invoked to return the path.
+COMPONENTS_DOWNLOAD_EXPORT
+void DetermineLocalPath(DownloadItem* download,
+                        const base::FilePath& virtual_path,
+                        LocalPathCallback callback);
+
 // Finch parameter key value for number of bytes used for content validation
 // during resumption.
 constexpr char kDownloadContentValidationLengthFinchKey[] =
diff --git a/components/download/public/common/mock_download_item_impl.h b/components/download/public/common/mock_download_item_impl.h
index 12b1e88..f7f38a3 100644
--- a/components/download/public/common/mock_download_item_impl.h
+++ b/components/download/public/common/mock_download_item_impl.h
@@ -27,12 +27,13 @@
   explicit MockDownloadItemImpl(DownloadItemImplDelegate* delegate);
   ~MockDownloadItemImpl() override;
 
-  MOCK_METHOD7(OnDownloadTargetDetermined,
+  MOCK_METHOD8(OnDownloadTargetDetermined,
                void(const base::FilePath&,
                     TargetDisposition,
                     DownloadDangerType,
                     MixedContentStatus,
                     const base::FilePath&,
+                    const base::FilePath&,
                     absl::optional<DownloadSchedule>,
                     DownloadInterruptReason));
   MOCK_METHOD1(AddObserver, void(DownloadItem::Observer*));
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc
index 3b579b81..b2b7711 100644
--- a/components/guest_view/browser/guest_view_base.cc
+++ b/components/guest_view/browser/guest_view_base.cc
@@ -72,17 +72,10 @@
     Destroy();
   }
 
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override {
+  void PrimaryPageChanged(content::Page& page) override {
     // TODO(1206312, 1205920): It is incorrect to assume that a navigation will
     // destroy the embedder.
     // If the embedder navigates to a different page then destroy the guest.
-    if (!navigation_handle->IsInMainFrame() ||
-        !navigation_handle->HasCommitted() ||
-        navigation_handle->IsSameDocument()) {
-      return;
-    }
-
     Destroy();
   }
 
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index b23f5e3..6f0a354db 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -873,6 +873,12 @@
   // A suitable label for the cluster. Will be nullopt if no suitable label
   // could be determined.
   absl::optional<std::u16string> label;
+
+  // A floating point score that's positive if the cluster matches the user's
+  // search query, and zero otherwise. This score changes depending on the
+  // entered search query, so this should never be persisted. It's a
+  // UI-state-specific score that's convenient to buffer here.
+  float search_match_score = 0.0;
 };
 
 // A minimal representation of `Cluster` used when retrieving them from
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc
index 0872340d..1015803c 100644
--- a/components/history_clusters/core/config.cc
+++ b/components/history_clusters/core/config.cc
@@ -59,6 +59,11 @@
           internal::kJourneys, "JourneysRescoreVisitsWithinClustersForQuery",
           rescore_visits_within_clusters_for_query);
 
+  sort_clusters_within_batch_for_query =
+      base::GetFieldTrialParamByFeatureAsBool(
+          internal::kJourneys, "JourneysSortClustersWithinBatchForQuery",
+          sort_clusters_within_batch_for_query);
+
   alternate_omnibox_action_text = base::GetFieldTrialParamByFeatureAsBool(
       internal::kJourneys, "JourneysAlternateOmniboxActionText",
       alternate_omnibox_action_text);
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h
index f754f58e..e24d7888 100644
--- a/components/history_clusters/core/config.h
+++ b/components/history_clusters/core/config.h
@@ -57,6 +57,11 @@
   // visits within a cluster to account for whether or not that visit matches.
   bool rescore_visits_within_clusters_for_query = true;
 
+  // If enabled, sorts clusters WITHIN a single batch from most search matches
+  // to least search matches. The batches themselves will still be ordered
+  // reverse chronologically, but the clusters within batches will be resorted.
+  bool sort_clusters_within_batch_for_query = false;
+
   // If enabled, changes the History Clusters omnibox action text to be:
   // "Resume your research" instead of "Resume your journey".
   bool alternate_omnibox_action_text = true;
diff --git a/components/history_clusters/core/history_clusters_util.cc b/components/history_clusters/core/history_clusters_util.cc
index 5a3309bf..1bff3dcf 100644
--- a/components/history_clusters/core/history_clusters_util.cc
+++ b/components/history_clusters/core/history_clusters_util.cc
@@ -38,12 +38,13 @@
 
 // Flags any elements within `cluster_visits` that match `find_nodes`. The
 // matching is deliberately meant to closely mirror the History implementation.
-// Returns true if any elements match, and returns false if none of them do.
-bool FlagMatchingVisits(const query_parser::QueryNodeVector& find_nodes,
-                        std::vector<history::ClusterVisit>* cluster_visits) {
+// Returns the total score of matching visits, and returns 0 if no visits match.
+float ComputeTotalMatchScore(
+    const query_parser::QueryNodeVector& find_nodes,
+    std::vector<history::ClusterVisit>* cluster_visits) {
   DCHECK(cluster_visits);
 
-  bool any_visits_match = false;
+  float total_matching_visit_score = 0.0;
 
   for (auto& visit : *cluster_visits) {
     query_parser::QueryWordVector find_in_words;
@@ -67,11 +68,12 @@
 
     if (query_parser::QueryParser::DoesQueryMatch(find_in_words, find_nodes)) {
       visit.matches_search_query = true;
-      any_visits_match = true;
+      DCHECK_GE(visit.score, 0);
+      total_matching_visit_score += visit.score;
     }
   }
 
-  return any_visits_match;
+  return total_matching_visit_score;
 }
 
 // Re-scores and re-sorts `cluster_visits` so that all visits that match the
@@ -160,19 +162,34 @@
   std::swap(all_clusters, *clusters);
 
   for (auto& cluster : all_clusters) {
-    bool any_visits_match = FlagMatchingVisits(find_nodes, &cluster.visits);
-    if (any_visits_match &&
+    const float total_matching_visit_score =
+        ComputeTotalMatchScore(find_nodes, &cluster.visits);
+    DCHECK_GE(total_matching_visit_score, 0);
+    if (total_matching_visit_score > 0 &&
         GetConfig().rescore_visits_within_clusters_for_query) {
       PromoteMatchingVisitsAboveNonMatchingVisits(&cluster.visits);
     }
 
-    // If any of the visits match, or if any of the `cluster.keywords` match,
-    // move this cluster into the list of surviving clusters.
-    if (any_visits_match ||
-        DoesQueryMatchClusterKeywords(find_nodes, cluster.keywords)) {
+    cluster.search_match_score = total_matching_visit_score;
+    if (DoesQueryMatchClusterKeywords(find_nodes, cluster.keywords)) {
+      // Arbitrarily chosen that cluster keyword matches are worth three points.
+      // TODO(crbug.com/1307071): Use relevancy score for each cluster keyword
+      // once support for that is added to the backend.
+      cluster.search_match_score += 3.0;
+    }
+
+    if (cluster.search_match_score > 0) {
+      // Move the matching clusters into the final list.
       clusters->push_back(std::move(cluster));
     }
   }
+
+  if (GetConfig().sort_clusters_within_batch_for_query) {
+    base::ranges::stable_sort(*clusters, [](auto& c1, auto& c2) {
+      // Use c1 > c2 to get higher scored clusters BEFORE lower scored clusters.
+      return c1.search_match_score > c2.search_match_score;
+    });
+  }
 }
 
 void CullNonProminentOrDuplicateClusters(
diff --git a/components/history_clusters/core/history_clusters_util_unittest.cc b/components/history_clusters/core/history_clusters_util_unittest.cc
index d03776b..fa11c78 100644
--- a/components/history_clusters/core/history_clusters_util_unittest.cc
+++ b/components/history_clusters/core/history_clusters_util_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/history_clusters/core/history_clusters_util.h"
 
 #include "base/strings/stringprintf.h"
+#include "components/history_clusters/core/config.h"
 #include "components/history_clusters/core/history_clusters_service_test_api.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -147,5 +148,70 @@
   }
 }
 
+TEST(HistoryClustersUtilTest, SortClustersWithinBatchForQuery) {
+  std::vector<history::Cluster> all_clusters;
+  all_clusters.push_back(
+      history::Cluster(1,
+                       {
+                           GetHardcodedClusterVisit(1),
+                           GetHardcodedClusterVisit(2),
+                       },
+                       {u"apples", u"Red Oranges"},
+                       /*should_show_on_prominent_ui_surfaces=*/false));
+  all_clusters.push_back(
+      history::Cluster(2,
+                       {
+                           GetHardcodedClusterVisit(1),
+                       },
+                       {u"search"},
+                       /*should_show_on_prominent_ui_surfaces=*/false));
+
+  // When the flag is off, leave the initial ordering alone.
+  {
+    Config config;
+    config.sort_clusters_within_batch_for_query = false;
+    SetConfigForTesting(config);
+
+    std::vector clusters = all_clusters;
+    ApplySearchQuery("search", &clusters);
+    ASSERT_EQ(clusters.size(), 2U);
+    EXPECT_EQ(clusters[0].cluster_id, 1);
+    EXPECT_EQ(clusters[1].cluster_id, 2);
+    EXPECT_FLOAT_EQ(clusters[0].search_match_score, 0.5);
+    EXPECT_FLOAT_EQ(clusters[1].search_match_score, 3.5);
+  }
+
+  // When the flag is on, second cluster should be sorted above the first one,
+  // because the second one has a match on both the keyword and visit.
+  {
+    Config config;
+    config.sort_clusters_within_batch_for_query = true;
+    SetConfigForTesting(config);
+
+    std::vector clusters = all_clusters;
+    ApplySearchQuery("search", &clusters);
+    ASSERT_EQ(clusters.size(), 2U);
+    EXPECT_EQ(clusters[0].cluster_id, 2);
+    EXPECT_EQ(clusters[1].cluster_id, 1);
+    EXPECT_FLOAT_EQ(clusters[0].search_match_score, 3.5);
+    EXPECT_FLOAT_EQ(clusters[1].search_match_score, 0.5);
+  }
+
+  // With flag on, if both scores are equal, the ordering should be preserved.
+  {
+    Config config;
+    config.sort_clusters_within_batch_for_query = true;
+    SetConfigForTesting(config);
+
+    std::vector clusters = all_clusters;
+    ApplySearchQuery("google", &clusters);
+    ASSERT_EQ(clusters.size(), 2U);
+    EXPECT_EQ(clusters[0].cluster_id, 1);
+    EXPECT_EQ(clusters[1].cluster_id, 2);
+    EXPECT_FLOAT_EQ(clusters[0].search_match_score, 0.5);
+    EXPECT_FLOAT_EQ(clusters[1].search_match_score, 0.5);
+  }
+}
+
 }  // namespace
 }  // namespace history_clusters
diff --git a/components/invalidation/impl/fake_invalidation_handler.cc b/components/invalidation/impl/fake_invalidation_handler.cc
index eb9f68b8..3c8cedb9 100644
--- a/components/invalidation/impl/fake_invalidation_handler.cc
+++ b/components/invalidation/impl/fake_invalidation_handler.cc
@@ -6,15 +6,8 @@
 
 namespace invalidation {
 
-FakeInvalidationHandler::FakeInvalidationHandler()
-    : state_(DEFAULT_INVALIDATION_ERROR),
-      invalidation_count_(0),
-      owner_name_("Fake") {}
-
 FakeInvalidationHandler::FakeInvalidationHandler(const std::string& owner_name)
-    : FakeInvalidationHandler() {
-  owner_name_ = owner_name;
-}
+    : owner_name_(owner_name) {}
 
 FakeInvalidationHandler::~FakeInvalidationHandler() = default;
 
diff --git a/components/invalidation/impl/fake_invalidation_handler.h b/components/invalidation/impl/fake_invalidation_handler.h
index 001abd2c..6c7c813 100644
--- a/components/invalidation/impl/fake_invalidation_handler.h
+++ b/components/invalidation/impl/fake_invalidation_handler.h
@@ -14,7 +14,6 @@
 
 class FakeInvalidationHandler : public InvalidationHandler {
  public:
-  FakeInvalidationHandler();
   explicit FakeInvalidationHandler(const std::string& owner);
   FakeInvalidationHandler(const FakeInvalidationHandler& other) = delete;
   FakeInvalidationHandler& operator=(const FakeInvalidationHandler& other) =
@@ -35,9 +34,9 @@
   void OnInvalidatorClientIdChange(const std::string& client_id) override;
 
  private:
-  InvalidatorState state_;
+  InvalidatorState state_ = DEFAULT_INVALIDATION_ERROR;
   TopicInvalidationMap last_invalidation_map_;
-  int invalidation_count_;
+  int invalidation_count_ = 0;
   std::string owner_name_;
   std::string client_id_;
 };
diff --git a/components/invalidation/impl/fcm_invalidation_service_unittest.cc b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
index 1f53441c..9ea80e6 100644
--- a/components/invalidation/impl/fcm_invalidation_service_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
@@ -221,7 +221,7 @@
   ASSERT_TRUE(invalidation_service->GetInvalidatorClientId().empty());
 
   // Register a handler *before* initializing the invalidation service.
-  FakeInvalidationHandler handler;
+  FakeInvalidationHandler handler("owner_1");
   invalidation_service->RegisterInvalidationHandler(&handler);
 
   // Because the invalidation service hasn't been initialized, the client ID is
@@ -252,7 +252,7 @@
 
   // Another handler that gets registered should immediately be informed of the
   // client ID.
-  FakeInvalidationHandler handler2;
+  FakeInvalidationHandler handler2(/*owner=*/"owner_2");
   invalidation_service->RegisterInvalidationHandler(&handler2);
   EXPECT_EQ(handler2.GetInvalidatorClientId(), "FreshInstanceID");
 
diff --git a/components/invalidation/impl/invalidation_logger.cc b/components/invalidation/impl/invalidation_logger.cc
index 2662273..4ddbd82 100644
--- a/components/invalidation/impl/invalidation_logger.cc
+++ b/components/invalidation/impl/invalidation_logger.cc
@@ -19,6 +19,8 @@
 InvalidationLogger::~InvalidationLogger() = default;
 
 void InvalidationLogger::OnRegistration(const std::string& registrar_name) {
+  DCHECK(registered_handlers_.find(registrar_name) ==
+         registered_handlers_.end());
   registered_handlers_.insert(registrar_name);
   EmitRegisteredHandlers();
 }
diff --git a/components/invalidation/impl/invalidation_logger.h b/components/invalidation/impl/invalidation_logger.h
index 1d979451..41a9934 100644
--- a/components/invalidation/impl/invalidation_logger.h
+++ b/components/invalidation/impl/invalidation_logger.h
@@ -90,9 +90,7 @@
   // The name of all invalidatorHandler registered (note that this is not
   // necessarily the same as the keys of latest_topics_, because they might
   // have not registered any Topic).
-  // TODO(crbug.com/1049591): it should be std::set, once handlers names are
-  // unique.
-  std::multiset<std::string> registered_handlers_;
+  std::set<std::string> registered_handlers_;
 };
 
 }  // namespace invalidation
diff --git a/components/invalidation/impl/invalidation_logger_observer.h b/components/invalidation/impl/invalidation_logger_observer.h
index 1767b7e..56a8b2c 100644
--- a/components/invalidation/impl/invalidation_logger_observer.h
+++ b/components/invalidation/impl/invalidation_logger_observer.h
@@ -27,7 +27,7 @@
 class InvalidationLoggerObserver {
  public:
   virtual void OnRegistrationChange(
-      const std::multiset<std::string>& registered_handlers) = 0;
+      const std::set<std::string>& registered_handlers) = 0;
   virtual void OnStateChange(const InvalidatorState& new_state,
                              const base::Time& last_change_timestamp) = 0;
   virtual void OnUpdatedTopics(const std::string& handler_name,
diff --git a/components/invalidation/impl/invalidation_logger_unittest.cc b/components/invalidation/impl/invalidation_logger_unittest.cc
index b531041..3c16a72 100644
--- a/components/invalidation/impl/invalidation_logger_unittest.cc
+++ b/components/invalidation/impl/invalidation_logger_unittest.cc
@@ -23,11 +23,10 @@
     invalidation_received = false;
     detailed_status_received = false;
     updated_topics_replicated = std::map<std::string, TopicCountMap>();
-    registered_handlers = std::multiset<std::string>();
+    registered_handlers = std::set<std::string>();
   }
 
-  void OnRegistrationChange(
-      const std::multiset<std::string>& handlers) override {
+  void OnRegistrationChange(const std::set<std::string>& handlers) override {
     registered_handlers = handlers;
     registration_change_received = true;
   }
@@ -62,7 +61,7 @@
   bool invalidation_received;
   bool detailed_status_received;
   std::map<std::string, TopicCountMap> updated_topics_replicated;
-  std::multiset<std::string> registered_handlers;
+  std::set<std::string> registered_handlers;
 };
 
 // Test that the callbacks are actually being called when observers are
@@ -263,22 +262,22 @@
   log.RegisterObserver(&observer_test);
 
   log.OnRegistration(std::string("FakeHandler1"));
-  std::multiset<std::string> test_multiset;
-  test_multiset.insert("FakeHandler1");
+  std::set<std::string> test_set;
+  test_set.insert("FakeHandler1");
   EXPECT_TRUE(observer_test.registration_change_received);
-  EXPECT_EQ(observer_test.registered_handlers, test_multiset);
+  EXPECT_EQ(observer_test.registered_handlers, test_set);
 
   observer_test.ResetStates();
   log.OnRegistration(std::string("FakeHandler2"));
-  test_multiset.insert("FakeHandler2");
+  test_set.insert("FakeHandler2");
   EXPECT_TRUE(observer_test.registration_change_received);
-  EXPECT_EQ(observer_test.registered_handlers, test_multiset);
+  EXPECT_EQ(observer_test.registered_handlers, test_set);
 
   observer_test.ResetStates();
   log.OnUnregistration(std::string("FakeHandler2"));
-  test_multiset.erase("FakeHandler2");
+  test_set.erase("FakeHandler2");
   EXPECT_TRUE(observer_test.registration_change_received);
-  EXPECT_EQ(observer_test.registered_handlers, test_multiset);
+  EXPECT_EQ(observer_test.registered_handlers, test_set);
 
   log.UnregisterObserver(&observer_test);
 }
diff --git a/components/invalidation/impl/invalidation_service_test_template.cc b/components/invalidation/impl/invalidation_service_test_template.cc
index 65eda862..ef0c20b 100644
--- a/components/invalidation/impl/invalidation_service_test_template.cc
+++ b/components/invalidation/impl/invalidation_service_test_template.cc
@@ -7,8 +7,10 @@
 namespace invalidation {
 
 BoundFakeInvalidationHandler::BoundFakeInvalidationHandler(
-    const InvalidationService& invalidator)
-    : invalidator_(invalidator),
+    const InvalidationService& invalidator,
+    const std::string& owner)
+    : FakeInvalidationHandler(owner),
+      invalidator_(invalidator),
       last_retrieved_state_(DEFAULT_INVALIDATION_ERROR) {}
 
 BoundFakeInvalidationHandler::~BoundFakeInvalidationHandler() = default;
diff --git a/components/invalidation/impl/invalidation_service_test_template.h b/components/invalidation/impl/invalidation_service_test_template.h
index 9df58226..64d8921 100644
--- a/components/invalidation/impl/invalidation_service_test_template.h
+++ b/components/invalidation/impl/invalidation_service_test_template.h
@@ -111,7 +111,7 @@
   InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  FakeInvalidationHandler handler;
+  FakeInvalidationHandler handler("owner");
 
   invalidator->RegisterInvalidationHandler(&handler);
 
@@ -175,10 +175,10 @@
   InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  FakeInvalidationHandler handler1;
-  FakeInvalidationHandler handler2;
-  FakeInvalidationHandler handler3;
-  FakeInvalidationHandler handler4;
+  FakeInvalidationHandler handler1(/*owner=*/"owner_1");
+  FakeInvalidationHandler handler2(/*owner=*/"owner_2");
+  FakeInvalidationHandler handler3(/*owner=*/"owner_3");
+  FakeInvalidationHandler handler4(/*owner=*/"owner_4");
 
   invalidator->RegisterInvalidationHandler(&handler1);
   invalidator->RegisterInvalidationHandler(&handler2);
@@ -256,8 +256,8 @@
   InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  FakeInvalidationHandler handler1;
-  FakeInvalidationHandler handler2;
+  FakeInvalidationHandler handler1(/*owner=*/"owner_1");
+  FakeInvalidationHandler handler2(/*owner=*/"owner_2");
 
   invalidator->RegisterInvalidationHandler(&handler1);
   invalidator->RegisterInvalidationHandler(&handler2);
@@ -279,10 +279,10 @@
   InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  FakeInvalidationHandler handler1;
+  FakeInvalidationHandler handler1(/*owner=*/"owner_1");
 
   // Control observer.
-  FakeInvalidationHandler handler2;
+  FakeInvalidationHandler handler2(/*owner=*/"owner_2");
 
   invalidator->RegisterInvalidationHandler(&handler1);
   invalidator->RegisterInvalidationHandler(&handler2);
@@ -331,7 +331,8 @@
 // the bound InvalidationService.
 class BoundFakeInvalidationHandler : public FakeInvalidationHandler {
  public:
-  explicit BoundFakeInvalidationHandler(const InvalidationService& invalidator);
+  BoundFakeInvalidationHandler(const InvalidationService& invalidator,
+                               const std::string& owner);
 
   BoundFakeInvalidationHandler(const BoundFakeInvalidationHandler&) = delete;
   BoundFakeInvalidationHandler& operator=(const BoundFakeInvalidationHandler&) =
@@ -356,7 +357,7 @@
   InvalidationService* const invalidator =
       this->CreateAndInitializeInvalidationService();
 
-  BoundFakeInvalidationHandler handler(*invalidator);
+  BoundFakeInvalidationHandler handler(*invalidator, "owner");
   invalidator->RegisterInvalidationHandler(&handler);
 
   this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
diff --git a/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc b/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc
index 55aa68fd..24b020f3 100644
--- a/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc
+++ b/components/invalidation/impl/invalidator_registrar_with_memory_unittest.cc
@@ -36,7 +36,7 @@
   auto invalidator = std::make_unique<InvalidatorRegistrarWithMemory>(
       &pref_service, "sender_id", /*migrate_old_prefs=*/false);
 
-  FakeInvalidationHandler handler;
+  FakeInvalidationHandler handler("owner");
   invalidator->RegisterHandler(&handler);
 
   TopicInvalidationMap invalidation_map;
@@ -105,10 +105,10 @@
   auto invalidator = std::make_unique<InvalidatorRegistrarWithMemory>(
       &pref_service, "sender_id", /*migrate_old_prefs=*/false);
 
-  FakeInvalidationHandler handler1;
-  FakeInvalidationHandler handler2;
-  FakeInvalidationHandler handler3;
-  FakeInvalidationHandler handler4;
+  FakeInvalidationHandler handler1("owner_1");
+  FakeInvalidationHandler handler2("owner_2");
+  FakeInvalidationHandler handler3("owner_3");
+  FakeInvalidationHandler handler4("owner_4");
 
   invalidator->RegisterHandler(&handler1);
   invalidator->RegisterHandler(&handler2);
@@ -208,10 +208,10 @@
   auto invalidator = std::make_unique<InvalidatorRegistrarWithMemory>(
       &pref_service, "sender_id", /*migrate_old_prefs=*/false);
 
-  FakeInvalidationHandler handler1;
+  FakeInvalidationHandler handler1("owner_1");
 
   // Control observer.
-  FakeInvalidationHandler handler2;
+  FakeInvalidationHandler handler2("owner_2");
 
   invalidator->RegisterHandler(&handler1);
   invalidator->RegisterHandler(&handler2);
diff --git a/components/invalidation/public/invalidation_handler.h b/components/invalidation/public/invalidation_handler.h
index 0ac1903..00dabd4b 100644
--- a/components/invalidation/public/invalidation_handler.h
+++ b/components/invalidation/public/invalidation_handler.h
@@ -32,8 +32,6 @@
 
   // Returned value must be unique for the handlers using the same invalidation
   // service.
-  // TODO(crbug.com/1049591): this is currently not the case for
-  // CloudPolicyInvalidator.
   virtual std::string GetOwnerName() const = 0;
 
   // Called on change of |client_id|. Client id is used to identify the
diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc
index 82b1d47d..a324c58b 100644
--- a/components/lens/lens_features.cc
+++ b/components/lens/lens_features.cc
@@ -13,52 +13,42 @@
 const base::Feature kLensStandalone{"LensStandalone",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kLensRegionSearch{"LensRegionSearch",
-                                      base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::FeatureParam<bool> kRegionSearchMacCursorFix{
-    &kLensRegionSearch, "region-search-mac-cursor-fix", true};
+    &kLensStandalone, "region-search-mac-cursor-fix", true};
 
 const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText1{
-    &kLensRegionSearch, "use-menu-item-alt-text-1", false};
+    &kLensStandalone, "use-menu-item-alt-text-1", false};
 
 const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText2{
-    &kLensRegionSearch, "use-menu-item-alt-text-2", false};
+    &kLensStandalone, "use-menu-item-alt-text-2", false};
 
 const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText3{
-    &kLensRegionSearch, "use-menu-item-alt-text-3", false};
+    &kLensStandalone, "use-menu-item-alt-text-3", false};
 
 const base::FeatureParam<bool> kRegionSearchUseMenuItemAltText4{
-    &kLensRegionSearch, "use-menu-item-alt-text-4", true};
+    &kLensStandalone, "use-menu-item-alt-text-4", true};
 
 const base::FeatureParam<bool> kEnableUKMLoggingForRegionSearch{
-    &kLensRegionSearch, "region-search-enable-ukm-logging", true};
+    &kLensStandalone, "region-search-enable-ukm-logging", true};
 
 const base::FeatureParam<bool> kEnableUKMLoggingForImageSearch{
     &kLensStandalone, "enable-ukm-logging", true};
 
-const base::FeatureParam<bool> kEnableSidePanelForLensRegionSearch{
-    &kLensRegionSearch, "region-search-enable-side-panel", false};
-
-const base::FeatureParam<bool> kEnableSidePanelForLensImageSearch{
+const base::FeatureParam<bool> kEnableSidePanelForLens{
     &kLensStandalone, "enable-side-panel", false};
 
 constexpr base::FeatureParam<int> kMaxPixelsForRegionSearch{
-    &kLensRegionSearch, "region-search-dimensions-max-pixels", 1000};
+    &kLensStandalone, "region-search-dimensions-max-pixels", 1000};
 
 constexpr base::FeatureParam<int> kMaxAreaForRegionSearch{
-    &kLensRegionSearch, "region-search-dimensions-max-area", 1000000};
+    &kLensStandalone, "region-search-dimensions-max-area", 1000000};
 
 constexpr base::FeatureParam<int> kMaxPixelsForImageSearch{
     &kLensStandalone, "dimensions-max-pixels", 1000};
 
-constexpr base::FeatureParam<std::string> kHomepageURLForImageSearch{
+constexpr base::FeatureParam<std::string> kHomepageURLForLens{
     &kLensStandalone, "lens-homepage-url", "https://lens.google.com/"};
 
-constexpr base::FeatureParam<std::string> kHomepageURLForRegionSearch{
-    &kLensRegionSearch, "region-search-lens-homepage-url",
-    "https://lens.google.com/"};
-
 bool GetEnableUKMLoggingForRegionSearch() {
   return kEnableUKMLoggingForRegionSearch.Get();
 }
@@ -79,12 +69,13 @@
   return kMaxPixelsForImageSearch.Get();
 }
 
-std::string GetHomepageURLForImageSearch() {
-  return kHomepageURLForImageSearch.Get();
+std::string GetHomepageURLForLens() {
+  return kHomepageURLForLens.Get();
 }
 
-std::string GetHomepageURLForRegionSearch() {
-  return kHomepageURLForRegionSearch.Get();
+bool IsLensSidePanelEnabled() {
+  return base::FeatureList::IsEnabled(kLensStandalone) &&
+         kEnableSidePanelForLens.Get();
 }
 
 }  // namespace features
diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h
index 5e9b072..73a7988a 100644
--- a/components/lens/lens_features.h
+++ b/components/lens/lens_features.h
@@ -41,11 +41,8 @@
 // Enables UKM logging for the LensStandalone feature.
 extern const base::FeatureParam<bool> kEnableUKMLoggingForImageSearch;
 
-// Enables the side panel for Lens Region Search.
-extern const base::FeatureParam<bool> kEnableSidePanelForLensRegionSearch;
-
-// Enables the side panel for Lens Image Search.
-extern const base::FeatureParam<bool> kEnableSidePanelForLensImageSearch;
+// Enables the side panel for Lens features on Chrome where supported.
+extern const base::FeatureParam<bool> kEnableSidePanelForLens;
 
 // Returns whether to enable UKM logging for Lens Region Search feature.
 extern bool GetEnableUKMLoggingForRegionSearch();
@@ -64,10 +61,10 @@
 extern int GetMaxPixelsForImageSearch();
 
 // The URL for the Lens home page.
-extern std::string GetHomepageURLForImageSearch();
+extern std::string GetHomepageURLForLens();
 
-// The URL for the Lens home page as defined by Lens Region Search feature.
-extern std::string GetHomepageURLForRegionSearch();
+// Returns whether the Lens side panel is enabled.
+extern bool IsLensSidePanelEnabled();
 
 }  // namespace features
 }  // namespace lens
diff --git a/components/mirroring/service/mirroring_features.cc b/components/mirroring/service/mirroring_features.cc
index 0d6bfda..28df046 100644
--- a/components/mirroring/service/mirroring_features.cc
+++ b/components/mirroring/service/mirroring_features.cc
@@ -25,6 +25,18 @@
 const base::Feature kCastStreamingVp9{"CastStreamingVp9",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether the allow list (legacy behavior) or blocklist is used to
+// determine whether remoting capabilities should be queried for as part of
+// configuring a mirroring session.
+const base::Feature kCastUseBlocklistForRemotingQuery{
+    "CastUseBlocklistForRemotingQuery", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables querying for remoting capabilities against ALL devices, as opposed to
+// just those controlled by the allow or blocklist. When set, this flag takes
+// precedence over the above flag.
+const base::Feature kCastForceEnableRemotingQuery{
+    "CastForceEnableRemotingQuery", base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsCastStreamingAV1Enabled() {
 #if BUILDFLAG(ENABLE_LIBAOM)
   return base::FeatureList::IsEnabled(features::kCastStreamingAv1);
diff --git a/components/mirroring/service/mirroring_features.h b/components/mirroring/service/mirroring_features.h
index 418bdef1..167b3fa43 100644
--- a/components/mirroring/service/mirroring_features.h
+++ b/components/mirroring/service/mirroring_features.h
@@ -21,6 +21,12 @@
 COMPONENT_EXPORT(MIRRORING_SERVICE)
 extern const base::Feature kCastStreamingVp9;
 
+COMPONENT_EXPORT(MIRRORING_SERVICE)
+extern const base::Feature kCastUseBlocklistForRemotingQuery;
+
+COMPONENT_EXPORT(MIRRORING_SERVICE)
+extern const base::Feature kCastForceEnableRemotingQuery;
+
 bool IsCastStreamingAV1Enabled();
 
 }  // namespace features
diff --git a/components/mirroring/service/session.cc b/components/mirroring/service/session.cc
index ccd24b4..68382d70 100644
--- a/components/mirroring/service/session.cc
+++ b/components/mirroring/service/session.cc
@@ -321,6 +321,29 @@
   return sink_metadata;
 }
 
+bool ShouldQueryForRemotingCapabilities(
+    const std::string& receiver_model_name) {
+  if (base::FeatureList::IsEnabled(features::kCastForceEnableRemotingQuery)) {
+    return true;
+  }
+
+  if (base::FeatureList::IsEnabled(
+          features::kCastUseBlocklistForRemotingQuery)) {
+    // The blocklist has not yet been fully determined.
+    // TODO(b/224993260): Implement this blocklist.
+    NOTREACHED();
+    return false;
+  }
+
+  // This is a workaround for Nest Hub devices, which do not support remoting.
+  // TODO(crbug.com/1198616): filtering hack should be removed. See
+  // issuetracker.google.com/135725157 for more information.
+  return base::StartsWith(receiver_model_name, "Chromecast",
+                          base::CompareCase::SENSITIVE) ||
+         base::StartsWith(receiver_model_name, "Eureka Dongle",
+                          base::CompareCase::SENSITIVE);
+}
+
 }  // namespace
 
 class Session::AudioCapturingCallback final
@@ -791,14 +814,8 @@
       media_remoter_->OnMirroringResumed();
   }
 
-  // This is a workaround for Nest Hub devices, which do not support remoting.
-  // TODO(crbug.com/1198616): filtering hack should be removed. See
-  // issuetracker.google.com/135725157 for more information.
   if (initially_starting_session &&
-      (base::StartsWith(session_params_.receiver_model_name, "Chromecast",
-                        base::CompareCase::SENSITIVE) ||
-       base::StartsWith(session_params_.receiver_model_name, "Eureka Dongle",
-                        base::CompareCase::SENSITIVE))) {
+      ShouldQueryForRemotingCapabilities(session_params_.receiver_model_name)) {
     QueryCapabilitiesForRemoting();
   }
 
diff --git a/components/nacl/renderer/nexe_load_manager.cc b/components/nacl/renderer/nexe_load_manager.cc
index 20b92af..b1e19fa 100644
--- a/components/nacl/renderer/nexe_load_manager.cc
+++ b/components/nacl/renderer/nexe_load_manager.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/containers/buffer_iterator.h"
 #include "base/logging.h"
 #include "base/memory/shared_memory_mapping.h"
 #include "base/metrics/histogram.h"
@@ -260,8 +261,8 @@
   base::ReadOnlySharedMemoryMapping shmem_mapping =
       crash_info_shmem_region_.MapAt(0, kNaClCrashInfoShmemSize);
   if (shmem_mapping.IsValid()) {
-    base::BufferIterator<const uint8_t> buffer =
-        shmem_mapping.GetMemoryAsBufferIterator<uint8_t>();
+    auto buffer = base::BufferIterator<const uint8_t>(
+        shmem_mapping.GetMemoryAsSpan<uint8_t>());
     const uint32_t* crash_log_length = buffer.Object<uint32_t>();
     base::span<const uint8_t> data = buffer.Span<uint8_t>(
         std::min<uint32_t>(*crash_log_length, kNaClCrashInfoMaxLogSize));
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java
index 7ea80915..bac57962 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java
@@ -357,7 +357,7 @@
     }
 
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         int[] parseForEmphasizeComponents(
                 String text, AutocompleteSchemeClassifier autocompleteSchemeClassifier);
     }
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index d456488..20e80e4 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -482,30 +482,30 @@
 }
 
 int OmniboxFieldTrial::MaxNumHQPUrlsIndexedAtStartup() {
-  const char* param = kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevicesParam;
-  const bool is_low_end_device = base::SysInfo::IsLowEndDevice();
-  if (is_low_end_device)
-    param = kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevicesParam;
-  std::string param_value(variations::GetVariationParamValue(
-      kBundledExperimentFieldTrialName, param));
-  int num_urls;
-  if (base::StringToInt(param_value, &num_urls))
-    return num_urls;
-
 #if BUILDFLAG(IS_ANDROID)
   // Limits on Android are chosen based on experiment results. See
   // crbug.com/715852#c18.
-  constexpr int kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevices = 100;
-  constexpr int kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevices = 1000;
-  if (is_low_end_device)
-    return kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevices;
-  return kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevices;
+  constexpr int kDefaultOnLowEndDevices = 100;
+  constexpr int kDefaultOnNonLowEndDevices = 1000;
 #else
   // Use 20,000 entries as a safety cap for users with spammed history,
   // such as users who were stuck in a redirect loop with autogenerated URLs.
   // This limit will only affect 0.01% of Windows users. crbug.com/750845.
-  return 20000;
+  constexpr int kDefaultOnLowEndDevices = 20000;
+  constexpr int kDefaultOnNonLowEndDevices = 20000;
 #endif  // BUILDFLAG(IS_ANDROID)
+
+  if (base::SysInfo::IsLowEndDevice()) {
+    return variations::GetVariationParamByFeatureAsInt(
+        omnibox::kHistoryQuickProviderAblateInMemoryURLIndexCacheFile,
+        kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevicesParam,
+        kDefaultOnLowEndDevices);
+  } else {
+    return variations::GetVariationParamByFeatureAsInt(
+        omnibox::kHistoryQuickProviderAblateInMemoryURLIndexCacheFile,
+        kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevicesParam,
+        kDefaultOnNonLowEndDevices);
+  }
 }
 
 size_t OmniboxFieldTrial::HQPMaxVisitsToScore() {
diff --git a/components/omnibox/browser/open_tab_provider.cc b/components/omnibox/browser/open_tab_provider.cc
index 52a5fb9..b5a92a1 100644
--- a/components/omnibox/browser/open_tab_provider.cc
+++ b/components/omnibox/browser/open_tab_provider.cc
@@ -37,13 +37,6 @@
   }
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-  constexpr size_t kMinQueryLength = 2;
-  if (input.text().length() < kMinQueryLength) {
-    // Exit early if the query is too short. This is to mitigate a short query
-    // matching a large volume of results with low confidence.
-    return;
-  }
-
   // TODO(crbug.com/1293702): Open tab search is currently implemented using
   // the history model to score open tabs. This is an interim implementation,
   // which is intended to be replaced with a scoring mechanism using only the
diff --git a/components/optimization_guide/content/browser/page_content_annotations_model_manager.h b/components/optimization_guide/content/browser/page_content_annotations_model_manager.h
index dc43f85..780fc083 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_model_manager.h
+++ b/components/optimization_guide/content/browser/page_content_annotations_model_manager.h
@@ -58,6 +58,9 @@
                 const std::vector<std::string>& inputs,
                 AnnotationType annotation_type) override;
 
+  absl::optional<ModelInfo> GetModelInfoForType(
+      AnnotationType type) const override;
+
   // Requests that the given model for |type| be loaded in the background and
   // then runs |callback| with true when the model is ready to execute. If the
   // model is ready now, the callback is run immediately. If the model file will
@@ -66,10 +69,6 @@
       AnnotationType type,
       base::OnceCallback<void(bool)> callback);
 
-  // Returns the model info associated with the given AnnotationType, if it is
-  // available and loaded.
-  absl::optional<ModelInfo> GetModelInfoForType(AnnotationType type) const;
-
   // Returns the version of the page topics model that is currently being used
   // to annotate page content. Will return |absl::nullopt| if no model is being
   // used to annotate page topics for received page content.
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service.cc b/components/optimization_guide/content/browser/page_content_annotations_service.cc
index 818f5f0..96cb5dc 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_service.cc
+++ b/components/optimization_guide/content/browser/page_content_annotations_service.cc
@@ -239,36 +239,37 @@
 }
 
 // static
-std::string PageContentAnnotationsService::StringInputForPageTopicsDomain(
-    const GURL& url) {
-  std::string domain = base::ToLowerASCII(url.host());
+std::string PageContentAnnotationsService::StringInputForPageTopicsHost(
+    const std::string& host) {
+  std::string output = base::ToLowerASCII(host);
 
   // Strip the 'www.' if it exists.
-  if (base::StartsWith(domain, "www.")) {
-    domain = domain.substr(4);
+  if (base::StartsWith(output, "www.")) {
+    output = output.substr(4);
   }
 
-  for (char c : std::vector<char>{'-', '_', '.', '+'}) {
-    std::replace(domain.begin(), domain.end(), c, ' ');
+  const char kCharsToReplaceWithSpace[] = {'-', '_', '.', '+'};
+  for (char c : kCharsToReplaceWithSpace) {
+    std::replace(output.begin(), output.end(), c, ' ');
   }
 
-  return domain;
+  return output;
 }
 
 void PageContentAnnotationsService::BatchAnnotatePageTopics(
     BatchAnnotationCallback callback,
-    const std::vector<GURL>& inputs) {
-  std::vector<std::string> domains;
-  for (const GURL& url : inputs) {
-    domains.emplace_back(StringInputForPageTopicsDomain(url));
+    const std::vector<std::string>& hosts) {
+  std::vector<std::string> tokenized_hosts;
+  for (const std::string& host : hosts) {
+    tokenized_hosts.emplace_back(StringInputForPageTopicsHost(host));
   }
 
   if (!annotator_) {
-    std::move(callback).Run(CreateEmptyBatchAnnotationResults(domains));
+    std::move(callback).Run(CreateEmptyBatchAnnotationResults(tokenized_hosts));
     return;
   }
 
-  annotator_->Annotate(std::move(callback), domains,
+  annotator_->Annotate(std::move(callback), tokenized_hosts,
                        AnnotationType::kPageTopics);
 }
 
@@ -289,8 +290,8 @@
 absl::optional<ModelInfo> PageContentAnnotationsService::GetModelInfoForType(
     AnnotationType type) const {
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
-  DCHECK(model_manager_);
-  return model_manager_->GetModelInfoForType(type);
+  DCHECK(annotator_);
+  return annotator_->GetModelInfoForType(type);
 #else
   return absl::nullopt;
 #endif
@@ -482,11 +483,7 @@
     return;
   }
 
-  std::vector<GURL> urls;
-  for (const std::string& domain : dummy_inputs) {
-    urls.emplace_back(GURL("https://" + domain));
-  }
-  BatchAnnotatePageTopics(base::DoNothing(), urls);
+  BatchAnnotatePageTopics(base::DoNothing(), dummy_inputs);
 }
 
 // static
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service.h b/components/optimization_guide/content/browser/page_content_annotations_service.h
index fc3b6dd..76c64aa 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_service.h
+++ b/components/optimization_guide/content/browser/page_content_annotations_service.h
@@ -107,10 +107,10 @@
                      const std::vector<std::string>& inputs,
                      AnnotationType annotation_type);
 
-  // Calls |BatchAnnotate| with pre-processing the urls into its domain string,
-  // all specific to PageTopics.
+  // Calls |BatchAnnotate| with pre-processing the hosts into tokens, all
+  // specific to PageTopics.
   void BatchAnnotatePageTopics(BatchAnnotationCallback callback,
-                               const std::vector<GURL>& inputs);
+                               const std::vector<std::string>& inputs);
 
   // Requests that the given model for |type| be loaded in the background and
   // then runs |callback| with true when the model is ready to execute. If the
@@ -135,7 +135,7 @@
 
  private:
   friend class PageContentAnnotationsServiceTest;
-  static std::string StringInputForPageTopicsDomain(const GURL& url);
+  static std::string StringInputForPageTopicsHost(const std::string& host);
 
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   // Callback invoked when |visit| has been annotated.
diff --git a/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc b/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc
index 99a681e..5d493192 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc
+++ b/components/optimization_guide/content/browser/page_content_annotations_service_unittest.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
 
 namespace optimization_guide {
 
@@ -17,29 +16,29 @@
   PageContentAnnotationsServiceTest() = default;
   ~PageContentAnnotationsServiceTest() override = default;
 
-  std::string CallStringInputForPageTopicsDomain(const GURL& url) {
-    return PageContentAnnotationsService::StringInputForPageTopicsDomain(url);
+  std::string CallStringInputForPageTopicsHost(const std::string& host) {
+    return PageContentAnnotationsService::StringInputForPageTopicsHost(host);
   }
 };
 
-TEST_F(PageContentAnnotationsServiceTest, PageTopicsDomain) {
-  std::vector<std::pair<GURL, std::string>> tests = {
-      {GURL("https://www.chromium.org/path?q=a"), "chromium org"},
-      {GURL("https://foo-bar.com/"), "foo bar com"},
-      {GURL("https://foo_bar.com/"), "foo bar com"},
-      {GURL("https://cats.co.uk/"), "cats co uk"},
-      {GURL("https://cats+dogs.com"), "cats dogs com"},
-      {GURL("https://www.foo-bar_.baz.com"), "foo bar  baz com"},
-      {GURL("https://www.foo-bar-baz.com"), "foo bar baz com"},
-      {GURL("https://WwW.LOWER-CASE.com"), "lower case com"},
+TEST_F(PageContentAnnotationsServiceTest, PageTopicsHost) {
+  std::vector<std::pair<std::string, std::string>> tests = {
+      {"www.chromium.org", "chromium org"},
+      {"foo-bar.com", "foo bar com"},
+      {"foo_bar.com", "foo bar com"},
+      {"cats.co.uk", "cats co uk"},
+      {"cats+dogs.com", "cats dogs com"},
+      {"www.foo-bar_.baz.com", "foo bar  baz com"},
+      {"www.foo-bar-baz.com", "foo bar baz com"},
+      {"WwW.LOWER-CASE.com", "lower case com"},
   };
 
   for (const auto& test : tests) {
-    GURL url = test.first;
+    std::string host = test.first;
     std::string expected = test.second;
-    std::string got = CallStringInputForPageTopicsDomain(url);
+    std::string got = CallStringInputForPageTopicsHost(host);
 
-    EXPECT_EQ(expected, got) << url;
+    EXPECT_EQ(expected, got) << host;
   }
 }
 
diff --git a/components/optimization_guide/content/browser/page_content_annotator.h b/components/optimization_guide/content/browser/page_content_annotator.h
index bf86383..f76b870 100644
--- a/components/optimization_guide/content/browser/page_content_annotator.h
+++ b/components/optimization_guide/content/browser/page_content_annotator.h
@@ -9,7 +9,9 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "components/optimization_guide/core/model_info.h"
 #include "components/optimization_guide/core/page_content_annotations_common.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace optimization_guide {
 
@@ -28,6 +30,11 @@
   virtual void Annotate(BatchAnnotationCallback callback,
                         const std::vector<std::string>& inputs,
                         AnnotationType annotation_type) = 0;
+
+  // Returns the model info associated with the given AnnotationType, if it is
+  // available and loaded.
+  virtual absl::optional<ModelInfo> GetModelInfoForType(
+      AnnotationType annotation_type) const = 0;
 };
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/content/browser/test_page_content_annotator.cc b/components/optimization_guide/content/browser/test_page_content_annotator.cc
index f1c12bed..62d7ba8 100644
--- a/components/optimization_guide/content/browser/test_page_content_annotator.cc
+++ b/components/optimization_guide/content/browser/test_page_content_annotator.cc
@@ -53,20 +53,40 @@
   std::move(callback).Run(results);
 }
 
+absl::optional<ModelInfo> TestPageContentAnnotator::GetModelInfoForType(
+    AnnotationType annotation_type) const {
+  if (annotation_type == AnnotationType::kPageTopics)
+    return topics_model_info_;
+
+  if (annotation_type == AnnotationType::kPageEntities)
+    return entities_model_info_;
+
+  if (annotation_type == AnnotationType::kPageEntities)
+    return visibility_scores_model_info_;
+
+  return absl::nullopt;
+}
+
 void TestPageContentAnnotator::UsePageTopics(
+    const absl::optional<ModelInfo>& model_info,
     const base::flat_map<std::string, std::vector<WeightedIdentifier>>&
         topics_by_input) {
+  topics_model_info_ = model_info;
   topics_by_input_ = topics_by_input;
 }
 
 void TestPageContentAnnotator::UsePageEntities(
+    const absl::optional<ModelInfo>& model_info,
     const base::flat_map<std::string, std::vector<ScoredEntityMetadata>>&
         entities_by_input) {
+  entities_model_info_ = model_info;
   entities_by_input_ = entities_by_input;
 }
 
 void TestPageContentAnnotator::UseVisibilityScores(
+    const absl::optional<ModelInfo>& model_info,
     const base::flat_map<std::string, double>& visibility_scores_for_input) {
+  visibility_scores_model_info_ = model_info;
   visibility_scores_for_input_ = visibility_scores_for_input;
 }
 
diff --git a/components/optimization_guide/content/browser/test_page_content_annotator.h b/components/optimization_guide/content/browser/test_page_content_annotator.h
index a38e1cc..d91a5590 100644
--- a/components/optimization_guide/content/browser/test_page_content_annotator.h
+++ b/components/optimization_guide/content/browser/test_page_content_annotator.h
@@ -23,18 +23,21 @@
   // The given page topics are used for the matching BatchAnnotationResults by
   // input string. If the input is not found, the output is left as nullopt.
   void UsePageTopics(
+      const absl::optional<ModelInfo>& model_info,
       const base::flat_map<std::string, std::vector<WeightedIdentifier>>&
           topics_by_input);
 
   // The given page entities are used for the matching BatchAnnotationResults by
   // input string. If the input is not found, the output is left as nullopt.
   void UsePageEntities(
+      const absl::optional<ModelInfo>& model_info,
       const base::flat_map<std::string, std::vector<ScoredEntityMetadata>>&
           entities_by_input);
 
   // The given visibility score is used for the matching BatchAnnotationResults
   // by input string. If the input is not found, the output is left as nullopt.
   void UseVisibilityScores(
+      const absl::optional<ModelInfo>& model_info,
       const base::flat_map<std::string, double>& visibility_scores_for_input);
 
   // PageContentAnnotator:
@@ -42,10 +45,18 @@
                 const std::vector<std::string>& inputs,
                 AnnotationType annotation_type) override;
 
+  absl::optional<ModelInfo> GetModelInfoForType(
+      AnnotationType annotation_type) const override;
+
  private:
+  absl::optional<ModelInfo> topics_model_info_;
   base::flat_map<std::string, std::vector<WeightedIdentifier>> topics_by_input_;
+
+  absl::optional<ModelInfo> entities_model_info_;
   base::flat_map<std::string, std::vector<ScoredEntityMetadata>>
       entities_by_input_;
+
+  absl::optional<ModelInfo> visibility_scores_model_info_;
   base::flat_map<std::string, double> visibility_scores_for_input_;
 };
 
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc
index c798be2e..b42818e 100644
--- a/components/optimization_guide/core/optimization_guide_features.cc
+++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -120,6 +120,10 @@
 const base::Feature kPageEntitiesModelBypassFilters{
     "PageEntitiesModelBypassFilters", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// This feature flag enables resetting the entities model on shutdown.
+const base::Feature kPageEntitiesModelResetOnShutdown{
+    "PageEntitiesModelResetOnShutdown", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables push notification of hints.
 const base::Feature kPushNotifications{"OptimizationGuidePushNotifications",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
@@ -473,6 +477,10 @@
   return !base::FeatureList::IsEnabled(kPageEntitiesModelBypassFilters);
 }
 
+bool ShouldResetPageEntitiesModelOnShutdown() {
+  return base::FeatureList::IsEnabled(kPageEntitiesModelResetOnShutdown);
+}
+
 bool ShouldExecutePageVisibilityModelOnPageContent(const std::string& locale) {
   return base::FeatureList::IsEnabled(kPageVisibilityPageContentAnnotations) &&
          IsSupportedLocaleForFeature(locale,
diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h
index bc760ec..f90021a1 100644
--- a/components/optimization_guide/core/optimization_guide_features.h
+++ b/components/optimization_guide/core/optimization_guide_features.h
@@ -237,6 +237,9 @@
 // model.
 bool ShouldProvideFilterPathForPageEntitiesModel();
 
+// Returns whether the page entities model should be reset on shutdown.
+bool ShouldResetPageEntitiesModelOnShutdown();
+
 // Returns whether the page visibility model should be executed on page content
 // for a user using |locale| as their browser language.
 bool ShouldExecutePageVisibilityModelOnPageContent(const std::string& locale);
diff --git a/components/optimization_guide/core/page_content_annotations_common.h b/components/optimization_guide/core/page_content_annotations_common.h
index 4f77386..0a7f14c 100644
--- a/components/optimization_guide/core/page_content_annotations_common.h
+++ b/components/optimization_guide/core/page_content_annotations_common.h
@@ -86,12 +86,12 @@
   BatchAnnotationResult(const BatchAnnotationResult&);
   ~BatchAnnotationResult();
 
-  std::string input() const { return input_; }
+  const std::string& input() const { return input_; }
   AnnotationType type() const { return type_; }
-  absl::optional<std::vector<WeightedIdentifier>> topics() const {
+  const absl::optional<std::vector<WeightedIdentifier>>& topics() const {
     return topics_;
   }
-  absl::optional<std::vector<ScoredEntityMetadata>> entities() const {
+  const absl::optional<std::vector<ScoredEntityMetadata>>& entities() const {
     return entities_;
   }
   absl::optional<double> visibility_score() const { return visibility_score_; }
diff --git a/components/optimization_guide/core/page_entities_model_executor_impl.cc b/components/optimization_guide/core/page_entities_model_executor_impl.cc
index f4da349..d3a3483 100644
--- a/components/optimization_guide/core/page_entities_model_executor_impl.cc
+++ b/components/optimization_guide/core/page_entities_model_executor_impl.cc
@@ -8,6 +8,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/timer/elapsed_timer.h"
 #include "components/optimization_guide/core/entity_annotator_native_library.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_model_provider.h"
 #include "components/optimization_guide/proto/page_entities_model_metadata.pb.h"
 
@@ -30,7 +31,9 @@
 EntityAnnotatorHolder::~EntityAnnotatorHolder() {
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
 
-  ResetEntityAnnotator();
+  if (features::ShouldResetPageEntitiesModelOnShutdown()) {
+    ResetEntityAnnotator();
+  }
 }
 
 void EntityAnnotatorHolder::
diff --git a/components/page_info/OWNERS b/components/page_info/OWNERS
index 7c601fd..535ff1d 100644
--- a/components/page_info/OWNERS
+++ b/components/page_info/OWNERS
@@ -1 +1,3 @@
 file://chrome/browser/ui/page_info/OWNERS
+
+per-file ...about_this_site*=dullweber@chromium.org
diff --git a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
index 824e73d..6140b5b 100644
--- a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
+++ b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
@@ -122,8 +122,8 @@
   // is needed. Even for syncing users we still should do the initial migration
   // to move local-only data that aren't synced to GMSCore and do the rolling
   // migration to ensure deletions aren’t resurrected.
-  if (is_initial_migration_needed ||
-      base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerAndroid)) {
+  if (is_initial_migration_needed &&
+      features::RequiresInitialMigrationForUnifiedPasswordManager()) {
     metrics_reporter_ = std::make_unique<MigrationMetricsReporter>(
         is_initial_migration_needed ? "InitialMigration" : "RollingMigration");
     PrepareForMigration();
diff --git a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
index c5ad3d93..9e0e90a 100644
--- a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
+++ b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
@@ -93,8 +93,8 @@
 TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
        CurrentMigrationVersionIsUpdatedWhenMigrationIsNeeded_SyncOn) {
   feature_list().InitAndEnableFeatureWithParameters(
-      /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-      {{"migration_version", "1"}});
+      /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
   Init();
   EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
       .WillRepeatedly(Return(true));
@@ -112,8 +112,8 @@
 TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
        AllPrefsAreUpdatedWhenMigrationIsNeeded_SyncOff) {
   feature_list().InitAndEnableFeatureWithParameters(
-      /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-      {{"migration_version", "1"}});
+      /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
   Init();
 
   EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
@@ -132,7 +132,8 @@
 TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
        PrefsUnchangedWhenAttemptedMigrationEarlierToday) {
   feature_list().InitAndEnableFeatureWithParameters(
-      features::kUnifiedPasswordManagerMigration, {{"migration_version", "1"}});
+      features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
   Init();
 
   prefs()->SetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt,
@@ -152,9 +153,9 @@
        LastAttemptUnchangedWhenRollingMigrationDisabled) {
   // Setup the pref to indicate that the initial migration has happened already.
   feature_list().InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "1"}}}},
-      /*disabled_features=*/{features::kUnifiedPasswordManagerAndroid});
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "1"}, {"stage", "1"}}}},
+      /*disabled_features=*/{});
   Init(/*current_migration_version=*/1);
 
   migrator()->StartMigrationIfNecessary();
@@ -166,13 +167,13 @@
                    password_manager::prefs::kTimeOfLastMigrationAttempt));
 }
 
+// TODO(crbug.com/1306001): Reenable rolling migration or clean up.
 TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
-       LastAttemptUpdatedInPrefsWhenRollingMigrationEnabled) {
+       DISABLED_LastAttemptUpdatedInPrefsWhenRollingMigrationEnabled) {
   // Setup the pref to indicate that the initial migration has happened already.
   feature_list().InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "1"}}},
-                            {features::kUnifiedPasswordManagerAndroid, {{}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "1"}, {"stage", "0"}}}},
       /*disabled_features=*/{});
   Init(/*current_migration_version=*/1);
 
@@ -193,8 +194,8 @@
       "PasswordManager.UnifiedPasswordManager.WasMigrationDone";
 
   feature_list().InitAndEnableFeatureWithParameters(
-      /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-      {{"migration_version", "1"}});
+      /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
   Init();
 
   histogram_tester.ExpectTotalCount(kMigrationFinishedMetric, 1);
@@ -208,8 +209,8 @@
       "PasswordManager.UnifiedPasswordManager.WasMigrationDone";
 
   feature_list().InitAndEnableFeatureWithParameters(
-      /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-      {{"migration_version", "1"}});
+      /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
   Init(/*current_migration_version=*/1);
 
   histogram_tester.ExpectTotalCount(kMigrationFinishedMetric, 1);
@@ -223,8 +224,8 @@
       "PasswordManager.UnifiedPasswordManager.WasMigrationDone";
 
   feature_list().InitAndEnableFeatureWithParameters(
-      /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-      {{"migration_version", "2"}});
+      /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "2"}, {"stage", "0"}});
 
   Init(/*current_migration_version=*/1);
 
@@ -235,8 +236,8 @@
 TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
        InitialMigrationForSyncingUserShouldMoveLocalOnlyDataToAndroidBackend) {
   feature_list().InitAndEnableFeatureWithParameters(
-      /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-      {{"migration_version", "1"}});
+      /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
   EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
       .WillRepeatedly(Return(true));
 
@@ -324,8 +325,8 @@
       .WillRepeatedly(Return(false));
 
   feature_list().InitAndEnableFeatureWithParameters(
-      /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-      {{"migration_version", "1"}});
+      /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
 
   const MigrationParam& p = GetParam();
 
@@ -349,14 +350,14 @@
   }
 }
 
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
 TEST_P(BuiltInBackendToAndroidBackendMigratorTestWithMigrationParams,
-       RollingMigration) {
+       DISABLED_RollingMigration) {
   // Setup the pref to indicate that the initial migration has happened already.
   // This implies that rolling migration will take place!
   feature_list().InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "1"}}},
-                            {features::kUnifiedPasswordManagerAndroid, {{}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "1"}, {"stage", "0"}}}},
       /*disabled_features=*/{});
   BuiltInBackendToAndroidBackendMigratorTest::Init(
       /*current_migration_version=*/1);
@@ -429,18 +430,16 @@
                                             0.0);
     if (GetParam().is_initial_migration) {
       feature_list().InitAndEnableFeatureWithParameters(
-          /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-          {{"migration_version", "1"}});
+          /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+          {{"migration_version", "1"}, {"stage", "0"}});
       latency_metric_ =
           "PasswordManager.UnifiedPasswordManager.InitialMigration.Latency";
       success_metric_ =
           "PasswordManager.UnifiedPasswordManager.InitialMigration.Success";
     } else {
       feature_list().InitWithFeaturesAndParameters(
-          /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                                 {{"migration_version", "1"}}},
-                                {features::kUnifiedPasswordManagerAndroid,
-                                 {{}}}},
+          /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                                 {{"migration_version", "1"}, {"stage", "0"}}}},
           /*disabled_features=*/{});
       // Setup the pref to indicate that the initial migration has happened
       // already.
@@ -508,17 +507,14 @@
                                      !GetParam().is_successful_migration);
 }
 
+// TODO(crbug.com/1306001): Add test cases rolling migration or clean up.
 INSTANTIATE_TEST_SUITE_P(
     BuiltInBackendToAndroidBackendMigratorTest,
     BuiltInBackendToAndroidBackendMigratorTestMetrics,
     testing::Values(MigrationParamForMetrics{.is_initial_migration = true,
                                              .is_successful_migration = true},
-                    MigrationParamForMetrics{.is_initial_migration = true,
-                                             .is_successful_migration = false},
-                    MigrationParamForMetrics{.is_initial_migration = false,
-                                             .is_successful_migration = true},
                     MigrationParamForMetrics{
-                        .is_initial_migration = false,
+                        .is_initial_migration = true,
                         .is_successful_migration = false}));
 
 class BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest
@@ -530,8 +526,8 @@
     prefs()->registry()->RegisterDoublePref(prefs::kTimeOfLastMigrationAttempt,
                                             0.0);
     feature_list().InitAndEnableFeatureWithParameters(
-        /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-        {{"migration_version", "1"}});
+        /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+        {{"migration_version", "1"}, {"stage", "0"}});
 
     migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>(
         &built_in_backend_, &android_backend_, prefs(), &sync_delegate_);
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
index e7d1565..2e636b7 100644
--- a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
+++ b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
@@ -68,8 +68,10 @@
   active_backend_->InitBackend(std::move(remote_form_changes_received),
                                std::move(sync_enabled_or_disabled_cb),
                                std::move(completion));
-  if (base::FeatureList::IsEnabled(
-          features::kUnifiedPasswordManagerMigration)) {
+
+  // Only start the migration when launching the UPM which needs chrome-local
+  // data in the remote store. For shadow traffic, this doesn't matter.
+  if (features::RequiresInitialMigrationForUnifiedPasswordManager()) {
     migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>(
         built_in_backend_.get(), android_backend_.get(), prefs_,
         sync_delegate_.get());
@@ -201,12 +203,16 @@
 }
 
 void PasswordStoreBackendMigrationDecorator::SyncStatusChanged() {
-  if (!base::FeatureList::IsEnabled(features::kUnifiedPasswordManagerMigration))
+  if (!features::RequiresInitialMigrationForUnifiedPasswordManager())
     return;
 
   if (sync_delegate_->IsSyncingPasswordsEnabled()) {
-    // Sync was enabled. Delete all the passwords from GMS Core local storage.
-    android_backend_->ClearAllLocalPasswords();
+    // During initial rollout, local passwords remain untouched. Only the use of
+    // a different local storage requires explicit migration.
+    if (features::ManagesLocalPasswordsInUnifiedPasswordManager()) {
+      // Sync was enabled. Delete all the passwords from GMS Core local storage.
+      android_backend_->ClearAllLocalPasswords();
+    }
   } else {
     // Clear migration pref to force rerun of initial migration of passwords
     // from Chrome to GMS Core local storage.
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc b/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
index 9288d4b4..3ff1bc5 100644
--- a/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
+++ b/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
@@ -36,8 +36,8 @@
         prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
 
     feature_list_.InitAndEnableFeatureWithParameters(
-        /*enabled_feature=*/features::kUnifiedPasswordManagerMigration,
-        {{"migration_version", "1"}});
+        /*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
+        {{"migration_version", "1"}, {"stage", "0"}});
 
     backend_migration_decorator_ =
         std::make_unique<PasswordStoreBackendMigrationDecorator>(
@@ -126,8 +126,9 @@
                    prefs::kCurrentMigrationVersionToGoogleMobileServices));
 }
 
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
 TEST_F(PasswordStoreBackendMigrationDecoratorTest,
-       LocalAndroidPasswordsClearedWhenSyncEnabled) {
+       DISABLED_LocalAndroidPasswordsClearedWhenSyncEnabled) {
   base::MockCallback<base::OnceCallback<void(bool)>> mock_completion_callback;
   base::RepeatingClosure sync_status_changed_closure;
 
diff --git a/components/password_manager/core/browser/password_store_proxy_backend.cc b/components/password_manager/core/browser/password_store_proxy_backend.cc
index 31b0701..23369493 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend.cc
+++ b/components/password_manager/core/browser/password_store_proxy_backend.cc
@@ -28,20 +28,8 @@
 
 bool ShouldExecuteModifyOperationsOnShadowBackend(PrefService* prefs,
                                                   bool is_syncing) {
-  if (!base::FeatureList::IsEnabled(
-          features::kUnifiedPasswordManagerShadowWriteOperationsAndroid)) {
-    return false;
-  }
-  if (is_syncing)
-    return false;
-  if (features::kMigrationVersion.Get() >
-      prefs->GetInteger(
-          prefs::kCurrentMigrationVersionToGoogleMobileServices)) {
-    // If initial migration isn't completed yet, we shouldn't modify the shadow
-    // backend.
-    return false;
-  }
-  return true;
+  // TODO(crbug.com/1306001): Reenable or clean up for local-only users.
+  return false;
 }
 
 bool ShouldExecuteReadOperationsOnShadowBackend(PrefService* prefs,
@@ -51,8 +39,11 @@
     // i.e. necessary migrations have happened and appropriate flags are set.
     return true;
   }
-  return is_syncing && base::FeatureList::IsEnabled(
-                           features::kUnifiedPasswordManagerShadowAndroid);
+  return is_syncing &&
+         base::FeatureList::IsEnabled(
+             features::kUnifiedPasswordManagerAndroid) &&
+         features::kUpmExperimentVariationParam.Get() ==
+             features::UpmExperimentVariation::kShadowSyncingUsers;
 }
 
 using MethodName = base::StrongAlias<struct MethodNameTag, std::string>;
diff --git a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
index 471be652..cc21114c 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
+++ b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
@@ -74,10 +74,9 @@
     proxy_backend_ = std::make_unique<PasswordStoreProxyBackend>(
         &main_backend_, &shadow_backend_, &prefs_, &sync_delegate_);
 
-    feature_list_.InitWithFeatures(
-        /*enabled_features=*/
-        {features::kUnifiedPasswordManagerShadowAndroid,
-         features::kUnifiedPasswordManagerShadowWriteOperationsAndroid},
+    feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                               {{"stage", "1"}}}},
         /*disabled_features=*/{});
 
     prefs_.registry()->RegisterIntegerPref(
@@ -321,8 +320,8 @@
        NoShadowGetAllLoginsAsyncWithoutSyncOrMigration) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
@@ -357,8 +356,8 @@
        NoShadowGetAutofillableLoginsAsyncWithoutSyncOrMigration) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
   EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
@@ -373,8 +372,8 @@
        NoShadowFillMatchingLoginsAsyncWithoutSyncOrMigration) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
   EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
@@ -401,8 +400,8 @@
        NoShadowAddLoginAsyncWhenSyncDisabledAndInitialMigrationIncomplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
@@ -415,12 +414,14 @@
                                 /*callback=*/base::DoNothing());
 }
 
-TEST_F(PasswordStoreProxyBackendTest,
-       ShadowAddLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) {
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
+TEST_F(
+    PasswordStoreProxyBackendTest,
+    DISABLED_ShadowAddLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
 
@@ -433,14 +434,16 @@
                                 /*callback=*/base::DoNothing());
 }
 
-TEST_F(PasswordStoreProxyBackendTest, ShadowAddLoginAsyncBasicMetricsTesting) {
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
+TEST_F(PasswordStoreProxyBackendTest,
+       DISABLED_ShadowAddLoginAsyncBasicMetricsTesting) {
   base::HistogramTester histogram_tester;
   // Set the prefs such that no initial migration is required to allow shadow
   // write operations.
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
   // Shadow write operations run only for non-syncing users.
@@ -493,8 +496,8 @@
        NoShadowUpdateLoginAsyncWhenSyncDisabledAndInitialMigrationIncomplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
@@ -507,12 +510,14 @@
                                    /*callback=*/base::DoNothing());
 }
 
-TEST_F(PasswordStoreProxyBackendTest,
-       ShadowGetAllLoginsAsyncWhenSyncDisabledAndInitialMigrationComplete) {
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
+TEST_F(
+    PasswordStoreProxyBackendTest,
+    DISABLED_ShadowGetAllLoginsAsyncWhenSyncDisabledAndInitialMigrationComplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
   EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
@@ -546,12 +551,14 @@
   histogram_tester.ExpectTotalCount(prefix + "InconsistentPasswords.Rel", 1);
 }
 
-TEST_F(PasswordStoreProxyBackendTest,
-       ShadowUpdateLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) {
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
+TEST_F(
+    PasswordStoreProxyBackendTest,
+    DISABLED_ShadowUpdateLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
 
@@ -578,8 +585,8 @@
        NoShadowRemoveLoginAsyncWhenSyncDisabledAndInitialMigrationIncomplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
@@ -592,12 +599,14 @@
                                    /*callback=*/base::DoNothing());
 }
 
-TEST_F(PasswordStoreProxyBackendTest,
-       ShadowRemoveLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) {
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
+TEST_F(
+    PasswordStoreProxyBackendTest,
+    DISABLED_ShadowRemoveLoginAsyncWhenSyncDisabledAndInitialMigrationComplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
 
@@ -630,8 +639,8 @@
     NoShadowRemoveLoginsByURLAndTimeAsyncWhenSyncDisabledAndInitialMigrationIncomplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
@@ -648,13 +657,14 @@
       /*callback=*/base::DoNothing());
 }
 
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
 TEST_F(
     PasswordStoreProxyBackendTest,
-    ShadowRemoveLoginsByURLAndTimeAsyncWhenSyncDisabledAndInitialMigrationComplete) {
+    DISABLED_ShadowRemoveLoginsByURLAndTimeAsyncWhenSyncDisabledAndInitialMigrationComplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
 
@@ -689,8 +699,8 @@
     NoShadowRemoveLoginsCreatedBetweenAsyncWhenSyncDisabledAndInitialMigrationIncomplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
@@ -705,13 +715,14 @@
       /*callback=*/base::DoNothing());
 }
 
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
 TEST_F(
     PasswordStoreProxyBackendTest,
-    ShadowRemoveLoginsCreatedBetweenAsyncWhenSyncDisabledAndInitialMigrationComplete) {
+    DISABLED_ShadowRemoveLoginsCreatedBetweenAsyncWhenSyncDisabledAndInitialMigrationComplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
 
@@ -742,8 +753,8 @@
     NoShadowDisableAutoSignInForOriginsAsyncWhenSyncDisabledAndInitialMigrationIncomplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
 
@@ -756,13 +767,14 @@
       base::BindRepeating(&FilterNoUrl), /*completion=*/base::DoNothing());
 }
 
+// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
 TEST_F(
     PasswordStoreProxyBackendTest,
-    ShadowDisableAutoSignInForOriginsAsyncWhenSyncDisabledAndInitialMigrationComplete) {
+    DISABLED_ShadowDisableAutoSignInForOriginsAsyncWhenSyncDisabledAndInitialMigrationComplete) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
+      /*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
+                             {{"migration_version", "2"}, {"stage", "1"}}}},
       /*disabled_features=*/{});
   prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
 
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index e493272..a454f16 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -193,23 +193,6 @@
 const base::Feature kUnifiedPasswordManagerAndroid{
     "UnifiedPasswordManagerAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables migration of passwords from built in storage to Google Mobile
-// Services.
-const base::Feature kUnifiedPasswordManagerMigration{
-    "UnifiedPasswordManagerMigration", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Sends shadow traffic to Google Mobile Services for password storage. This
-// allows to check stability without switching away from the local storage as
-// source of truth.
-const base::Feature kUnifiedPasswordManagerShadowAndroid{
-    "UnifiedPasswordManagerShadowAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Similar to kUnifiedPasswordManagerShadowAndroid but send modify operations
-// instead of read operations.Relevant only for non-sync'ing users.
-const base::Feature kUnifiedPasswordManagerShadowWriteOperationsAndroid{
-    "UnifiedPasswordManagerShadowWriteOperationsAndroid",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 // If enabled, the built-in sync functionality in PasswordSyncBridge becomes
 // unused, meaning that SyncService/SyncEngine will no longer download or
 // upload changes to/from the Sync server. Instead, an external Android-specific
@@ -249,7 +232,7 @@
 // Current migration version to Google Mobile Services. If version saved in pref
 // is lower than 'kMigrationVersion' passwords will be re-uploaded.
 extern const base::FeatureParam<int> kMigrationVersion = {
-    &kUnifiedPasswordManagerMigration, "migration_version", 1};
+    &kUnifiedPasswordManagerAndroid, "migration_version", 1};
 #endif
 
 // Field trial identifier for password generation requirements.
@@ -294,9 +277,44 @@
 
 #if BUILDFLAG(IS_ANDROID)
 bool UsesUnifiedPasswordManagerUi() {
-  return base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid) &&
-         kUpmExperimentVariationParam.Get() !=
-             UpmExperimentVariation::kShadowSyncingUsers;
+  if (!base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid))
+    return false;
+  UpmExperimentVariation variation = kUpmExperimentVariationParam.Get();
+  switch (variation) {
+    case UpmExperimentVariation::kEnableForSyncingUsers:
+      return true;
+    case UpmExperimentVariation::kShadowSyncingUsers:
+      return false;
+  }
+  NOTREACHED() << "Define explicitly whether UI is required!";
+  return false;
+}
+
+bool RequiresInitialMigrationForUnifiedPasswordManager() {
+  if (!base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid))
+    return false;
+  UpmExperimentVariation variation = kUpmExperimentVariationParam.Get();
+  switch (variation) {
+    case UpmExperimentVariation::kEnableForSyncingUsers:
+      return true;
+    case UpmExperimentVariation::kShadowSyncingUsers:
+      return false;
+  }
+  NOTREACHED() << "Define explicitly whether migration is required!";
+  return false;
+}
+
+bool ManagesLocalPasswordsInUnifiedPasswordManager() {
+  if (!base::FeatureList::IsEnabled(kUnifiedPasswordManagerAndroid))
+    return false;
+  UpmExperimentVariation variation = kUpmExperimentVariationParam.Get();
+  switch (variation) {
+    case UpmExperimentVariation::kEnableForSyncingUsers:
+    case UpmExperimentVariation::kShadowSyncingUsers:
+      return false;
+  }
+  NOTREACHED() << "Define explicitly whether migration is required!";
+  return false;
 }
 #endif  // IS_ANDROID
 
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 3d997607..540549c 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -64,9 +64,6 @@
 #if BUILDFLAG(IS_ANDROID)
 extern const base::Feature kUnifiedCredentialManagerDryRun;
 extern const base::Feature kUnifiedPasswordManagerAndroid;
-extern const base::Feature kUnifiedPasswordManagerMigration;
-extern const base::Feature kUnifiedPasswordManagerShadowAndroid;
-extern const base::Feature kUnifiedPasswordManagerShadowWriteOperationsAndroid;
 extern const base::Feature kUnifiedPasswordManagerSyncUsingAndroidBackendOnly;
 #endif
 extern const base::Feature kUnifiedPasswordManagerDesktop;
@@ -120,6 +117,15 @@
 // Returns true if the unified password manager feature is active and in a stage
 // that allows to use the new UI.
 bool UsesUnifiedPasswordManagerUi();
+
+// Returns true if the unified password manager feature is active and in a stage
+// that requires migrating existing credentials initially. Independent of
+// whether only non-syncable data needs to be migrated or full credentials.
+bool RequiresInitialMigrationForUnifiedPasswordManager();
+
+// Returns true if the unified password manager feature is active and in a stage
+// that uses the unified storage for passwords that remain local on the device.
+bool ManagesLocalPasswordsInUnifiedPasswordManager();
 #endif  // IS_ANDROID
 
 }  // namespace password_manager::features
diff --git a/components/policy/core/common/management/management_service.cc b/components/policy/core/common/management/management_service.cc
index f5f50bf..87b149f 100644
--- a/components/policy/core/common/management/management_service.cc
+++ b/components/policy/core/common/management/management_service.cc
@@ -107,16 +107,17 @@
   ManagementAuthorityTrustworthiness previous =
       GetManagementAuthorityTrustworthiness();
   for (const auto& provider : management_status_providers_) {
-    if (provider->RequiresCache())
+    if (provider->RequiresCache()) {
       provider->UpdateCache(provider->FetchAuthority());
+    }
 
-  ManagementAuthorityTrustworthiness next =
-      GetManagementAuthorityTrustworthiness();
-  base::UmaHistogramBoolean(
-      "Enterprise.ManagementAuthorityTrustworthiness.Cache.ValueChange",
-      previous != next);
-  if (callback)
-    std::move(callback).Run(previous, next);
+    ManagementAuthorityTrustworthiness next =
+        GetManagementAuthorityTrustworthiness();
+    base::UmaHistogramBoolean(
+        "Enterprise.ManagementAuthorityTrustworthiness.Cache.ValueChange",
+        previous != next);
+    if (callback)
+      std::move(callback).Run(previous, next);
   }
 }
 
@@ -190,4 +191,10 @@
   management_status_providers_ = std::move(providers);
 }
 
+void ManagementService::AddManagementStatusProvider(
+    std::unique_ptr<ManagementStatusProvider> provider) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  management_status_providers_.push_back(std::move(provider));
+}
+
 }  // namespace policy
diff --git a/components/policy/core/common/management/management_service.h b/components/policy/core/common/management/management_service.h
index 4ce5dc8..6836d27f 100644
--- a/components/policy/core/common/management/management_service.h
+++ b/components/policy/core/common/management/management_service.h
@@ -131,6 +131,10 @@
   // Sets the management status providers to be used by the service.
   void SetManagementStatusProvider(
       std::vector<std::unique_ptr<ManagementStatusProvider>> providers);
+
+  void AddManagementStatusProvider(
+      std::unique_ptr<ManagementStatusProvider> provider);
+
   const std::vector<std::unique_ptr<ManagementStatusProvider>>&
   management_status_providers() {
     return management_status_providers_;
diff --git a/components/policy/core/common/management/platform_management_service.cc b/components/policy/core/common/management/platform_management_service.cc
index 6e663f8f..a68d3787 100644
--- a/components/policy/core/common/management/platform_management_service.cc
+++ b/components/policy/core/common/management/platform_management_service.cc
@@ -51,6 +51,14 @@
 
 PlatformManagementService::~PlatformManagementService() = default;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+void PlatformManagementService::AddChromeOsStatusProvider(
+    std::unique_ptr<ManagementStatusProvider> provider) {
+  AddManagementStatusProvider(std::move(provider));
+  has_cros_status_provider_ = true;
+}
+#endif
+
 void PlatformManagementService::RefreshCache(CacheRefreshCallback callback) {
   if (!base::FeatureList::IsEnabled(features::kEnableCachedManagementStatus))
     return;
diff --git a/components/policy/core/common/management/platform_management_service.h b/components/policy/core/common/management/platform_management_service.h
index 1afd29d..ea542f71 100644
--- a/components/policy/core/common/management/platform_management_service.h
+++ b/components/policy/core/common/management/platform_management_service.h
@@ -7,6 +7,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/no_destructor.h"
+#include "build/chromeos_buildflags.h"
 
 #include "components/policy/core/common/management/management_service.h"
 #include "components/policy/policy_export.h"
@@ -22,6 +23,12 @@
   // Returns the singleton instance of PlatformManagementService.
   static PlatformManagementService* GetInstance();
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  void AddChromeOsStatusProvider(
+      std::unique_ptr<ManagementStatusProvider> provider);
+  bool has_cros_status_provider() const { return has_cros_status_provider_; }
+#endif
+
   void RefreshCache(CacheRefreshCallback callback) override;
 
  private:
@@ -42,6 +49,10 @@
 
   PlatformManagementService();
   ~PlatformManagementService() override;
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  bool has_cros_status_provider_;
+#endif
 };
 
 }  // namespace policy
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index b1f7683..6394980 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -1645,10 +1645,6 @@
 }
 
 void NativeWidgetNSWindowBridge::UpdateWindowGeometry() {
-  if (fullscreen_controller_ &&
-      fullscreen_controller_->IsInFullscreenTransition())
-    return;
-
   gfx::Rect window_in_screen = gfx::ScreenRectFromNSRect([window_ frame]);
   gfx::Rect content_in_screen = gfx::ScreenRectFromNSRect(
       [window_ contentRectForFrameRect:[window_ frame]]);
diff --git a/components/search/BUILD.gn b/components/search/BUILD.gn
index ae69d747d..df271d3 100644
--- a/components/search/BUILD.gn
+++ b/components/search/BUILD.gn
@@ -14,7 +14,6 @@
 
   deps = [
     "//base",
-    "//components/commerce/core:feature_list",
     "//components/google/core/common",
     "//components/history/core/browser",
     "//components/keyed_service/core",
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 84c9b02..791a360 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -10,7 +10,6 @@
 #include "base/strings/string_split.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "components/commerce/core/commerce_feature_list.h"
 
 namespace ntp_features {
 
@@ -181,98 +180,6 @@
 const char kRealboxMatchSearchboxThemeParam[] =
     "RealboxMatchSearchboxThemeParam";
 
-// Params for Discount Consent V2 in the NTP Cart module.
-const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[] =
-    "discount-consent-ntp-variation";
-const base::FeatureParam<int> kNtpChromeCartModuleDiscountConsentNtpVariation{
-    &commerce::kDiscountConsentV2,
-    kNtpChromeCartModuleDiscountConsentNtpVariationParam, 0};
-const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[] =
-    "discount-consent-ntp-reshow-time";
-const base::FeatureParam<base::TimeDelta>
-    kNtpChromeCartModuleDiscountConsentReshowTime{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentReshowTimeParam, base::Days(28)};
-const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[] =
-    "discount-consent-ntp-max-dismiss-count";
-const base::FeatureParam<int>
-    kNtpChromeCartModuleDiscountConsentMaxDismissalCount{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam, 1};
-
-// String change variation params.
-const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[] =
-    "string-change-content";
-const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentStringChangeContent{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentStringChangeContentParam, ""};
-
-const char kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[] =
-    "inline-card-show-button";
-const base::FeatureParam<bool>
-    kNtpChromeCartModuleDiscountConsentInlineShowCloseButton{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentStringChangeContentParam, true};
-
-// Discount consent v2 step 1 params.
-const char
-    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[] =
-        "step-one-use-static-content";
-const base::FeatureParam<bool>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam,
-        false};
-const char kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[] =
-    "step-one-static-content";
-const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam, ""};
-const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[] =
-    "step-one-one-cart-content";
-const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam, ""};
-const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[] =
-    "step-one-two-carts-content";
-const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam, ""};
-const char
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[] =
-        "step-one-three-carts-content";
-const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam,
-        ""};
-
-// Discount consent v2 step 2 params.
-const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[] =
-    "step-two-content";
-const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepTwoContent{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam, ""};
-const char
-    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[] =
-        "step-two-different-color";
-const base::FeatureParam<bool>
-    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam,
-        false};
-const char kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[] =
-    "dialog-content-title";
-const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle{
-        &commerce::kDiscountConsentV2,
-        kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam, ""};
-
 base::TimeDelta GetModulesLoadTimeout() {
   std::string param_value = base::GetFieldTrialParamValueByFeature(
       kModules, kNtpModulesLoadTimeoutMillisecondsParam);
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index 6b29afbb..eb51b2f 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -111,93 +111,6 @@
 // Parameter determining the variations of searchbox theme matching.
 extern const char kRealboxMatchSearchboxThemeParam[];
 
-// The following are Feature params for Discount user consent v2.
-// This indicates the Discount Consent v2 variation on the NTP Cart module.
-enum class DiscountConsentNtpVariation {
-  // These values are persisted to logs. Entries should not be renumbered and
-  // numeric values should never be reused.
-  kDefault = 0,
-  kStringChange = 1,
-  kInline = 2,
-  kDialog = 3,
-  kMaxValue = kDialog
-};
-
-// Param indicates the ConsentV2 variation. See DiscountConsentNtpVariation
-// enum.
-extern const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[];
-extern const base::FeatureParam<int>
-    kNtpChromeCartModuleDiscountConsentNtpVariation;
-// The time interval, after the last dismissal, before reshowing the consent.
-extern const char kNtpChromeCartModuleDiscountConsentReshowTimeParam[];
-extern const base::FeatureParam<base::TimeDelta>
-    kNtpChromeCartModuleDiscountConsentReshowTime;
-// The max number of dismisses allowed.
-extern const char kNtpChromeCartModuleDiscountConsentMaxDismissalCountParam[];
-extern const base::FeatureParam<int>
-    kNtpChromeCartModuleDiscountConsentMaxDismissalCount;
-
-// String change variation params. This string is replacing the content string
-// of the v1 consent.
-extern const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[];
-extern const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentStringChangeContent;
-
-// DiscountConsentNtpVariation::kInline and DiscountConsentNtpVariation::kDialog
-// params. This indicate whether the 'x' button should show.
-extern const char
-    kNtpChromeCartModuleDiscountConsentInlineShowCloseButtonParam[];
-extern const base::FeatureParam<bool>
-    kNtpChromeCartModuleDiscountConsentInlineShowCloseButton;
-
-// The following are discount consent step 1 params.
-// This indicates whether the content in step 1 is a static string that does not
-// contain any merchant names.
-extern const char
-    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[];
-extern const base::FeatureParam<bool>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent;
-// This the content string use in step 1 if
-// kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent.Get() is true.
-extern const char
-    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[];
-extern const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent;
-// This is a string template that takes in one merchant name, and it's used when
-// there is only 1 Chrome Cart.
-extern const char
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[];
-extern const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart;
-// This is a string template that takes in two merchant names, and it's used
-// when there are only 2 Chrome Carts.
-extern const char
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[];
-extern const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts;
-// This is a string template that takes in two merchant names, and it's used
-// when there are 3 or more Chrome Carts.
-extern const char
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[];
-extern const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts;
-
-// The following are discount consent step 2 params.
-// This is the content string used in step 2. This is the actual consent string.
-extern const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[];
-extern const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpStepTwoContent;
-// This is used to indicate whether the backgound-color of step 2 should change.
-extern const char
-    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[];
-extern const base::FeatureParam<bool>
-    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor;
-// This is the content title use in the dialog consent.
-extern const char
-    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[];
-extern const base::FeatureParam<std::string>
-    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle;
-
 // Returns the timeout after which the load of a module should be aborted.
 base::TimeDelta GetModulesLoadTimeout();
 
diff --git a/components/search_engines/search_terms_data.cc b/components/search_engines/search_terms_data.cc
index 018730d..17224159b 100644
--- a/components/search_engines/search_terms_data.cc
+++ b/components/search_engines/search_terms_data.cc
@@ -114,12 +114,8 @@
   const std::string kGoogleHomepageURLPath = std::string("searchbyimage/");
 
 #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
-  // If both LensStandalone and LensRegionSearch features are enabled,
-  // LensStandalone parameters will take precedence even if the values differ.
   if (base::FeatureList::IsEnabled(lens::features::kLensStandalone)) {
-    return lens::features::GetHomepageURLForImageSearch();
-  } else if (base::FeatureList::IsEnabled(lens::features::kLensRegionSearch)) {
-    return lens::features::GetHomepageURLForRegionSearch();
+    return lens::features::GetHomepageURLForLens();
   }
 #endif  // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID)
 
diff --git a/components/security_interstitials/content/insecure_form_navigation_throttle.cc b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
index 111af0d0..a1edb4dd 100644
--- a/components/security_interstitials/content/insecure_form_navigation_throttle.cc
+++ b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
@@ -96,9 +96,8 @@
 
   // Do not set special error page HTML for insecure forms in subframes; those
   // are already hard blocked.
-  if (!handle->IsInMainFrame()) {
+  if (handle->GetParentFrameOrOuterDocument())
     return content::NavigationThrottle::PROCEED;
-  }
 
   url::Origin form_originating_origin =
       handle->GetInitiatorOrigin().value_or(url::Origin());
diff --git a/components/security_interstitials/content/security_interstitial_tab_helper.cc b/components/security_interstitials/content/security_interstitial_tab_helper.cc
index a996c61..2d040dc 100644
--- a/components/security_interstitials/content/security_interstitial_tab_helper.cc
+++ b/components/security_interstitials/content/security_interstitial_tab_helper.cc
@@ -54,8 +54,8 @@
     content::NavigationHandle* navigation_handle,
     std::unique_ptr<security_interstitials::SecurityInterstitialPage>
         blocking_page) {
-  // An interstitial should not be shown in a prerendered page. The prerender
-  // should just be canceled.
+  // An interstitial should not be shown in a prerendered page or in a fenced
+  // frame. The prerender should just be canceled.
   DCHECK(navigation_handle->IsInPrimaryMainFrame());
 
   // CreateForWebContents() creates a tab helper if it doesn't yet exist for the
diff --git a/components/segmentation_platform/components_unittests.filter b/components/segmentation_platform/components_unittests.filter
index c7b8036..7509640b 100644
--- a/components/segmentation_platform/components_unittests.filter
+++ b/components/segmentation_platform/components_unittests.filter
@@ -9,6 +9,7 @@
 ModelExecutionManagerFactoryTest.*
 ModelExecutionManagerTest.*
 ModelExecutionSchedulerTest.*
+OptimizationGuideSegmentationModelProviderTest.*
 SegmentationModelExecutorTest.*
 SegmentationPlatformDummyUkmManagerTest.*
 SegmentationPlatformServiceImplEmptyConfigTest.*
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index 741e6bd..062d542f 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -79,6 +79,8 @@
     "segmentation_platform_service_impl.h",
     "segmentation_ukm_helper.cc",
     "segmentation_ukm_helper.h",
+    "selection/segment_result_provider.cc",
+    "selection/segment_result_provider.h",
     "selection/segment_score_provider.cc",
     "selection/segment_score_provider.h",
     "selection/segment_selector.h",
@@ -117,6 +119,7 @@
     "//components/history/core/browser:browser",
     "//components/keyed_service/core",
     "//components/leveldb_proto",
+    "//components/optimization_guide:machine_learning_tflite_buildflags",
     "//components/prefs",
     "//components/segmentation_platform/internal/proto",
     "//components/segmentation_platform/public",
@@ -127,25 +130,15 @@
     "//url:url",
   ]
 
-  public_deps = [
-    "//components/optimization_guide/core",
-    "//components/optimization_guide/proto:optimization_guide_proto",
-  ]
+  public_deps =
+      [ "//components/optimization_guide/proto:optimization_guide_proto" ]
 
   if (build_with_tflite_lib) {
     sources += [
       "execution/model_execution_manager_impl.cc",
       "execution/model_execution_manager_impl.h",
-      "execution/segmentation_model_executor.cc",
-      "execution/segmentation_model_executor.h",
-      "execution/segmentation_model_handler.cc",
-      "execution/segmentation_model_handler.h",
     ]
-
-    deps += [
-      "//third_party/tflite:tflite_public_headers",
-      "//third_party/tflite_support",
-    ]
+    deps += [ "//third_party/tflite:tflite_public_headers" ]
   }
 
   if (is_android) {
@@ -158,6 +151,26 @@
   }
 }
 
+if (build_with_tflite_lib) {
+  static_library("optimization_guide_segmentation_handler") {
+    sources = [
+      "execution/optimization_guide/optimization_guide_segmentation_model_handler.cc",
+      "execution/optimization_guide/optimization_guide_segmentation_model_handler.h",
+      "execution/optimization_guide/optimization_guide_segmentation_model_provider.cc",
+      "execution/optimization_guide/optimization_guide_segmentation_model_provider.h",
+      "execution/optimization_guide/segmentation_model_executor.cc",
+      "execution/optimization_guide/segmentation_model_executor.h",
+    ]
+    deps = [
+      ":internal",
+      "//base",
+      "//components/optimization_guide/core",
+      "//components/segmentation_platform/internal/proto",
+      "//components/segmentation_platform/public",
+    ]
+  }
+}
+
 source_set("unit_tests") {
   testonly = true
 
@@ -195,7 +208,10 @@
     "execution/mock_feature_aggregator.h",
     "execution/mock_feature_list_query_processor.cc",
     "execution/mock_feature_list_query_processor.h",
+    "execution/mock_model_provider.cc",
+    "execution/mock_model_provider.h",
     "execution/model_execution_manager_factory_unittest.cc",
+    "execution/query_processor.h",
     "mock_ukm_data_manager.cc",
     "mock_ukm_data_manager.h",
     "scheduler/model_execution_scheduler_unittest.cc",
@@ -223,6 +239,7 @@
     "//base/test:test_support",
     "//components/history/core/browser:browser",
     "//components/leveldb_proto:test_support",
+    "//components/optimization_guide/core",
     "//components/optimization_guide/core:test_support",
     "//components/prefs",
     "//components/prefs:test_support",
@@ -243,9 +260,13 @@
     # tests in //components/segmentation_platform/components_unittests.filter
     sources += [
       "execution/model_execution_manager_impl_unittest.cc",
-      "execution/segmentation_model_executor_unittest.cc",
+      "execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc",
+      "execution/optimization_guide/segmentation_model_executor_unittest.cc",
     ]
-    deps += [ ":unit_tests_bundle_data" ]
+    deps += [
+      ":optimization_guide_segmentation_handler",
+      ":unit_tests_bundle_data",
+    ]
   }
 }
 
diff --git a/components/segmentation_platform/internal/execution/custom_input_processor.cc b/components/segmentation_platform/internal/execution/custom_input_processor.cc
index 09925052..09a20c3 100644
--- a/components/segmentation_platform/internal/execution/custom_input_processor.cc
+++ b/components/segmentation_platform/internal/execution/custom_input_processor.cc
@@ -4,6 +4,7 @@
 
 #include "components/segmentation_platform/internal/execution/custom_input_processor.h"
 
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/feature_processor_state.h"
diff --git a/components/segmentation_platform/internal/execution/feature_processor_state.cc b/components/segmentation_platform/internal/execution/feature_processor_state.cc
index a9e3ea9..e794fd6 100644
--- a/components/segmentation_platform/internal/execution/feature_processor_state.cc
+++ b/components/segmentation_platform/internal/execution/feature_processor_state.cc
@@ -4,6 +4,7 @@
 
 #include "components/segmentation_platform/internal/execution/feature_processor_state.h"
 
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 
 namespace segmentation_platform {
diff --git a/components/segmentation_platform/internal/execution/mock_model_provider.cc b/components/segmentation_platform/internal/execution/mock_model_provider.cc
new file mode 100644
index 0000000..23efe60
--- /dev/null
+++ b/components/segmentation_platform/internal/execution/mock_model_provider.cc
@@ -0,0 +1,57 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
+
+#include <utility>
+#include "base/callback.h"
+
+namespace segmentation_platform {
+
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+
+// Stores the client callbacks to |data|.
+void StoreClientCallback(
+    optimization_guide::proto::OptimizationTarget segment_id,
+    TestModelProviderFactory::Data* data,
+    const ModelProvider::ModelUpdatedCallback& model_updated_callback) {
+  data->model_providers_callbacks.emplace(
+      std::make_pair(segment_id, model_updated_callback));
+}
+
+}  // namespace
+
+MockModelProvider::MockModelProvider(
+    optimization_guide::proto::OptimizationTarget segment_id,
+    base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)>
+        get_client_callback)
+    : ModelProvider(segment_id), get_client_callback_(get_client_callback) {
+  ON_CALL(*this, InitAndFetchModel(_))
+      .WillByDefault(
+          Invoke([&](const ModelUpdatedCallback& model_updated_callback) {
+            get_client_callback_.Run(model_updated_callback);
+          }));
+}
+MockModelProvider::~MockModelProvider() = default;
+
+TestModelProviderFactory::Data::Data() = default;
+TestModelProviderFactory::Data::~Data() = default;
+
+std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateProvider(
+    optimization_guide::proto::OptimizationTarget segment_id) {
+  auto provider = std::make_unique<MockModelProvider>(
+      segment_id, base::BindRepeating(&StoreClientCallback, segment_id, data_));
+  data_->model_providers.emplace(std::make_pair(segment_id, provider.get()));
+  return provider;
+}
+
+std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateDefaultProvider(
+    optimization_guide::proto::OptimizationTarget) {
+  return nullptr;
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/mock_model_provider.h b/components/segmentation_platform/internal/execution/mock_model_provider.h
new file mode 100644
index 0000000..7bf0ce6
--- /dev/null
+++ b/components/segmentation_platform/internal/execution/mock_model_provider.h
@@ -0,0 +1,83 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_MODEL_PROVIDER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_MODEL_PROVIDER_H_
+
+#include <map>
+#include <memory>
+#include "base/memory/raw_ptr.h"
+#include "components/segmentation_platform/public/model_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace segmentation_platform {
+
+// Mock model provider for testing, to be used with TestModelProviderFactory.
+class MockModelProvider : public ModelProvider {
+ public:
+  MockModelProvider(
+      optimization_guide::proto::OptimizationTarget segment_id,
+      base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)>
+          get_client_callback);
+  ~MockModelProvider() override;
+
+  MOCK_METHOD(void,
+              ExecuteModelWithInput,
+              (const std::vector<float>& input,
+               base::OnceCallback<void(const absl::optional<float>&)> callback),
+              (override));
+
+  MOCK_METHOD(void,
+              InitAndFetchModel,
+              (const ModelUpdatedCallback& model_updated_callback),
+              (override));
+
+  MOCK_METHOD(bool, ModelAvailable, (), (override));
+
+ private:
+  base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)>
+      get_client_callback_;
+};
+
+// Test factory for providers, keeps track of model requests, but does not run
+// the model callbacks.
+class TestModelProviderFactory : public ModelProviderFactory {
+ public:
+  // List of model requests given to the providers.
+  struct Data {
+    Data();
+    ~Data();
+
+    // Map of targets to model providers, added when provider is created. The
+    // list is not cleared when providers are destroyed.
+    std::map<optimization_guide::proto::OptimizationTarget, MockModelProvider*>
+        model_providers;
+    // Map from target to updated callback, recorded when InitAndFetchModel()
+    // was called on any provider.
+    std::map<optimization_guide::proto::OptimizationTarget,
+             ModelProvider::ModelUpdatedCallback>
+        model_providers_callbacks;
+  };
+
+  // Records requests to `data`. `data` is not owned, and the caller must ensure
+  // it is valid when the factory or provider is in use. Note that providers can
+  // live longer than factory.
+  explicit TestModelProviderFactory(Data* data) : data_(data) {}
+
+  // ModelProviderFactory impl, that keeps track of the created provider and
+  // callbacks in |data_|.
+  std::unique_ptr<ModelProvider> CreateProvider(
+      optimization_guide::proto::OptimizationTarget segment_id) override;
+
+  std::unique_ptr<ModelProvider> CreateDefaultProvider(
+      optimization_guide::proto::OptimizationTarget) override;
+
+ private:
+  raw_ptr<Data> data_;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MOCK_MODEL_PROVIDER_H_
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc b/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc
index 17979f0..cd30339 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_factory.cc
@@ -11,74 +11,34 @@
 #include "base/task/sequenced_task_runner.h"
 #include "components/optimization_guide/machine_learning_tflite_buildflags.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
+#include "components/segmentation_platform/public/model_provider.h"
 
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
-#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h"
 #else
 #include "components/segmentation_platform/internal/execution/dummy_model_execution_manager.h"
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
-namespace base {
-class Clock;
-}  // namespace base
-
-namespace optimization_guide {
-class OptimizationGuideModelProvider;
-}  // namespace optimization_guide
-
 namespace segmentation_platform {
-class SegmentInfoDatabase;
-class SignalDatabase;
-
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
-
-const char kSegmentationModelMetadataTypeUrl[] =
-    "type.googleapis.com/"
-    "google.internal.chrome.optimizationguide.v1.SegmentationModelMetadata";
-
-// CreateModelHandler makes it possible to pass in any creator of the
-// SegmentationModelHandler, which makes it possible to create mock versions.
-std::unique_ptr<SegmentationModelHandler> CreateModelHandler(
-    optimization_guide::OptimizationGuideModelProvider* model_provider,
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    optimization_guide::proto::OptimizationTarget optimization_target,
-    const SegmentationModelHandler::ModelUpdatedCallback&
-        model_updated_callback) {
-  // Preparing the version data to be sent to server along with the request to
-  // download the model.
-  optimization_guide::proto::Any any_metadata;
-  any_metadata.set_type_url(kSegmentationModelMetadataTypeUrl);
-  proto::SegmentationModelMetadata model_metadata;
-  proto::VersionInfo* version_info = model_metadata.mutable_version_info();
-  version_info->set_metadata_cur_version(
-      proto::CurrentVersion::METADATA_VERSION);
-  model_metadata.SerializeToString(any_metadata.mutable_value());
-  return std::make_unique<SegmentationModelHandler>(
-      model_provider, background_task_runner, optimization_target,
-      model_updated_callback, std::move(any_metadata));
-}
-#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
 std::unique_ptr<ModelExecutionManager> CreateModelExecutionManager(
-    optimization_guide::OptimizationGuideModelProvider* model_provider,
+    ModelProviderFactory* model_provider_factory,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids,
+    const base::flat_set<optimization_guide::proto::OptimizationTarget>&
+        segment_ids,
     base::Clock* clock,
     SegmentInfoDatabase* segment_database,
     SignalDatabase* signal_database,
     FeatureListQueryProcessor* feature_list_query_processor,
     const ModelExecutionManager::SegmentationModelUpdatedCallback&
         model_updated_callback) {
+  // TODO(ssid): The execution manager should always be created, since it can
+  // run default models.
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   return std::make_unique<ModelExecutionManagerImpl>(
-      segment_ids,
-      base::BindRepeating(&CreateModelHandler, model_provider,
-                          background_task_runner),
-      clock, segment_database, signal_database, feature_list_query_processor,
-      model_updated_callback);
+      segment_ids, model_provider_factory, clock, segment_database,
+      signal_database, feature_list_query_processor, model_updated_callback);
 #else
   return std::make_unique<DummyModelExecutionManager>();
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_factory.h b/components/segmentation_platform/internal/execution/model_execution_manager_factory.h
index 1000adc..5c4c0cb 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_factory.h
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_factory.h
@@ -17,12 +17,9 @@
 class Clock;
 }  // namespace base
 
-namespace optimization_guide {
-class OptimizationGuideModelProvider;
-}  // namespace optimization_guide
-
 namespace segmentation_platform {
 class FeatureListQueryProcessor;
+class ModelProviderFactory;
 class SegmentInfoDatabase;
 class SignalDatabase;
 
@@ -31,9 +28,10 @@
 // BUILDFLAG(BUILD_WITH_TFLITE_LIB) is not set, in case of the full
 // implementation provided by ModelExecutionManagerImpl.
 std::unique_ptr<ModelExecutionManager> CreateModelExecutionManager(
-    optimization_guide::OptimizationGuideModelProvider* model_provider,
+    ModelProviderFactory* model_provider_factory,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids,
+    const base::flat_set<optimization_guide::proto::OptimizationTarget>&
+        segment_ids,
     base::Clock* clock,
     SegmentInfoDatabase* segment_database,
     SignalDatabase* signal_database,
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc
index e2ed325..c7de750 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_factory_unittest.cc
@@ -13,25 +13,24 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/mock_signal_database.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/feature_aggregator_impl.h"
 #include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace segmentation_platform {
+
 class ModelExecutionManagerFactoryTest : public testing::Test {
  public:
   ModelExecutionManagerFactoryTest() = default;
   ~ModelExecutionManagerFactoryTest() override = default;
 
   void SetUp() override {
-    optimization_guide_model_provider_ = std::make_unique<
-        optimization_guide::TestOptimizationGuideModelProvider>();
     segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
     feature_list_query_processor_ = std::make_unique<FeatureListQueryProcessor>(
         &mock_signal_database_, std::make_unique<FeatureAggregatorImpl>());
@@ -45,8 +44,6 @@
   }
 
   base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider>
-      optimization_guide_model_provider_;
   base::SimpleTestClock test_clock_;
   std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
   MockSignalDatabase mock_signal_database_;
@@ -54,15 +51,29 @@
 };
 
 TEST_F(ModelExecutionManagerFactoryTest, CreateModelExecutionManager) {
+  TestModelProviderFactory::Data data;
+  TestModelProviderFactory factory(&data);
   auto model_execution_manager = CreateModelExecutionManager(
-      optimization_guide_model_provider_.get(),
-      task_environment_.GetMainThreadTaskRunner(),
+      &factory, task_environment_.GetMainThreadTaskRunner(),
       {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB},
       &test_clock_, segment_database_.get(), &mock_signal_database_,
       feature_list_query_processor_.get(), base::DoNothing());
   // This should work regardless of whether a DummyModelExecutionManager or
   // ModelExecutionManagerImpl is returned.
-  CHECK(model_execution_manager);
+  ASSERT_TRUE(model_execution_manager);
+
+  // Executing model with any provider should not crash.
+  base::RunLoop wait_for_execution;
+  proto::SegmentInfo segment_info;
+  segment_info.set_segment_id(
+      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  model_execution_manager->ExecuteModel(
+      segment_info,
+      base::BindOnce(
+          [](base::RepeatingClosure quit,
+             const std::pair<float, ModelExecutionStatus>&) { quit.Run(); },
+          wait_for_execution.QuitClosure()));
+  wait_for_execution.Run();
 }
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
index bce51f6..2318c6c2 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/location.h"
+#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -24,13 +25,13 @@
 #include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
-#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h"
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
 #include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
 #include "components/segmentation_platform/internal/stats.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/perfetto/include/perfetto/tracing/track.h"
 
@@ -74,7 +75,8 @@
   std::unique_ptr<ModelExecutionTraceEvent> trace_event;
 
   OptimizationTarget segment_id;
-  raw_ptr<SegmentationModelHandler> model_handler = nullptr;
+  int64_t model_version = 0;
+  raw_ptr<ModelProvider> model_provider = nullptr;
   ModelExecutionCallback callback;
   std::vector<float> input_tensor;
   base::Time total_execution_start_time;
@@ -97,7 +99,7 @@
 
 ModelExecutionManagerImpl::ModelExecutionManagerImpl(
     const base::flat_set<OptimizationTarget>& segment_ids,
-    ModelHandlerCreator model_handler_creator,
+    ModelProviderFactory* model_provider_factory,
     base::Clock* clock,
     SegmentInfoDatabase* segment_database,
     SignalDatabase* signal_database,
@@ -109,13 +111,12 @@
       model_updated_callback_(model_updated_callback) {
   feature_list_query_processor_ = feature_list_query_processor;
   for (OptimizationTarget segment_id : segment_ids) {
-    model_handlers_.emplace(std::make_pair(
-        segment_id,
-        model_handler_creator.Run(
-            segment_id,
-            base::BindRepeating(
-                &ModelExecutionManagerImpl::OnSegmentationModelUpdated,
-                weak_ptr_factory_.GetWeakPtr()))));
+    std::unique_ptr<ModelProvider> provider =
+        model_provider_factory->CreateProvider(segment_id);
+    provider->InitAndFetchModel(base::BindRepeating(
+        &ModelExecutionManagerImpl::OnSegmentationModelUpdated,
+        weak_ptr_factory_.GetWeakPtr()));
+    model_providers_.emplace(std::make_pair(segment_id, std::move(provider)));
   }
 }
 
@@ -125,23 +126,23 @@
     const proto::SegmentInfo& segment_info,
     ModelExecutionCallback callback) {
   OptimizationTarget segment_id = segment_info.segment_id();
-  auto model_handler_it = model_handlers_.find(segment_id);
-  DCHECK(model_handler_it != model_handlers_.end());
+  auto model_provider_it = model_providers_.find(segment_id);
+  DCHECK(model_provider_it != model_providers_.end());
 
-  SegmentationModelHandler& handler = *model_handler_it->second;
+  ModelProvider& provider = *model_provider_it->second;
 
   // Create an ExecutionState that will stay with this request until it has been
   // fully processed.
   auto state = std::make_unique<ExecutionState>();
   state->segment_id = segment_id;
-  state->model_handler = &handler;
+  state->model_provider = &provider;
   state->callback = std::move(callback);
   state->total_execution_start_time = clock_->Now();
 
   ModelExecutionTraceEvent trace_event(
       "ModelExecutionManagerImpl::ExecuteModel", *state);
 
-  if (!handler.ModelAvailable()) {
+  if (!provider.ModelAvailable()) {
     RunModelExecutionCallback(std::move(state), 0,
                               ModelExecutionStatus::kSkippedModelNotReady);
     return;
@@ -155,6 +156,7 @@
     return;
   }
 
+  state->model_version = segment_info.model_version();
   feature_list_query_processor_->ProcessFeatureList(
       segment_info.model_metadata(), segment_id, clock_->Now(),
       base::BindOnce(
@@ -182,10 +184,10 @@
     std::unique_ptr<ExecutionState> state) {
   ModelExecutionTraceEvent trace_event(
       "ModelExecutionManagerImpl::ExecuteModel", *state);
-  auto it = model_handlers_.find(state->segment_id);
-  DCHECK(it != model_handlers_.end());
+  auto it = model_providers_.find(state->segment_id);
+  DCHECK(it != model_providers_.end());
 
-  SegmentationModelHandler* handler = (*it).second.get();
+  ModelProvider* handler = (*it).second.get();
 
   if (VLOG_IS_ON(1)) {
     std::stringstream log_input;
@@ -201,9 +203,9 @@
                                               const_input_tensor);
   state->model_execution_start_time = clock_->Now();
   handler->ExecuteModelWithInput(
+      const_input_tensor,
       base::BindOnce(&ModelExecutionManagerImpl::OnModelExecutionComplete,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(state)),
-      const_input_tensor);
+                     weak_ptr_factory_.GetWeakPtr(), std::move(state)));
 }
 
 void ModelExecutionManagerImpl::OnModelExecutionComplete(
@@ -219,10 +221,10 @@
             << optimization_guide::proto::OptimizationTarget_Name(
                    state->segment_id);
     stats::RecordModelExecutionResult(state->segment_id, result.value());
-    if (state->model_handler->GetModelInfo()) {
+    if (state->model_version) {
       SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
-          state->segment_id, state->model_handler->GetModelInfo()->GetVersion(),
-          state->input_tensor, result.value());
+          state->segment_id, state->model_version, state->input_tensor,
+          result.value());
     }
     RunModelExecutionCallback(std::move(state), *result,
                               ModelExecutionStatus::kSuccess);
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.h b/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
index bce08664..957140d 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
@@ -15,12 +15,10 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/sequenced_task_runner.h"
-#include "components/optimization_guide/core/model_executor.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
-#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h"
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -30,6 +28,8 @@
 
 namespace segmentation_platform {
 class FeatureListQueryProcessor;
+class ModelProvider;
+class ModelProviderFactory;
 class SignalDatabase;
 
 namespace proto {
@@ -51,14 +51,9 @@
 // so the SegmentationModelHandler instances can be created early.
 class ModelExecutionManagerImpl : public ModelExecutionManager {
  public:
-  using ModelHandlerCreator =
-      base::RepeatingCallback<std::unique_ptr<SegmentationModelHandler>(
-          optimization_guide::proto::OptimizationTarget,
-          const SegmentationModelHandler::ModelUpdatedCallback&)>;
-
   ModelExecutionManagerImpl(
       const base::flat_set<OptimizationTarget>& segment_ids,
-      ModelHandlerCreator model_handler_creator,
+      ModelProviderFactory* model_provider_factory,
       base::Clock* clock,
       SegmentInfoDatabase* segment_database,
       SignalDatabase* signal_database,
@@ -135,8 +130,8 @@
                                   bool success);
 
   // All the relevant handlers for each of the segments.
-  std::map<OptimizationTarget, std::unique_ptr<SegmentationModelHandler>>
-      model_handlers_;
+  // TODO(ssid): Move the ownership of providers outside this class.
+  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> model_providers_;
 
   // Used to access the current time.
   raw_ptr<base::Clock> clock_;
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
index e4f8356..2b197ca9 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
@@ -18,24 +18,25 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/mock_signal_database.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
-#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h"
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::base::test::RunOnceCallback;
 using testing::_;
+using testing::Invoke;
 using testing::Return;
 using testing::SaveArg;
 using testing::SetArgReferee;
@@ -77,29 +78,6 @@
               (override));
 };
 
-class MockSegmentationModelHandler : public SegmentationModelHandler {
- public:
-  MockSegmentationModelHandler(
-      optimization_guide::OptimizationGuideModelProvider* model_provider,
-      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      optimization_guide::proto::OptimizationTarget optimization_target,
-      const SegmentationModelHandler::ModelUpdatedCallback&
-          model_updated_callback)
-      : SegmentationModelHandler(model_provider,
-                                 background_task_runner,
-                                 optimization_target,
-                                 model_updated_callback,
-                                 absl::nullopt) {}
-
-  MOCK_METHOD(void,
-              ExecuteModelWithInput,
-              (base::OnceCallback<void(const absl::optional<float>&)> callback,
-               const std::vector<float>& input),
-              (override));
-
-  MOCK_METHOD(bool, ModelAvailable, (), (const override));
-};
-
 // TODO(ssid): Use mock_feature_list_query_processor.h.
 class MockFeatureListQueryProcessor : public FeatureListQueryProcessor {
  public:
@@ -119,12 +97,11 @@
 
 class ModelExecutionManagerTest : public testing::Test {
  public:
-  ModelExecutionManagerTest() = default;
+  ModelExecutionManagerTest()
+      : model_provider_factory_(&model_provider_data_) {}
   ~ModelExecutionManagerTest() override = default;
 
   void SetUp() override {
-    optimization_guide_model_provider_ = std::make_unique<
-        optimization_guide::TestOptimizationGuideModelProvider>();
     segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
     signal_database_ = std::make_unique<MockSignalDatabase>();
     clock_.SetNow(base::Time::Now());
@@ -132,7 +109,7 @@
 
   void TearDown() override {
     model_execution_manager_.reset();
-    // Allow for the SegmentationModelExecutor owned by SegmentationModelHandler
+    // Allow for the SegmentationModelExecutor owned by ModelProvider
     // to be destroyed.
     RunUntilIdle();
   }
@@ -143,25 +120,8 @@
     feature_list_query_processor_ =
         std::make_unique<MockFeatureListQueryProcessor>();
     model_execution_manager_ = std::make_unique<ModelExecutionManagerImpl>(
-        segment_ids,
-        base::BindRepeating(&ModelExecutionManagerTest::CreateModelHandler,
-                            base::Unretained(this)),
-        &clock_, segment_database_.get(), signal_database_.get(),
-        feature_list_query_processor_.get(), callback);
-  }
-
-  std::unique_ptr<SegmentationModelHandler> CreateModelHandler(
-      optimization_guide::proto::OptimizationTarget segment_id,
-      const SegmentationModelHandler::ModelUpdatedCallback&
-          model_updated_callback) {
-    auto handler = std::make_unique<MockSegmentationModelHandler>(
-        optimization_guide_model_provider_.get(),
-        task_environment_.GetMainThreadTaskRunner(), segment_id,
-        model_updated_callback);
-    model_handlers_.emplace(std::make_pair(segment_id, handler.get()));
-    model_handlers_callbacks_.emplace(
-        std::make_pair(segment_id, model_updated_callback));
-    return handler;
+        segment_ids, &model_provider_factory_, &clock_, segment_database_.get(),
+        signal_database_.get(), feature_list_query_processor_.get(), callback);
   }
 
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
@@ -186,22 +146,21 @@
     std::move(closure).Run();
   }
 
-  MockSegmentationModelHandler& FindHandler(
+  MockModelProvider& FindHandler(
       optimization_guide::proto::OptimizationTarget segment_id) {
-    return *(*model_handlers_.find(segment_id)).second;
+    return *(*model_provider_data_.model_providers.find(segment_id)).second;
   }
 
   base::Time StartTime(base::TimeDelta bucket_duration, int64_t bucket_count) {
     return clock_.Now() - bucket_duration * bucket_count;
   }
 
+ protected:
   base::test::TaskEnvironment task_environment_;
 
-  std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider>
-      optimization_guide_model_provider_;
-  std::map<OptimizationTarget, MockSegmentationModelHandler*> model_handlers_;
-  std::map<OptimizationTarget, SegmentationModelHandler::ModelUpdatedCallback>
-      model_handlers_callbacks_;
+  TestModelProviderFactory::Data model_provider_data_;
+  TestModelProviderFactory model_provider_factory_;
+
   base::SimpleTestClock clock_;
   std::unique_ptr<test::TestSegmentInfoDatabase> segment_database_;
   std::unique_ptr<MockSignalDatabase> signal_database_;
@@ -268,8 +227,8 @@
   // SegmentInfoDatabase, nor invokes the callback.
   EXPECT_CALL(*mock_segment_database_ptr, GetSegmentInfo(_, _)).Times(0);
   EXPECT_CALL(callback, Run(_)).Times(0);
-  model_handlers_callbacks_[segment_id].Run(segment_id, metadata,
-                                            kModelVersion);
+  model_provider_data_.model_providers_callbacks[segment_id].Run(
+      segment_id, metadata, kModelVersion);
 }
 
 TEST_F(ModelExecutionManagerTest, OnSegmentationModelUpdatedNoOldMetadata) {
@@ -284,8 +243,8 @@
   metadata.set_bucket_duration(42u);
   metadata.set_time_unit(proto::TimeUnit::DAY);
   EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&segment_info));
-  model_handlers_callbacks_[segment_id].Run(segment_id, metadata,
-                                            kModelVersion);
+  model_provider_data_.model_providers_callbacks[segment_id].Run(
+      segment_id, metadata, kModelVersion);
 
   // Verify that the resulting callback was invoked correctly.
   EXPECT_EQ(segment_id, segment_info.segment_id());
@@ -367,8 +326,8 @@
   // Invoke the callback and store the resulting invocation of the outer
   // callback for verification.
   EXPECT_CALL(callback, Run(_)).WillOnce(SaveArg<0>(&segment_info));
-  model_handlers_callbacks_[segment_id].Run(segment_id, metadata,
-                                            kModelVersion);
+  model_provider_data_.model_providers_callbacks[segment_id].Run(
+      segment_id, metadata, kModelVersion);
 
   // Should now have the metadata from the new proto.
   EXPECT_EQ(segment_id, segment_info.segment_id());
@@ -462,8 +421,8 @@
   EXPECT_CALL(FindHandler(segment_id), ModelAvailable())
       .WillRepeatedly(Return(true));
   EXPECT_CALL(FindHandler(segment_id),
-              ExecuteModelWithInput(_, std::vector<float>{1, 2, 3, 4, 5, 6, 7}))
-      .WillOnce(RunOnceCallback<0>(absl::make_optional(0.8)));
+              ExecuteModelWithInput(std::vector<float>{1, 2, 3, 4, 5, 6, 7}, _))
+      .WillOnce(RunOnceCallback<1>(absl::make_optional(0.8)));
 
   ExecuteModel(std::make_pair(0.8, ModelExecutionStatus::kSuccess));
 }
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_handler.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
similarity index 75%
rename from components/segmentation_platform/internal/execution/segmentation_model_handler.cc
rename to components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
index bdd16cc..949d6a6 100644
--- a/components/segmentation_platform/internal/execution/segmentation_model_handler.cc
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h"
 
 #include <memory>
 #include <vector>
@@ -10,18 +10,19 @@
 #include "components/optimization_guide/core/model_executor.h"
 #include "components/optimization_guide/proto/common_types.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/execution/segmentation_model_executor.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/stats.h"
 
 namespace segmentation_platform {
 
-SegmentationModelHandler::SegmentationModelHandler(
-    optimization_guide::OptimizationGuideModelProvider* model_provider,
-    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-    optimization_guide::proto::OptimizationTarget optimization_target,
-    const ModelUpdatedCallback& model_updated_callback,
-    absl::optional<optimization_guide::proto::Any>&& model_metadata)
+OptimizationGuideSegmentationModelHandler::
+    OptimizationGuideSegmentationModelHandler(
+        optimization_guide::OptimizationGuideModelProvider* model_provider,
+        scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+        optimization_guide::proto::OptimizationTarget optimization_target,
+        const ModelUpdatedCallback& model_updated_callback,
+        absl::optional<optimization_guide::proto::Any>&& model_metadata)
     : optimization_guide::ModelHandler<float, const std::vector<float>&>(
           model_provider,
           background_task_runner,
@@ -34,9 +35,10 @@
       stats::SegmentationModelAvailability::kModelHandlerCreated);
 }
 
-SegmentationModelHandler::~SegmentationModelHandler() = default;
+OptimizationGuideSegmentationModelHandler::
+    ~OptimizationGuideSegmentationModelHandler() = default;
 
-void SegmentationModelHandler::OnModelUpdated(
+void OptimizationGuideSegmentationModelHandler::OnModelUpdated(
     optimization_guide::proto::OptimizationTarget optimization_target,
     const optimization_guide::ModelInfo& model_info) {
   // First invoke parent to update internal status.
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_handler.h b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
similarity index 71%
rename from components/segmentation_platform/internal/execution/segmentation_model_handler.h
rename to components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
index 4a07b9b3..4eb60ff 100644
--- a/components/segmentation_platform/internal/execution/segmentation_model_handler.h
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_HANDLER_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_HANDLER_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_HANDLER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_HANDLER_H_
 
 #include <memory>
 #include <vector>
@@ -25,7 +25,7 @@
 // its parent class.
 // See documentation for SegmentationModelExecutor for details on the
 // requirements for the ML model and the inputs to execution.
-class SegmentationModelHandler
+class OptimizationGuideSegmentationModelHandler
     : public optimization_guide::ModelHandler<float,
                                               const std::vector<float>&> {
  public:
@@ -34,18 +34,20 @@
       proto::SegmentationModelMetadata,
       int64_t)>;
 
-  explicit SegmentationModelHandler(
+  explicit OptimizationGuideSegmentationModelHandler(
       optimization_guide::OptimizationGuideModelProvider* model_provider,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner,
       optimization_guide::proto::OptimizationTarget optimization_target,
       const ModelUpdatedCallback& model_updated_callback,
       absl::optional<optimization_guide::proto::Any>&& model_metadata);
 
-  ~SegmentationModelHandler() override;
+  ~OptimizationGuideSegmentationModelHandler() override;
 
   // Disallow copy/assign.
-  SegmentationModelHandler(const SegmentationModelHandler&) = delete;
-  SegmentationModelHandler& operator=(const SegmentationModelHandler&) = delete;
+  OptimizationGuideSegmentationModelHandler(
+      const OptimizationGuideSegmentationModelHandler&) = delete;
+  OptimizationGuideSegmentationModelHandler& operator=(
+      const OptimizationGuideSegmentationModelHandler&) = delete;
 
   // optimization_guide::ModelHandler overrides.
   void OnModelUpdated(
@@ -60,4 +62,4 @@
 
 }  // namespace segmentation_platform
 
-#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_HANDLER_H_
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_HANDLER_H_
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
new file mode 100644
index 0000000..be5e59da
--- /dev/null
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.cc
@@ -0,0 +1,80 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/optimization_guide/core/model_executor.h"
+#include "components/optimization_guide/proto/common_types.pb.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/stats.h"
+
+namespace segmentation_platform {
+
+namespace {
+
+const char kSegmentationModelMetadataTypeUrl[] =
+    "type.googleapis.com/"
+    "google.internal.chrome.optimizationguide.v1.SegmentationModelMetadata";
+
+absl::optional<optimization_guide::proto::Any> GetModelFetchConfig() {
+  // Preparing the version data to be sent to server along with the request to
+  // download the model.
+  optimization_guide::proto::Any any_metadata;
+  any_metadata.set_type_url(kSegmentationModelMetadataTypeUrl);
+  proto::SegmentationModelMetadata model_metadata;
+  proto::VersionInfo* version_info = model_metadata.mutable_version_info();
+  version_info->set_metadata_cur_version(
+      proto::CurrentVersion::METADATA_VERSION);
+  model_metadata.SerializeToString(any_metadata.mutable_value());
+  return any_metadata;
+}
+
+}  // namespace
+
+OptimizationGuideSegmentationModelProvider::
+    OptimizationGuideSegmentationModelProvider(
+        optimization_guide::OptimizationGuideModelProvider* model_provider,
+        scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+        optimization_guide::proto::OptimizationTarget optimization_target)
+    : ModelProvider(optimization_target),
+      model_provider_(model_provider),
+      background_task_runner_(background_task_runner) {}
+
+OptimizationGuideSegmentationModelProvider::
+    ~OptimizationGuideSegmentationModelProvider() = default;
+
+void OptimizationGuideSegmentationModelProvider::InitAndFetchModel(
+    const ModelUpdatedCallback& model_updated_callback) {
+  DCHECK(!model_handler_);
+  model_handler_ = std::make_unique<OptimizationGuideSegmentationModelHandler>(
+      model_provider_, background_task_runner_, optimization_target_,
+      model_updated_callback, GetModelFetchConfig());
+}
+
+void OptimizationGuideSegmentationModelProvider::ExecuteModelWithInput(
+    const std::vector<float>& inputs,
+    ExecutionCallback callback) {
+  if (!model_handler_) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
+    return;
+  }
+  model_handler_->ExecuteModelWithInput(std::move(callback), inputs);
+}
+
+bool OptimizationGuideSegmentationModelProvider::ModelAvailable() {
+  if (!model_handler_) {
+    return false;
+  }
+  return model_handler_->ModelAvailable();
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
new file mode 100644
index 0000000..2e7bdee
--- /dev/null
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h
@@ -0,0 +1,60 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_PROVIDER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_PROVIDER_H_
+
+#include <memory>
+#include <vector>
+
+#include "components/optimization_guide/core/model_handler.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
+
+namespace optimization_guide {
+class OptimizationGuideSegmentationModelProvider;
+}  // namespace optimization_guide
+
+namespace segmentation_platform {
+
+class OptimizationGuideSegmentationModelHandler;
+
+// Model provider implementation that uses optimization guide to fetch and
+// execute models.
+class OptimizationGuideSegmentationModelProvider : public ModelProvider {
+ public:
+  OptimizationGuideSegmentationModelProvider(
+      optimization_guide::OptimizationGuideModelProvider* model_provider,
+      scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+      optimization_guide::proto::OptimizationTarget optimization_target);
+
+  ~OptimizationGuideSegmentationModelProvider() override;
+
+  // Disallow copy/assign.
+  OptimizationGuideSegmentationModelProvider(
+      const OptimizationGuideSegmentationModelProvider&) = delete;
+  OptimizationGuideSegmentationModelProvider& operator=(
+      const OptimizationGuideSegmentationModelProvider&) = delete;
+
+  // ModelProvider impl:
+  void InitAndFetchModel(
+      const ModelUpdatedCallback& model_updated_callback) override;
+  void ExecuteModelWithInput(const std::vector<float>& inputs,
+                             ExecutionCallback callback) override;
+  bool ModelAvailable() override;
+
+  OptimizationGuideSegmentationModelHandler& model_handler_for_testing() {
+    return *model_handler_;
+  }
+
+ private:
+  raw_ptr<optimization_guide::OptimizationGuideModelProvider> model_provider_;
+  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
+
+  std::unique_ptr<OptimizationGuideSegmentationModelHandler> model_handler_;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_PROVIDER_H_
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
new file mode 100644
index 0000000..5586081
--- /dev/null
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
+
+#include "base/test/task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/optimization_guide/core/optimization_guide_util.h"
+#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace segmentation_platform {
+
+namespace {
+
+class ModelObserverTracker
+    : public optimization_guide::TestOptimizationGuideModelProvider {
+ public:
+  void AddObserverForOptimizationTargetModel(
+      optimization_guide::proto::OptimizationTarget target,
+      const absl::optional<optimization_guide::proto::Any>& model_metadata,
+      optimization_guide::OptimizationTargetModelObserver* observer) override {
+    registered_model_metadata_.insert_or_assign(target, model_metadata);
+  }
+
+  bool DidRegisterForTarget(
+      optimization_guide::proto::OptimizationTarget target) const {
+    auto it = registered_model_metadata_.find(target);
+    if (it == registered_model_metadata_.end())
+      return false;
+    const auto& model_metadata = registered_model_metadata_.at(target);
+
+    EXPECT_TRUE(model_metadata);
+    absl::optional<proto::SegmentationModelMetadata> metadata =
+        optimization_guide::ParsedAnyMetadata<proto::SegmentationModelMetadata>(
+            model_metadata.value());
+    EXPECT_TRUE(metadata);
+    EXPECT_EQ(metadata->version_info().metadata_cur_version(),
+              proto::CurrentVersion::METADATA_VERSION);
+
+    return true;
+  }
+
+ private:
+  base::flat_map<optimization_guide::proto::OptimizationTarget,
+                 absl::optional<optimization_guide::proto::Any>>
+      registered_model_metadata_;
+};
+
+}  // namespace
+
+class OptimizationGuideSegmentationModelProviderTest : public testing::Test {
+ public:
+  OptimizationGuideSegmentationModelProviderTest() = default;
+  ~OptimizationGuideSegmentationModelProviderTest() override = default;
+
+  void SetUp() override {
+    task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+    model_observer_tracker_ = std::make_unique<ModelObserverTracker>();
+  }
+
+  void TearDown() override {
+    model_observer_tracker_.reset();
+    task_runner_->RunPendingTasks();
+  }
+
+  std::unique_ptr<OptimizationGuideSegmentationModelProvider>
+  CreateModelProvider(optimization_guide::proto::OptimizationTarget target) {
+    return std::make_unique<OptimizationGuideSegmentationModelProvider>(
+        model_observer_tracker_.get(), task_runner_, target);
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+
+  std::unique_ptr<ModelObserverTracker> model_observer_tracker_;
+};
+
+TEST_F(OptimizationGuideSegmentationModelProviderTest, InitAndFetchModel) {
+  std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
+      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
+                              OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+
+  // Not initialized yet.
+  EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+
+  // Init should register observer.
+  provider->InitAndFetchModel(base::DoNothing());
+  EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+
+  // Different target does not register yet.
+  EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+
+  // Initialize voice provider.
+  std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider2 =
+      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
+                              OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+  provider2->InitAndFetchModel(base::DoNothing());
+
+  // 2 observers should be available:
+  EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+
+  EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+}
+
+TEST_F(OptimizationGuideSegmentationModelProviderTest,
+       ExecuteModelWithoutFetch) {
+  std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
+      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
+                              OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+
+  base::RunLoop run_loop;
+  std::vector<float> input = {4, 5};
+  provider->ExecuteModelWithInput(
+      input,
+      base::BindOnce(
+          [](base::RunLoop* run_loop, const absl::optional<float>& output) {
+            EXPECT_FALSE(output.has_value());
+            run_loop->Quit();
+          },
+          &run_loop));
+  run_loop.Run();
+}
+
+TEST_F(OptimizationGuideSegmentationModelProviderTest, ExecuteModelWithFetch) {
+  std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
+      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
+                              OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+  provider->InitAndFetchModel(base::DoNothing());
+  EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+
+  base::RunLoop run_loop;
+  std::vector<float> input = {4, 5};
+  provider->ExecuteModelWithInput(
+      input,
+      base::BindOnce(
+          [](base::RunLoop* run_loop, const absl::optional<float>& output) {
+            // TODO(ssid): Consider using a mock executor to return results.
+            // This failure is caused by not no TFLite model being loaded in the
+            // opt-guide executor.
+            EXPECT_FALSE(output.has_value());
+            run_loop->Quit();
+          },
+          &run_loop));
+  task_runner_->RunPendingTasks();
+  run_loop.Run();
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_executor.cc b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.cc
similarity index 94%
rename from components/segmentation_platform/internal/execution/segmentation_model_executor.cc
rename to components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.cc
index 3b7396d..faaaf0f 100644
--- a/components/segmentation_platform/internal/execution/segmentation_model_executor.cc
+++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/segmentation_platform/internal/execution/segmentation_model_executor.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h"
 
 #include <vector>
 
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_executor.h b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
similarity index 83%
rename from components/segmentation_platform/internal/execution/segmentation_model_executor.h
rename to components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
index 33e885e..02cbdcd 100644
--- a/components/segmentation_platform/internal/execution/segmentation_model_executor.h
+++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_EXECUTOR_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_EXECUTOR_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_EXECUTOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_EXECUTOR_H_
 
 #include <memory>
 #include <vector>
@@ -45,4 +45,4 @@
 
 }  // namespace segmentation_platform
 
-#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_SEGMENTATION_MODEL_EXECUTOR_H_
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_OPTIMIZATION_GUIDE_SEGMENTATION_MODEL_EXECUTOR_H_
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_executor_unittest.cc b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
similarity index 76%
rename from components/segmentation_platform/internal/execution/segmentation_model_executor_unittest.cc
rename to components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
index 5944bbdd..87b8409 100644
--- a/components/segmentation_platform/internal/execution/segmentation_model_executor_unittest.cc
+++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/segmentation_platform/internal/execution/segmentation_model_executor.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h"
 
 #include <memory>
 
@@ -20,9 +20,11 @@
 #include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
 #include "components/optimization_guide/proto/common_types.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/execution/segmentation_model_handler.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h"
+#include "components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_provider.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/model_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -60,26 +62,26 @@
                            .AppendASCII("segmentation_platform")
                            .AppendASCII("adder.tflite");
 
-    optimization_guide_model_provider_ = std::make_unique<
+    optimization_guide_segmentation_model_provider_ = std::make_unique<
         optimization_guide::TestOptimizationGuideModelProvider>();
   }
 
   void TearDown() override { ResetModelExecutor(); }
 
-  void CreateModelExecutor(
-      SegmentationModelHandler::ModelUpdatedCallback callback) {
-    if (model_executor_handle_)
-      model_executor_handle_.reset();
+  void CreateModelExecutor(ModelProvider::ModelUpdatedCallback callback) {
+    if (opt_guide_model_provider_)
+      opt_guide_model_provider_.reset();
 
-    model_executor_handle_ = std::make_unique<SegmentationModelHandler>(
-        optimization_guide_model_provider_.get(),
-        task_environment_.GetMainThreadTaskRunner(), kOptimizationTarget,
-        callback, absl::nullopt);
+    opt_guide_model_provider_ =
+        std::make_unique<OptimizationGuideSegmentationModelProvider>(
+            optimization_guide_segmentation_model_provider_.get(),
+            task_environment_.GetMainThreadTaskRunner(), kOptimizationTarget);
+    opt_guide_model_provider_->InitAndFetchModel(callback);
   }
 
   void ResetModelExecutor() {
-    model_executor_handle_.reset();
-    // Allow for the SegmentationModelExecutor owned by SegmentationModelHandler
+    opt_guide_model_provider_.reset();
+    // Allow for the SegmentationModelExecutor owned by ModelProvider
     // to be destroyed.
     RunUntilIdle();
   }
@@ -101,32 +103,33 @@
           "type.googleapis.com/"
           "segmentation_platform.proto.SegmentationModelMetadata");
     }
-    DCHECK(model_executor_handle_);
+    DCHECK(opt_guide_model_provider_);
 
     auto model_metadata = optimization_guide::TestModelInfoBuilder()
                               .SetModelMetadata(any)
                               .SetModelFilePath(model_file_path_)
                               .SetVersion(kModelVersion)
                               .Build();
-    model_executor_handle_->OnModelUpdated(kOptimizationTarget,
-                                           *model_metadata);
+    opt_guide_model_handler().OnModelUpdated(kOptimizationTarget,
+                                             *model_metadata);
     RunUntilIdle();
   }
 
-  SegmentationModelHandler* model_executor_handle() {
-    return model_executor_handle_.get();
+  OptimizationGuideSegmentationModelHandler& opt_guide_model_handler() {
+    return opt_guide_model_provider_->model_handler_for_testing();
   }
 
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
- private:
+ protected:
   base::test::TaskEnvironment task_environment_;
 
   base::FilePath model_file_path_;
   std::unique_ptr<optimization_guide::TestOptimizationGuideModelProvider>
-      optimization_guide_model_provider_;
+      optimization_guide_segmentation_model_provider_;
 
-  std::unique_ptr<SegmentationModelHandler> model_executor_handle_;
+  std::unique_ptr<OptimizationGuideSegmentationModelProvider>
+      opt_guide_model_provider_;
 };
 
 TEST_F(SegmentationModelExecutorTest, ExecuteWithLoadedModel) {
@@ -154,12 +157,13 @@
   PushModelFileToModelExecutor(metadata);
   model_update_runloop->Run();
 
-  EXPECT_TRUE(model_executor_handle()->ModelAvailable());
+  EXPECT_TRUE(opt_guide_model_handler().ModelAvailable());
 
   std::vector<float> input = {4, 5};
 
   std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-  model_executor_handle()->ExecuteModelWithInput(
+  opt_guide_model_provider_->ExecuteModelWithInput(
+      input,
       base::BindOnce(
           [](base::RunLoop* run_loop, const absl::optional<float>& output) {
             EXPECT_TRUE(output.has_value());
@@ -168,8 +172,7 @@
 
             run_loop->Quit();
           },
-          run_loop.get()),
-      input);
+          run_loop.get()));
   run_loop->Run();
 
   ResetModelExecutor();
@@ -178,7 +181,7 @@
 TEST_F(SegmentationModelExecutorTest, FailToProvideMetadata) {
   std::unique_ptr<base::RunLoop> model_update_runloop =
       std::make_unique<base::RunLoop>();
-  base::MockCallback<SegmentationModelHandler::ModelUpdatedCallback> callback;
+  base::MockCallback<ModelProvider::ModelUpdatedCallback> callback;
   CreateModelExecutor(callback.Get());
   EXPECT_CALL(callback, Run(_, _, _)).Times(0);
 
@@ -187,7 +190,7 @@
   PushModelFileToModelExecutor(absl::nullopt);
   model_update_runloop->RunUntilIdle();
 
-  EXPECT_TRUE(model_executor_handle()->ModelAvailable());
+  EXPECT_TRUE(opt_guide_model_handler().ModelAvailable());
 }
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/execution/sql_feature_processor.cc b/components/segmentation_platform/internal/execution/sql_feature_processor.cc
index 1f9881f..14c50f5b 100644
--- a/components/segmentation_platform/internal/execution/sql_feature_processor.cc
+++ b/components/segmentation_platform/internal/execution/sql_feature_processor.cc
@@ -4,6 +4,7 @@
 
 #include "components/segmentation_platform/internal/execution/sql_feature_processor.h"
 
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/segmentation_platform/internal/execution/feature_processor_state.h"
 
 namespace segmentation_platform {
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index b91ef47..475ff5d 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -43,6 +43,7 @@
 #include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/internal/ukm_data_manager.h"
 #include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/model_provider.h"
 
 using optimization_guide::proto::OptimizationTarget;
 
@@ -57,7 +58,7 @@
 }  // namespace
 
 SegmentationPlatformServiceImpl::SegmentationPlatformServiceImpl(
-    optimization_guide::OptimizationGuideModelProvider* model_provider,
+    std::unique_ptr<ModelProviderFactory> model_provider,
     leveldb_proto::ProtoDatabaseProvider* db_provider,
     const base::FilePath& storage_dir,
     UkmDataManager* ukm_data_manager,
@@ -80,7 +81,7 @@
               storage_dir.Append(kSignalStorageConfigDBName),
               task_runner),
           ukm_data_manager,
-          model_provider,
+          std::move(model_provider),
           pref_service,
           history_service,
           task_runner,
@@ -94,13 +95,13 @@
     std::unique_ptr<leveldb_proto::ProtoDatabase<proto::SignalStorageConfigs>>
         signal_storage_config_db,
     UkmDataManager* ukm_data_manager,
-    optimization_guide::OptimizationGuideModelProvider* model_provider,
+    std::unique_ptr<ModelProviderFactory> model_provider,
     PrefService* pref_service,
     history::HistoryService* history_service,
     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
     base::Clock* clock,
     std::vector<std::unique_ptr<Config>> configs)
-    : model_provider_(model_provider),
+    : model_provider_factory_(std::move(model_provider)),
       task_runner_(task_runner),
       clock_(clock),
       platform_options_(PlatformOptions::CreateDefault()),
@@ -138,7 +139,7 @@
         std::make_unique<SegmentSelectorImpl>(
             segment_info_database_.get(), signal_storage_config_.get(),
             segmentation_result_prefs_.get(), config.get(), clock,
-            platform_options_);
+            platform_options_, model_provider_factory_.get());
   }
 
   proxy_ = std::make_unique<ServiceProxyImpl>(segment_info_database_.get(),
@@ -250,7 +251,7 @@
   training_data_collector_->OnServiceInitialized();
 
   model_execution_manager_ = CreateModelExecutionManager(
-      model_provider_, task_runner_, all_segment_ids_, clock_,
+      model_provider_factory_.get(), task_runner_, all_segment_ids_, clock_,
       segment_info_database_.get(), signal_database_.get(),
       feature_list_query_processor_.get(),
       base::BindRepeating(
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
index 46fd76d8..8bec06f 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.h
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
@@ -35,10 +35,6 @@
 class ProtoDatabaseProvider;
 }  // namespace leveldb_proto
 
-namespace optimization_guide {
-class OptimizationGuideModelProvider;
-}  // namespace optimization_guide
-
 class PrefService;
 
 namespace segmentation_platform {
@@ -56,6 +52,7 @@
 class HistoryServiceObserver;
 class ModelExecutionManager;
 class ModelExecutionSchedulerImpl;
+class ModelProviderFactory;
 class SegmentationResultPrefs;
 class SegmentInfoDatabase;
 class SegmentSelectorImpl;
@@ -87,7 +84,7 @@
 class SegmentationPlatformServiceImpl : public SegmentationPlatformService {
  public:
   SegmentationPlatformServiceImpl(
-      optimization_guide::OptimizationGuideModelProvider* model_provider,
+      std::unique_ptr<ModelProviderFactory> model_provider,
       leveldb_proto::ProtoDatabaseProvider* db_provider,
       const base::FilePath& storage_dir,
       UkmDataManager* ukm_data_manager,
@@ -106,7 +103,7 @@
       std::unique_ptr<leveldb_proto::ProtoDatabase<proto::SignalStorageConfigs>>
           signal_storage_config_db,
       UkmDataManager* ukm_data_manager,
-      optimization_guide::OptimizationGuideModelProvider* model_provider,
+      std::unique_ptr<ModelProviderFactory> model_provider,
       PrefService* pref_service,
       history::HistoryService* history_service,
       const scoped_refptr<base::SequencedTaskRunner>& task_runner,
@@ -147,7 +144,8 @@
   // Called when service status changes.
   void OnServiceStatusChanged();
 
-  raw_ptr<optimization_guide::OptimizationGuideModelProvider> model_provider_;
+  std::unique_ptr<ModelProviderFactory> model_provider_factory_;
+
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   raw_ptr<base::Clock> clock_;
   const PlatformOptions platform_options_;
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
index 3320ad0..65e0889 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
@@ -20,7 +20,6 @@
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/leveldb_proto/public/shared_proto_database_client_list.h"
 #include "components/leveldb_proto/testing/fake_db.h"
-#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
 #include "components/optimization_guide/machine_learning_tflite_buildflags.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -31,8 +30,7 @@
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/dummy_ukm_data_manager.h"
 #include "components/segmentation_platform/internal/execution/feature_aggregator_impl.h"
-#include "components/segmentation_platform/internal/execution/model_execution_manager.h"
-#include "components/segmentation_platform/internal/execution/model_execution_manager_factory.h"
+#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/proto/signal.pb.h"
@@ -48,11 +46,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
-#include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
-#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB
-
 using ::testing::_;
+using ::testing::Invoke;
 
 namespace segmentation_platform {
 namespace {
@@ -103,8 +98,6 @@
   return configs;
 }
 
-}  // namespace
-
 // A mock of the ServiceProxy::Observer.
 class MockServiceProxyObserver : public ServiceProxy::Observer {
  public:
@@ -118,6 +111,8 @@
               (override));
 };
 
+}  // namespace
+
 class SegmentationPlatformServiceImplTest : public testing::Test {
  public:
   explicit SegmentationPlatformServiceImplTest(
@@ -158,8 +153,9 @@
         std::make_unique<SegmentationPlatformServiceImpl>(
             std::move(segment_db), std::move(signal_db),
             std::move(segment_storage_config_db), ukm_data_manager_.get(),
-            &model_provider_, &pref_service_, /*history_service=*/nullptr,
-            task_runner_, &test_clock_, std::move(configs));
+            std::make_unique<TestModelProviderFactory>(&model_provider_data_),
+            &pref_service_, /*history_service=*/nullptr, task_runner_,
+            &test_clock_, std::move(configs));
     segmentation_platform_service_impl_->GetServiceProxy()->AddObserver(
         &observer_);
   }
@@ -243,11 +239,6 @@
     // ModelExecutionManagerImpl is publishing the correct data and whether that
     // leads to the SegmentationPlatformServiceImpl doing the right thing.
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
-    ModelExecutionManagerImpl* mem_impl =
-        static_cast<ModelExecutionManagerImpl*>(
-            segmentation_platform_service_impl_->model_execution_manager_
-                .get());
-
     base::HistogramTester histogram_tester;
     proto::SegmentationModelMetadata metadata;
     metadata.set_time_unit(proto::TimeUnit::DAY);
@@ -266,9 +257,13 @@
     // been updated and every time at startup. This will first read the old info
     // from the database, and then write the merged result of the old and new to
     // the database.
-    mem_impl->OnSegmentationModelUpdated(
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, metadata,
-        kModelVersion);
+    ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count(
+        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+    model_provider_data_
+        .model_providers_callbacks
+            [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE]
+        .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+             metadata, kModelVersion);
     segment_db_->GetCallback(true);
     segment_db_->UpdateCallback(true);
 
@@ -301,9 +296,13 @@
     task_environment_.RunUntilIdle();
     segment_db_->LoadCallback(true);
 
-    mem_impl->OnSegmentationModelUpdated(
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE, metadata,
-        kModelVersion);
+    ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count(
+        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+    model_provider_data_
+        .model_providers_callbacks
+            [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE]
+        .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
+             metadata, kModelVersion);
     segment_db_->GetCallback(true);
     segment_db_->UpdateCallback(true);
 
@@ -351,7 +350,7 @@
   raw_ptr<leveldb_proto::test::FakeDB<proto::SignalData>> signal_db_;
   raw_ptr<leveldb_proto::test::FakeDB<proto::SignalStorageConfigs>>
       segment_storage_config_db_;
-  optimization_guide::TestOptimizationGuideModelProvider model_provider_;
+  TestModelProviderFactory::Data model_provider_data_;
   TestingPrefServiceSimple pref_service_;
   base::SimpleTestClock test_clock_;
   std::unique_ptr<UkmDataManager> ukm_data_manager_;
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc
new file mode 100644
index 0000000..f8cec69
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -0,0 +1,148 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+
+namespace segmentation_platform {
+namespace {
+
+class SegmentResultProviderImpl : public SegmentResultProvider {
+ public:
+  SegmentResultProviderImpl(SegmentInfoDatabase* segment_database,
+                            SignalStorageConfig* signal_storage_config,
+                            ModelProviderFactory* model_provider_factory,
+                            base::Clock* clock,
+                            bool force_refresh_results)
+      : segment_database_(segment_database),
+        signal_storage_config_(signal_storage_config),
+        model_provider_factory_(model_provider_factory),
+        clock_(clock),
+        force_refresh_results_(force_refresh_results),
+        task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+
+  void GetSegmentResult(OptimizationTarget segment_id,
+                        const std::string& segmentation_key,
+                        SegmentResultCallback callback) override;
+
+  SegmentResultProviderImpl(SegmentResultProviderImpl&) = delete;
+  SegmentResultProviderImpl& operator=(SegmentResultProviderImpl&) = delete;
+
+ private:
+  void OnGetSegmentInfo(OptimizationTarget segment_id,
+                        const std::string& segmentation_key,
+                        SegmentResultCallback callback,
+                        absl::optional<proto::SegmentInfo> available_segment);
+
+  const raw_ptr<SegmentInfoDatabase> segment_database_;
+  const raw_ptr<SignalStorageConfig> signal_storage_config_;
+  const raw_ptr<ModelProviderFactory> model_provider_factory_;
+  const raw_ptr<base::Clock> clock_;
+  const bool force_refresh_results_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  base::WeakPtrFactory<SegmentResultProviderImpl> weak_ptr_factory_{this};
+};
+
+void SegmentResultProviderImpl::GetSegmentResult(
+    OptimizationTarget segment_id,
+    const std::string& segmentation_key,
+    SegmentResultCallback callback) {
+  segment_database_->GetSegmentInfo(
+      segment_id, base::BindOnce(&SegmentResultProviderImpl::OnGetSegmentInfo,
+                                 weak_ptr_factory_.GetWeakPtr(), segment_id,
+                                 segmentation_key, std::move(callback)));
+}
+
+void SegmentResultProviderImpl::OnGetSegmentInfo(
+    OptimizationTarget segment_id,
+    const std::string& segmentation_key,
+    SegmentResultCallback callback,
+    absl::optional<proto::SegmentInfo> available_segment) {
+  // Don't compute results if we don't have enough signals, or don't have
+  // valid unexpired results for any of the segments.
+  proto::SegmentInfo* segment_info = nullptr;
+  if (available_segment) {
+    segment_info = &available_segment.value();
+  } else {
+    VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(segment_id)
+            << " does not have segment info.";
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  std::make_unique<SegmentResult>(
+                                      ResultState::kSegmentNotAvailable)));
+    return;
+  }
+
+  // TODO(ssid): Remove this check since scheduler does this before executing
+  // the model.
+  if (!force_refresh_results_ &&
+      !signal_storage_config_->MeetsSignalCollectionRequirement(
+          segment_info->model_metadata())) {
+    VLOG(1) << __func__ << ": segment="
+            << OptimizationTarget_Name(segment_info->segment_id())
+            << " does not meet signal collection requirements.";
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  std::make_unique<SegmentResult>(
+                                      ResultState::kSignalsNotCollected)));
+    return;
+  }
+
+  if (metadata_utils::HasExpiredOrUnavailableResult(*segment_info,
+                                                    clock_->Now())) {
+    VLOG(1) << __func__ << ": segment="
+            << OptimizationTarget_Name(segment_info->segment_id())
+            << " has expired or unavailable result.";
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  std::make_unique<SegmentResult>(
+                                      ResultState::kDatabaseScoreNotReady)));
+    return;
+  }
+
+  int rank = metadata_utils::ConvertToDiscreteScore(
+      segmentation_key, segment_info->prediction_result().result(),
+      segment_info->model_metadata());
+  VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(segment_id)
+          << ": result=" << segment_info->prediction_result().result()
+          << ", rank=" << rank;
+
+  auto result =
+      std::make_unique<SegmentResult>(ResultState::kSuccessFromDatabase, rank);
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), std::move(result)));
+}
+
+}  // namespace
+
+SegmentResultProvider::SegmentResult::SegmentResult(ResultState state)
+    : state(state) {}
+SegmentResultProvider::SegmentResult::SegmentResult(ResultState state, int rank)
+    : state(state), rank(rank) {}
+SegmentResultProvider::SegmentResult::~SegmentResult() = default;
+
+// static
+std::unique_ptr<SegmentResultProvider> SegmentResultProvider::Create(
+    SegmentInfoDatabase* segment_database,
+    SignalStorageConfig* signal_storage_config,
+    ModelProviderFactory* model_provider_factory,
+    base::Clock* clock,
+    bool force_refresh_results) {
+  return std::make_unique<SegmentResultProviderImpl>(
+      segment_database, signal_storage_config, model_provider_factory, clock,
+      force_refresh_results);
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.h b/components/segmentation_platform/internal/selection/segment_result_provider.h
new file mode 100644
index 0000000..a730ea49
--- /dev/null
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.h
@@ -0,0 +1,63 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_RESULT_PROVIDER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_RESULT_PROVIDER_H_
+
+#include "base/callback.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+class Clock;
+}
+namespace segmentation_platform {
+
+class ModelProviderFactory;
+class SignalStorageConfig;
+
+// Used for retrieving the result of a particular model.
+class SegmentResultProvider {
+ public:
+  SegmentResultProvider() = default;
+  virtual ~SegmentResultProvider() = default;
+
+  enum class ResultState {
+    kUnknown = 0,
+    kSuccessFromDatabase = 1,
+    kSegmentNotAvailable = 2,
+    kSignalsNotCollected = 3,
+    kDatabaseScoreNotReady = 4,
+  };
+  struct SegmentResult {
+    explicit SegmentResult(ResultState state);
+    SegmentResult(ResultState state, int rank);
+    ~SegmentResult();
+    SegmentResult(SegmentResult&) = delete;
+    SegmentResult& operator=(SegmentResult&) = delete;
+
+    ResultState state = ResultState::kUnknown;
+    absl::optional<int> rank;
+  };
+  using SegmentResultCallback =
+      base::OnceCallback<void(std::unique_ptr<SegmentResult>)>;
+
+  // Creates the instance.
+  static std::unique_ptr<SegmentResultProvider> Create(
+      SegmentInfoDatabase* segment_info_database,
+      SignalStorageConfig* signal_storage_config,
+      ModelProviderFactory* model_provider_factory,
+      base::Clock* clock,
+      bool force_refresh_results);
+
+  // Returns latest available score for the segment.
+  virtual void GetSegmentResult(OptimizationTarget segment_id,
+                                const std::string& segmentation_key,
+                                SegmentResultCallback callback) = 0;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_RESULT_PROVIDER_H_
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index fa78630c..d7034c7e7 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -5,6 +5,8 @@
 #include "components/segmentation_platform/internal/selection/segment_selector_impl.h"
 
 #include "base/containers/contains.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/clock.h"
@@ -15,11 +17,35 @@
 #include "components/segmentation_platform/internal/platform_options.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
 #include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/model_provider.h"
 
 namespace segmentation_platform {
+namespace {
+
+stats::SegmentationSelectionFailureReason GetFailureReason(
+    SegmentResultProvider::ResultState result_state) {
+  switch (result_state) {
+    case SegmentResultProvider::ResultState::kUnknown:
+    case SegmentResultProvider::ResultState::kSuccessFromDatabase:
+      NOTREACHED();
+      return stats::SegmentationSelectionFailureReason::kMaxValue;
+    case SegmentResultProvider::ResultState::kDatabaseScoreNotReady:
+      return stats::SegmentationSelectionFailureReason::
+          kAtLeastOneSegmentNotReady;
+    case SegmentResultProvider::ResultState::kSegmentNotAvailable:
+      return stats::SegmentationSelectionFailureReason::
+          kAtLeastOneSegmentNotAvailable;
+    case SegmentResultProvider::ResultState::kSignalsNotCollected:
+      return stats::SegmentationSelectionFailureReason::
+          kAtLeastOneSegmentSignalsNotCollected;
+  }
+}
+
+}  // namespace
 
 using optimization_guide::proto::OptimizationTarget_Name;
 
@@ -29,13 +55,21 @@
     SegmentationResultPrefs* result_prefs,
     const Config* config,
     base::Clock* clock,
-    const PlatformOptions& platform_options)
-    : segment_database_(segment_database),
+    const PlatformOptions& platform_options,
+    ModelProviderFactory* model_provider_factory)
+    : segment_result_provider_(SegmentResultProvider::Create(
+          segment_database,
+          signal_storage_config,
+          model_provider_factory,
+          clock,
+          platform_options.force_refresh_results)),
+      segment_database_(segment_database),
       signal_storage_config_(signal_storage_config),
       result_prefs_(result_prefs),
       config_(config),
       clock_(clock),
-      platform_options_(platform_options) {
+      platform_options_(platform_options),
+      model_provider_factory_(model_provider_factory) {
   // Read selected segment from prefs.
   const auto& selected_segment =
       result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
@@ -71,52 +105,13 @@
   if (!base::Contains(config_->segment_ids, segment_id))
     return;
 
-  segment_database_->GetSegmentInfoForSegments(
-      config_->segment_ids,
-      base::BindOnce(&SegmentSelectorImpl::RunSegmentSelection,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void SegmentSelectorImpl::RunSegmentSelection(
-    std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments) {
-  if (!CanComputeSegmentSelection(*all_segments))
+  if (!CanComputeSegmentSelection())
     return;
 
-  OptimizationTarget selected_segment = FindBestSegment(*all_segments);
-  UpdateSelectedSegment(selected_segment);
+  GetRankForNextSegment(std::make_unique<SegmentRanks>());
 }
 
-bool SegmentSelectorImpl::CanComputeSegmentSelection(
-    const SegmentInfoDatabase::SegmentInfoList& all_segments) {
-  VLOG(1) << __func__ << ": all_segments.size()=" << all_segments.size();
-  // Don't compute results if we don't have enough signals, or don't have
-  // valid unexpired results for any of the segments.
-  for (const auto& pair : all_segments) {
-    const proto::SegmentInfo& segment_info = pair.second;
-    if (!platform_options_.force_refresh_results &&
-        !signal_storage_config_->MeetsSignalCollectionRequirement(
-            segment_info.model_metadata())) {
-      VLOG(1) << __func__ << ": segment="
-              << OptimizationTarget_Name(segment_info.segment_id())
-              << " does not meet signal collection requirements.";
-      stats::RecordSegmentSelectionFailure(
-          config_->segmentation_key, stats::SegmentationSelectionFailureReason::
-                                         kAtLeastOneSegmentSignalsNotCollected);
-      return false;
-    }
-
-    if (metadata_utils::HasExpiredOrUnavailableResult(segment_info,
-                                                      clock_->Now())) {
-      VLOG(1) << __func__ << ": segment="
-              << OptimizationTarget_Name(segment_info.segment_id())
-              << " has expired or unavailable result.";
-      stats::RecordSegmentSelectionFailure(
-          config_->segmentation_key, stats::SegmentationSelectionFailureReason::
-                                         kAtLeastOneSegmentNotReady);
-      return false;
-    }
-  }
-
+bool SegmentSelectorImpl::CanComputeSegmentSelection() {
   // Don't compute results if segment selection TTL hasn't expired.
   const auto& previous_selection =
       result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
@@ -141,31 +136,55 @@
   return true;
 }
 
+void SegmentSelectorImpl::GetRankForNextSegment(
+    std::unique_ptr<SegmentRanks> ranks) {
+  for (OptimizationTarget needed_segment : config_->segment_ids) {
+    if (ranks->count(needed_segment) == 0) {
+      segment_result_provider_->GetSegmentResult(
+          needed_segment, config_->segmentation_key,
+          base::BindOnce(&SegmentSelectorImpl::OnGetResultForSegmentSelection,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(ranks),
+                         needed_segment));
+      return;
+    }
+  }
+  OptimizationTarget selected_segment = FindBestSegment(*ranks);
+  UpdateSelectedSegment(selected_segment);
+}
+
+void SegmentSelectorImpl::OnGetResultForSegmentSelection(
+    std::unique_ptr<SegmentRanks> ranks,
+    OptimizationTarget current_segment_id,
+    std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
+  if (!result->rank) {
+    stats::RecordSegmentSelectionFailure(config_->segmentation_key,
+                                         GetFailureReason(result->state));
+    return;
+  }
+  ranks->insert(std::make_pair(current_segment_id, *result->rank));
+
+  GetRankForNextSegment(std::move(ranks));
+}
+
 OptimizationTarget SegmentSelectorImpl::FindBestSegment(
-    const SegmentInfoDatabase::SegmentInfoList& all_segments) {
-  int max_score = 0;
-  OptimizationTarget max_score_id =
+    const SegmentRanks& segment_results) {
+  int max_rank = 0;
+  OptimizationTarget max_rank_id =
       OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
-  // Loop through all the results. Convert them to discrete scores. Select the
-  // one with highest discrete score.
-  for (const auto& pair : all_segments) {
+  // Loop through all the results. Convert them to discrete ranks. Select the
+  // one with highest discrete rank.
+  for (const auto& pair : segment_results) {
     OptimizationTarget id = pair.first;
-    const proto::SegmentInfo& info = pair.second;
-    int score = metadata_utils::ConvertToDiscreteScore(
-        config_->segmentation_key, info.prediction_result().result(),
-        info.model_metadata());
-    VLOG(1) << __func__ << ": segment=" << OptimizationTarget_Name(id)
-            << ": result=" << info.prediction_result().result()
-            << ", score=" << score;
-    if (score > max_score) {
-      max_score = score;
-      max_score_id = id;
-    } else if (score == max_score && score > 0) {
+    int rank = pair.second;
+    if (rank > max_rank) {
+      max_rank = rank;
+      max_rank_id = id;
+    } else if (rank == max_rank && rank > 0) {
       // TODO(shaktisahu): Use fallback priority.
     }
   }
 
-  return max_score_id;
+  return max_rank_id;
 }
 
 void SegmentSelectorImpl::UpdateSelectedSegment(
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h
index 4a2c9845..03823b53 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.h
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -5,12 +5,13 @@
 #ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_SELECTOR_IMPL_H_
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_SELECTOR_IMPL_H_
 
-#include "base/memory/raw_ptr.h"
-#include "components/segmentation_platform/internal/selection/segment_selector.h"
-
 #include "base/callback_helpers.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/platform_options.h"
+#include "components/segmentation_platform/internal/selection/segment_result_provider.h"
+#include "components/segmentation_platform/internal/selection/segment_selector.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
 
 namespace base {
@@ -21,6 +22,7 @@
 
 struct Config;
 class ModelExecutionScheduler;
+class ModelProviderFactory;
 class SegmentationResultPrefs;
 class SignalStorageConfig;
 
@@ -31,7 +33,8 @@
                       SegmentationResultPrefs* result_prefs,
                       const Config* config,
                       base::Clock* clock,
-                      const PlatformOptions& platform_options);
+                      const PlatformOptions& platform_options,
+                      ModelProviderFactory* model_provider_factory);
 
   ~SegmentSelectorImpl() override;
 
@@ -53,21 +56,29 @@
   // For testing.
   friend class SegmentSelectorTest;
 
-  // Helper method to run segment selection if possible.
-  void RunSegmentSelection(
-      std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments);
+  using SegmentRanks = base::flat_map<OptimizationTarget, int>;
 
   // Determines whether segment selection can be run based on whether all
   // segments have met signal collection requirement, have valid results, and
   // segment selection TTL has expired.
-  bool CanComputeSegmentSelection(
-      const SegmentInfoDatabase::SegmentInfoList& all_segments);
+  bool CanComputeSegmentSelection();
+
+  // Gets ranks for each segment from SegmentResultProvider, and then computes
+  // segment selection.
+  void GetRankForNextSegment(std::unique_ptr<SegmentRanks> ranks);
+
+  // Callback used to get result from SegmentResultProvider for each segment.
+  void OnGetResultForSegmentSelection(
+      std::unique_ptr<SegmentRanks> ranks,
+      OptimizationTarget current_segment_id,
+      std::unique_ptr<SegmentResultProvider::SegmentResult> result);
 
   // Loops through all segments, performs discrete mapping, honors finch
-  // supplied tie-breakers, TTL, inertia etc, and finds the highest score.
+  // supplied tie-breakers, TTL, inertia etc, and finds the highest rank.
   // Ignores the segments that have no results.
-  OptimizationTarget FindBestSegment(
-      const SegmentInfoDatabase::SegmentInfoList& all_segments);
+  OptimizationTarget FindBestSegment(const SegmentRanks& segment_scores);
+
+  std::unique_ptr<SegmentResultProvider> segment_result_provider_;
 
   // The database storing metadata and results.
   raw_ptr<SegmentInfoDatabase> segment_database_;
@@ -86,6 +97,8 @@
 
   const PlatformOptions platform_options_;
 
+  const raw_ptr<ModelProviderFactory> model_provider_factory_;
+
   // Segment selection result is read from prefs on init and used for serving
   // the clients in the current session.
   SegmentSelectionResult selected_segment_last_session_;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
index e02af0ef..1f271c2 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -71,7 +71,7 @@
     prefs_ = std::make_unique<TestSegmentationResultPrefs>();
     segment_selector_ = std::make_unique<SegmentSelectorImpl>(
         segment_database_.get(), &signal_storage_config_, prefs_.get(),
-        &config_, &clock_, PlatformOptions::CreateDefault());
+        &config_, &clock_, PlatformOptions::CreateDefault(), nullptr);
   }
 
   void GetSelectedSegment(const SegmentSelectionResult& expected) {
@@ -106,6 +106,7 @@
   void CompleteModelExecution(OptimizationTarget segment_id, float score) {
     segment_database_->AddPredictionResult(segment_id, score, clock_.Now());
     segment_selector_->OnModelExecutionCompleted(segment_id);
+    task_environment_.RunUntilIdle();
   }
 
   base::test::TaskEnvironment task_environment_;
@@ -137,6 +138,7 @@
 
   clock_.Advance(base::Days(1));
   segment_selector_->OnModelExecutionCompleted(segment_id);
+  task_environment_.RunUntilIdle();
   ASSERT_TRUE(prefs_->selection.has_value());
   ASSERT_EQ(segment_id2, prefs_->selection->segment_id);
 }
@@ -285,7 +287,9 @@
       OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
   OptimizationTarget segment_id1 =
       OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  float mapping0[][2] = {{1.0, 0}};
   float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
+  InitializeMetadataForSegment(segment_id0, mapping0, 1);
   InitializeMetadataForSegment(segment_id1, mapping1, 3);
 
   // Set up a selected segment in prefs.
@@ -295,7 +299,7 @@
   // Construct a segment selector. It should read result from last session.
   segment_selector_ = std::make_unique<SegmentSelectorImpl>(
       segment_database_.get(), &signal_storage_config_, prefs_.get(), &config_,
-      &clock_, PlatformOptions::CreateDefault());
+      &clock_, PlatformOptions::CreateDefault(), nullptr);
 
   SegmentSelectionResult result;
   result.segment = segment_id0;
@@ -306,8 +310,10 @@
   // Add results for a new segment.
   base::Time result_timestamp = base::Time::Now();
   segment_database_->AddPredictionResult(segment_id1, 0.6, result_timestamp);
+  segment_database_->AddPredictionResult(segment_id0, 0.5, result_timestamp);
 
   segment_selector_->OnModelExecutionCompleted(segment_id1);
+  task_environment_.RunUntilIdle();
   ASSERT_TRUE(prefs_->selection.has_value());
   ASSERT_EQ(segment_id1, prefs_->selection->segment_id);
 
@@ -337,6 +343,7 @@
 
   clock_.Advance(base::Days(1));
   segment_selector_->OnModelExecutionCompleted(segment_id);
+  task_environment_.RunUntilIdle();
   ASSERT_TRUE(prefs_->selection.has_value());
   ASSERT_EQ(segment_id2, prefs_->selection->segment_id);
 
diff --git a/components/segmentation_platform/internal/service_proxy_impl.cc b/components/segmentation_platform/internal/service_proxy_impl.cc
index 05cbc4a9..5864dcd7 100644
--- a/components/segmentation_platform/internal/service_proxy_impl.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl.cc
@@ -11,8 +11,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/core/model_util.h"
-#include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h"
diff --git a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
index a35b13e..e538165 100644
--- a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
@@ -67,7 +67,8 @@
                             result_prefs,
                             config,
                             nullptr,
-                            PlatformOptions::CreateDefault()) {}
+                            PlatformOptions::CreateDefault(),
+                            nullptr) {}
   ~FakeSegmentSelectorImpl() override = default;
 
   void UpdateSelectedSegment(OptimizationTarget new_selection) override {
diff --git a/components/segmentation_platform/internal/stats.h b/components/segmentation_platform/internal/stats.h
index 5e1e707..a5d9088 100644
--- a/components/segmentation_platform/internal/stats.h
+++ b/components/segmentation_platform/internal/stats.h
@@ -159,7 +159,8 @@
   kFailedToSaveModelResult = 8,
   kInvalidSelectionResultInPrefs = 9,
   kDBInitFailure = 10,
-  kMaxValue = kDBInitFailure
+  kAtLeastOneSegmentNotAvailable = 11,
+  kMaxValue = kAtLeastOneSegmentNotAvailable
 };
 
 // Records the reason for failure or success to compute a segment selection.
diff --git a/components/segmentation_platform/public/BUILD.gn b/components/segmentation_platform/public/BUILD.gn
index 4f325522..0dd677e 100644
--- a/components/segmentation_platform/public/BUILD.gn
+++ b/components/segmentation_platform/public/BUILD.gn
@@ -13,6 +13,8 @@
     "config.h",
     "features.cc",
     "features.h",
+    "model_provider.cc",
+    "model_provider.h",
     "segment_selection_result.cc",
     "segment_selection_result.h",
     "segmentation_platform_service.cc",
diff --git a/components/segmentation_platform/public/model_provider.cc b/components/segmentation_platform/public/model_provider.cc
new file mode 100644
index 0000000..1eede716
--- /dev/null
+++ b/components/segmentation_platform/public/model_provider.cc
@@ -0,0 +1,17 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/segmentation_platform/public/model_provider.h"
+
+namespace segmentation_platform {
+
+ModelProvider::ModelProvider(
+    optimization_guide::proto::OptimizationTarget optimization_target)
+    : optimization_target_(optimization_target) {}
+
+ModelProvider::~ModelProvider() = default;
+
+ModelProviderFactory::~ModelProviderFactory() = default;
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/public/model_provider.h b/components/segmentation_platform/public/model_provider.h
new file mode 100644
index 0000000..fe41711
--- /dev/null
+++ b/components/segmentation_platform/public/model_provider.h
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_MODEL_PROVIDER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_MODEL_PROVIDER_H_
+
+#include "base/callback.h"
+#include "base/task/sequenced_task_runner.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace segmentation_platform {
+namespace proto {
+class SegmentationModelMetadata;
+}  // namespace proto
+
+// Interface used by the segmentation platform to get model and metadata for a
+// single optimization target.
+class ModelProvider {
+ public:
+  using ModelUpdatedCallback = base::RepeatingCallback<void(
+      optimization_guide::proto::OptimizationTarget,
+      proto::SegmentationModelMetadata,
+      int64_t)>;
+  using ExecutionCallback =
+      base::OnceCallback<void(const absl::optional<float>&)>;
+
+  explicit ModelProvider(
+      optimization_guide::proto::OptimizationTarget optimization_target);
+  virtual ~ModelProvider();
+
+  ModelProvider(ModelProvider&) = delete;
+  ModelProvider& operator=(ModelProvider&) = delete;
+
+  // Implementation should return metadata that will be used to execute model.
+  // The metadata provided should define the number of features needed by the
+  // ExecuteModelWithInput() method. Starts a fetch request for the model for
+  // optimization target. The `model_updated_callback` can be called multiple
+  // times when new models are available for the optimization target.
+  virtual void InitAndFetchModel(
+      const ModelUpdatedCallback& model_updated_callback) = 0;
+
+  // Executes the latest model available, with the given inputs and returns
+  // result via `callback`. Should be called only after InitAndFetchModel()
+  // otherwise returns absl::nullopt. Implementation could be a heuristic or
+  // model execution to return a result. The inputs to this method are the
+  // computed tensors based on the features provided in the latest call to
+  // `model_updated_callback`. The result is a float score with the probability
+  // of positive result. Also see `discrete_mapping` field in the
+  // `SegmentationModelMetadata` for how the score will be used to determine the
+  // segment.
+  virtual void ExecuteModelWithInput(const std::vector<float>& inputs,
+                                     ExecutionCallback callback) = 0;
+
+  // Returns true if a model is available.
+  virtual bool ModelAvailable() = 0;
+
+ protected:
+  const optimization_guide::proto::OptimizationTarget optimization_target_;
+};
+
+// Interface used by segmentation platform to create ModelProvider(s).
+class ModelProviderFactory {
+ public:
+  virtual ~ModelProviderFactory();
+
+  // Creates a model provider for the given `optimization_target`.
+  virtual std::unique_ptr<ModelProvider> CreateProvider(
+      optimization_guide::proto::OptimizationTarget) = 0;
+
+  // Creates a default model provider to be used when the original provider did
+  // not provide a model. Returns `nullptr` when a default provider is not
+  // available.
+  virtual std::unique_ptr<ModelProvider> CreateDefaultProvider(
+      optimization_guide::proto::OptimizationTarget) = 0;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_MODEL_PROVIDER_H_
diff --git a/components/services/app_service/public/cpp/BUILD.gn b/components/services/app_service/public/cpp/BUILD.gn
index ecf0d7d..0467960 100644
--- a/components/services/app_service/public/cpp/BUILD.gn
+++ b/components/services/app_service/public/cpp/BUILD.gn
@@ -202,8 +202,6 @@
   sources = [
     "intent.cc",
     "intent.h",
-    "intent_constants.cc",
-    "intent_constants.h",
     "intent_filter_util.cc",
     "intent_filter_util.h",
     "intent_util.cc",
@@ -239,6 +237,7 @@
   ]
 
   deps = [
+    ":app_types",
     ":intents",
     "//base",
     "//components/services/app_service/public/mojom",
diff --git a/components/services/app_service/public/cpp/app_update.cc b/components/services/app_service/public/cpp/app_update.cc
index 225c22e..723d349 100644
--- a/components/services/app_service/public/cpp/app_update.cc
+++ b/components/services/app_service/public/cpp/app_update.cc
@@ -39,7 +39,17 @@
   }
 }
 
-void CloneIntentFilters(
+std::vector<apps::IntentFilterPtr> ConvertMojomIntentFiltersToIntentFilters(
+    const std::vector<apps::mojom::IntentFilterPtr>& mojom_intent_filters) {
+  std::vector<apps::IntentFilterPtr> intent_filters;
+  for (const auto& mojom_intent_filter : mojom_intent_filters) {
+    intent_filters.push_back(
+        apps::ConvertMojomIntentFilterToIntentFilter(mojom_intent_filter));
+  }
+  return intent_filters;
+}
+
+void CloneMojomIntentFilters(
     const std::vector<apps::mojom::IntentFilterPtr>& clone_from,
     std::vector<apps::mojom::IntentFilterPtr>* clone_to) {
   for (const auto& intent_filter : clone_from) {
@@ -156,7 +166,7 @@
   }
   if (!delta->intent_filters.empty()) {
     state->intent_filters.clear();
-    ::CloneIntentFilters(delta->intent_filters, &state->intent_filters);
+    ::CloneMojomIntentFilters(delta->intent_filters, &state->intent_filters);
   }
   if (delta->resize_locked != apps::mojom::OptionalBool::kUnknown) {
     state->resize_locked = delta->resize_locked;
@@ -855,28 +865,25 @@
          (!mojom_state_ || (mojom_delta_->paused != mojom_state_->paused));
 }
 
-std::vector<apps::mojom::IntentFilterPtr> AppUpdate::IntentFilters() const {
-  std::vector<apps::mojom::IntentFilterPtr> intent_filters;
+apps::IntentFilters AppUpdate::IntentFilters() const {
+  if (ShouldUseNonMojom()) {
+    if (delta_ && !delta_->intent_filters.empty()) {
+      return CloneIntentFilters(delta_->intent_filters);
+    }
+    if (state_ && !state_->intent_filters.empty()) {
+      return CloneIntentFilters(state_->intent_filters);
+    }
+    return std::vector<IntentFilterPtr>{};
+  }
 
   if (mojom_delta_ && !mojom_delta_->intent_filters.empty()) {
-    ::CloneIntentFilters(mojom_delta_->intent_filters, &intent_filters);
+    return ::ConvertMojomIntentFiltersToIntentFilters(
+        mojom_delta_->intent_filters);
   } else if (mojom_state_ && !mojom_state_->intent_filters.empty()) {
-    ::CloneIntentFilters(mojom_state_->intent_filters, &intent_filters);
+    return ::ConvertMojomIntentFiltersToIntentFilters(
+        mojom_state_->intent_filters);
   }
-
-  return intent_filters;
-}
-
-apps::IntentFilters AppUpdate::GetIntentFilters() const {
-  apps::IntentFilters intent_filters;
-
-  if (delta_ && !delta_->intent_filters.empty()) {
-    intent_filters = CloneIntentFilters(delta_->intent_filters);
-  } else if (state_ && !state_->intent_filters.empty()) {
-    intent_filters = CloneIntentFilters(state_->intent_filters);
-  }
-
-  return intent_filters;
+  return std::vector<IntentFilterPtr>{};
 }
 
 bool AppUpdate::IntentFiltersChanged() const {
diff --git a/components/services/app_service/public/cpp/app_update.h b/components/services/app_service/public/cpp/app_update.h
index fdab66f..09b6b51a 100644
--- a/components/services/app_service/public/cpp/app_update.h
+++ b/components/services/app_service/public/cpp/app_update.h
@@ -163,8 +163,7 @@
   absl::optional<bool> Paused() const;
   bool PausedChanged() const;
 
-  std::vector<apps::mojom::IntentFilterPtr> IntentFilters() const;
-  apps::IntentFilters GetIntentFilters() const;
+  apps::IntentFilters IntentFilters() const;
   bool IntentFiltersChanged() const;
 
   absl::optional<bool> ResizeLocked() const;
diff --git a/components/services/app_service/public/cpp/app_update_mojom_unittest.cc b/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
index 4a8fdd1..6a35122 100644
--- a/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
+++ b/components/services/app_service/public/cpp/app_update_mojom_unittest.cc
@@ -102,7 +102,7 @@
   absl::optional<bool> expect_paused_;
   bool expect_paused_changed_;
 
-  std::vector<apps::mojom::IntentFilterPtr> expect_intent_filters_;
+  std::vector<apps::IntentFilterPtr> expect_intent_filters_;
   bool expect_intent_filters_changed_;
 
   absl::optional<bool> expect_resize_locked_;
@@ -236,7 +236,7 @@
     EXPECT_EQ(expect_paused_, u.Paused());
     EXPECT_EQ(expect_paused_changed_, u.PausedChanged());
 
-    EXPECT_EQ(expect_intent_filters_, u.IntentFilters());
+    EXPECT_TRUE(IsEqual(expect_intent_filters_, u.IntentFilters()));
     EXPECT_EQ(expect_intent_filters_changed_, u.IntentFiltersChanged());
 
     EXPECT_EQ(expect_resize_locked_, u.ResizeLocked());
@@ -881,7 +881,8 @@
       intent_filter->conditions.push_back(std::move(host_condition));
 
       state->intent_filters.push_back(intent_filter.Clone());
-      expect_intent_filters_.push_back(intent_filter.Clone());
+      expect_intent_filters_.push_back(
+          apps::ConvertMojomIntentFilterToIntentFilter(intent_filter));
       expect_intent_filters_changed_ = false;
       CheckExpects(u);
     }
@@ -910,7 +911,8 @@
       intent_filter->conditions.push_back(std::move(host_condition));
 
       delta->intent_filters.push_back(intent_filter.Clone());
-      expect_intent_filters_.push_back(intent_filter.Clone());
+      expect_intent_filters_.push_back(
+          apps::ConvertMojomIntentFilterToIntentFilter(intent_filter));
       expect_intent_filters_changed_ = true;
       CheckExpects(u);
     }
diff --git a/components/services/app_service/public/cpp/app_update_unittest.cc b/components/services/app_service/public/cpp/app_update_unittest.cc
index 261f7eb..2781f4b 100644
--- a/components/services/app_service/public/cpp/app_update_unittest.cc
+++ b/components/services/app_service/public/cpp/app_update_unittest.cc
@@ -245,7 +245,7 @@
     EXPECT_EQ(expect_paused_, u.Paused());
     EXPECT_EQ(expect_paused_changed_, u.PausedChanged());
 
-    EXPECT_TRUE(IsEqual(expect_intent_filters_, u.GetIntentFilters()));
+    EXPECT_TRUE(IsEqual(expect_intent_filters_, u.IntentFilters()));
     EXPECT_EQ(expect_intent_filters_changed_, u.IntentFiltersChanged());
 
     EXPECT_EQ(expect_resize_locked_, u.ResizeLocked());
diff --git a/components/services/app_service/public/cpp/intent_constants.cc b/components/services/app_service/public/cpp/intent_constants.cc
deleted file mode 100644
index 14e3812e..0000000
--- a/components/services/app_service/public/cpp/intent_constants.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/services/app_service/public/cpp/intent_constants.h"
-
-namespace apps {
-
-const char kUseBrowserForLink[] = "use_browser";
-
-}  // namespace apps
diff --git a/components/services/app_service/public/cpp/intent_constants.h b/components/services/app_service/public/cpp/intent_constants.h
deleted file mode 100644
index 762a46f..0000000
--- a/components/services/app_service/public/cpp/intent_constants.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_CONSTANTS_H_
-#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_CONSTANTS_H_
-
-namespace apps {
-
-// App ID value which can be used as a Preferred App to denote that the browser
-// will open the link, and that we should not prompt the user about it.
-extern const char kUseBrowserForLink[];
-
-}  // namespace apps
-
-#endif  // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_CONSTANTS_H_
diff --git a/components/services/app_service/public/cpp/intent_filter.cc b/components/services/app_service/public/cpp/intent_filter.cc
index 98174e21..7cc8315 100644
--- a/components/services/app_service/public/cpp/intent_filter.cc
+++ b/components/services/app_service/public/cpp/intent_filter.cc
@@ -93,6 +93,99 @@
   return intent_filter;
 }
 
+void IntentFilter::AddSingleValueCondition(
+    ConditionType condition_type,
+    const std::string& value,
+    PatternMatchType pattern_match_type) {
+  ConditionValues condition_values;
+  condition_values.push_back(
+      std::make_unique<ConditionValue>(value, pattern_match_type));
+  conditions.push_back(
+      std::make_unique<Condition>(condition_type, std::move(condition_values)));
+}
+
+int IntentFilter::GetFilterMatchLevel() {
+  int match_level = static_cast<int>(IntentFilterMatchLevel::kNone);
+  for (const auto& condition : conditions) {
+    switch (condition->condition_type) {
+      case ConditionType::kAction:
+        // Action always need to be matched, so there is no need for
+        // match level.
+        break;
+      case ConditionType::kScheme:
+        match_level += static_cast<int>(IntentFilterMatchLevel::kScheme);
+        break;
+      case ConditionType::kHost:
+        match_level += static_cast<int>(IntentFilterMatchLevel::kHost);
+        break;
+      case ConditionType::kPattern:
+        match_level += static_cast<int>(IntentFilterMatchLevel::kPattern);
+        break;
+      case ConditionType::kMimeType:
+      case ConditionType::kFile:
+        match_level += static_cast<int>(IntentFilterMatchLevel::kMimeType);
+        break;
+    }
+  }
+  return match_level;
+}
+
+void IntentFilter::GetMimeTypesAndExtensions(
+    std::set<std::string>& mime_types,
+    std::set<std::string>& file_extensions) {
+  for (const auto& condition : conditions) {
+    if (condition->condition_type != ConditionType::kFile) {
+      continue;
+    }
+    for (const auto& condition_value : condition->condition_values) {
+      if (condition_value->match_type == PatternMatchType::kFileExtension) {
+        file_extensions.insert(condition_value->value);
+      }
+      if (condition_value->match_type == PatternMatchType::kMimeType) {
+        mime_types.insert(condition_value->value);
+      }
+    }
+  }
+}
+
+bool IntentFilter::IsBrowserFilter() {
+  if (GetFilterMatchLevel() !=
+      static_cast<int>(IntentFilterMatchLevel::kScheme)) {
+    return false;
+  }
+  for (const auto& condition : conditions) {
+    if (condition->condition_type != ConditionType::kScheme) {
+      continue;
+    }
+    for (const auto& condition_value : condition->condition_values) {
+      if (condition_value->value == url::kHttpScheme ||
+          condition_value->value == url::kHttpsScheme) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+bool IntentFilter::IsFileExtensionsFilter() {
+  for (const auto& condition : conditions) {
+    // We expect action conditions to be paired with file conditions.
+    if (condition->condition_type == ConditionType::kAction) {
+      continue;
+    }
+    if (condition->condition_type != ConditionType::kFile) {
+      return false;
+    }
+    for (const auto& condition_value : condition->condition_values) {
+      if (condition_value->match_type != PatternMatchType::kFileExtension) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 IntentFilters CloneIntentFilters(const IntentFilters& intent_filters) {
   IntentFilters ret;
   for (const auto& intent_filter : intent_filters) {
diff --git a/components/services/app_service/public/cpp/intent_filter.h b/components/services/app_service/public/cpp/intent_filter.h
index 4fd72b8..042f43b 100644
--- a/components/services/app_service/public/cpp/intent_filter.h
+++ b/components/services/app_service/public/cpp/intent_filter.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_
 #define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_
 
+#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -16,6 +17,19 @@
 
 namespace apps {
 
+// The concept of match level is taken from Android. The values are not
+// necessary the same.
+// See
+// https://developer.android.com/reference/android/content/IntentFilter.html#constants_2
+// for more details.
+enum class IntentFilterMatchLevel {
+  kNone = 0,
+  kScheme = 1,
+  kHost = 2,
+  kPattern = 4,
+  kMimeType = 8,
+};
+
 // The intent filter matching condition types.
 enum class ConditionType {
   kScheme,    // Matches the URL scheme (e.g. https, tel).
@@ -92,6 +106,29 @@
 
   std::unique_ptr<IntentFilter> Clone() const;
 
+  // Creates condition that only contain one value and adds the condition to
+  // the intent filter.
+  void AddSingleValueCondition(apps::ConditionType condition_type,
+                               const std::string& value,
+                               apps::PatternMatchType pattern_match_type);
+
+  // Gets the intent_filter match level. The higher the return value, the better
+  // the match is. For example, a filter with scheme, host and path is better
+  // match compare with filter with only scheme. Each condition type has a
+  // matching level value, and this function will return the sum of the matching
+  // level values of all existing condition types.
+  int GetFilterMatchLevel();
+
+  void GetMimeTypesAndExtensions(std::set<std::string>& mime_types,
+                                 std::set<std::string>& file_extensions);
+
+  // Returns true if the filter is a browser filter, i.e. can handle all https
+  // or http scheme.
+  bool IsBrowserFilter();
+
+  // Returns true if the filter only contains file extension pattern matches.
+  bool IsFileExtensionsFilter();
+
   Conditions conditions;
 
   // Activity which registered this filter. We only fill this field for ARC
diff --git a/components/services/app_service/public/cpp/intent_filter_util.cc b/components/services/app_service/public/cpp/intent_filter_util.cc
index dfcc3689..bed5aabe 100644
--- a/components/services/app_service/public/cpp/intent_filter_util.cc
+++ b/components/services/app_service/public/cpp/intent_filter_util.cc
@@ -6,7 +6,6 @@
 
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
-#include "components/services/app_service/public/cpp/intent_constants.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/services/app_service/public/mojom/types.mojom-shared.h"
 #include "url/url_constants.h"
@@ -92,17 +91,6 @@
   return condition;
 }
 
-void AddSingleValueCondition(apps::ConditionType condition_type,
-                             const std::string& value,
-                             apps::PatternMatchType pattern_match_type,
-                             apps::IntentFilterPtr& intent_filter) {
-  apps::ConditionValues condition_values;
-  condition_values.push_back(
-      std::make_unique<apps::ConditionValue>(value, pattern_match_type));
-  intent_filter->conditions.push_back(std::make_unique<apps::Condition>(
-      condition_type, std::move(condition_values)));
-}
-
 void AddSingleValueCondition(apps::mojom::ConditionType condition_type,
                              const std::string& value,
                              apps::mojom::PatternMatchType pattern_match_type,
@@ -118,18 +106,20 @@
 apps::IntentFilterPtr MakeIntentFilterForUrlScope(const GURL& url) {
   auto intent_filter = std::make_unique<apps::IntentFilter>();
 
-  AddSingleValueCondition(apps::ConditionType::kAction,
-                          apps_util::kIntentActionView,
-                          apps::PatternMatchType::kNone, intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kAction,
+                                         apps_util::kIntentActionView,
+                                         apps::PatternMatchType::kNone);
 
-  AddSingleValueCondition(apps::ConditionType::kScheme, url.scheme(),
-                          apps::PatternMatchType::kNone, intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme,
+                                         url.scheme(),
+                                         apps::PatternMatchType::kNone);
 
-  AddSingleValueCondition(apps::ConditionType::kHost, url.host(),
-                          apps::PatternMatchType::kNone, intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, url.host(),
+                                         apps::PatternMatchType::kNone);
 
-  AddSingleValueCondition(apps::ConditionType::kPattern, url.path(),
-                          apps::PatternMatchType::kPrefix, intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern,
+                                         url.path(),
+                                         apps::PatternMatchType::kPrefix);
 
   return intent_filter;
 }
@@ -155,7 +145,7 @@
 }
 
 int GetFilterMatchLevel(const apps::mojom::IntentFilterPtr& intent_filter) {
-  int match_level = IntentFilterMatchLevel::kNone;
+  int match_level = static_cast<int>(apps::IntentFilterMatchLevel::kNone);
   for (const auto& condition : intent_filter->conditions) {
     switch (condition->condition_type) {
       case apps::mojom::ConditionType::kAction:
@@ -163,17 +153,18 @@
         // match level.
         break;
       case apps::mojom::ConditionType::kScheme:
-        match_level += IntentFilterMatchLevel::kScheme;
+        match_level += static_cast<int>(apps::IntentFilterMatchLevel::kScheme);
         break;
       case apps::mojom::ConditionType::kHost:
-        match_level += IntentFilterMatchLevel::kHost;
+        match_level += static_cast<int>(apps::IntentFilterMatchLevel::kHost);
         break;
       case apps::mojom::ConditionType::kPattern:
-        match_level += IntentFilterMatchLevel::kPattern;
+        match_level += static_cast<int>(apps::IntentFilterMatchLevel::kPattern);
         break;
       case apps::mojom::ConditionType::kMimeType:
       case apps::mojom::ConditionType::kFile:
-        match_level += IntentFilterMatchLevel::kMimeType;
+        match_level +=
+            static_cast<int>(apps::IntentFilterMatchLevel::kMimeType);
         break;
     }
   }
@@ -217,7 +208,8 @@
 }
 
 bool IsBrowserFilter(const apps::mojom::IntentFilterPtr& filter) {
-  if (GetFilterMatchLevel(filter) != IntentFilterMatchLevel::kScheme) {
+  if (GetFilterMatchLevel(filter) !=
+      static_cast<int>(apps::IntentFilterMatchLevel::kScheme)) {
     return false;
   }
   for (const auto& condition : filter->conditions) {
@@ -314,11 +306,61 @@
 }
 
 bool IsSupportedLinkForApp(const std::string& app_id,
+                           const apps::IntentFilterPtr& intent_filter) {
+  // Filters associated with kUseBrowserForLink are a special case. These
+  // filters do not "belong" to the app and should not be treated as supported
+  // links.
+  if (app_id == apps_util::kUseBrowserForLink) {
+    return false;
+  }
+
+  bool action = false;
+  bool scheme = false;
+  bool host = false;
+  bool pattern = false;
+  for (auto& condition : intent_filter->conditions) {
+    switch (condition->condition_type) {
+      case apps::ConditionType::kAction:
+        for (auto& condition_value : condition->condition_values) {
+          if (condition_value->value == apps_util::kIntentActionView) {
+            action = true;
+            break;
+          }
+        }
+        break;
+      case apps::ConditionType::kScheme:
+        for (auto& condition_value : condition->condition_values) {
+          if (condition_value->value == "http" ||
+              condition_value->value == "https") {
+            scheme = true;
+            break;
+          }
+        }
+        break;
+      case apps::ConditionType::kHost:
+        host = true;
+        break;
+      case apps::ConditionType::kPattern:
+        pattern = true;
+        break;
+      default:
+        break;
+    }
+
+    if (action && scheme && host && pattern) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool IsSupportedLinkForApp(const std::string& app_id,
                            const apps::mojom::IntentFilterPtr& intent_filter) {
   // Filters associated with kUseBrowserForLink are a special case. These
   // filters do not "belong" to the app and should not be treated as supported
   // links.
-  if (app_id == apps::kUseBrowserForLink) {
+  if (app_id == apps_util::kUseBrowserForLink) {
     return false;
   }
 
diff --git a/components/services/app_service/public/cpp/intent_filter_util.h b/components/services/app_service/public/cpp/intent_filter_util.h
index e7ee2ad..97eb7d1 100644
--- a/components/services/app_service/public/cpp/intent_filter_util.h
+++ b/components/services/app_service/public/cpp/intent_filter_util.h
@@ -15,19 +15,6 @@
 
 namespace apps_util {
 
-// The concept of match level is taken from Android. The values are not
-// necessary the same.
-// See
-// https://developer.android.com/reference/android/content/IntentFilter.html#constants_2
-// for more details.
-enum IntentFilterMatchLevel {
-  kNone = 0,
-  kScheme = 1,
-  kHost = 2,
-  kPattern = 4,
-  kMimeType = 8,
-};
-
 // Creates condition value that makes up App Service intent filter
 // condition. Each condition contains a list of condition values.
 // For pattern type of condition, the value match will be based on the
@@ -46,13 +33,6 @@
     apps::mojom::ConditionType condition_type,
     std::vector<apps::mojom::ConditionValuePtr> condition_values);
 
-// Creates condition that only contain one value and add the condition to
-// the intent filter.
-void AddSingleValueCondition(apps::ConditionType condition_type,
-                             const std::string& value,
-                             apps::PatternMatchType pattern_match_type,
-                             apps::IntentFilterPtr& intent_filter);
-
 // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
 void AddSingleValueCondition(apps::mojom::ConditionType condition_type,
                              const std::string& value,
@@ -98,8 +78,14 @@
 std::set<std::string> AppManagementGetSupportedLinks(
     const apps::mojom::IntentFilterPtr& intent_filter);
 
+// Checks if the `intent_filter` is a supported link for `app_id`, i.e. it has
+// the "view" action, a http or https scheme, and at least one host and pattern.
+bool IsSupportedLinkForApp(const std::string& app_id,
+                           const apps::IntentFilterPtr& intent_filter);
+
 // Check if the |intent_filter| is a supported link for |app_id|, i.e. it has
 // the "view" action, a http or https scheme, and at least one host and pattern.
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
 bool IsSupportedLinkForApp(const std::string& app_id,
                            const apps::mojom::IntentFilterPtr& intent_filter);
 
diff --git a/components/services/app_service/public/cpp/intent_filter_util_unittest.cc b/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
index 5da79c2e3..b48c3b9 100644
--- a/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
+++ b/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
@@ -27,6 +27,29 @@
 
 class IntentFilterUtilTest : public testing::Test {
  protected:
+  apps::IntentFilterPtr MakeFilter(std::string scheme,
+                                   std::string host,
+                                   std::string path,
+                                   apps::PatternMatchType pattern) {
+    auto intent_filter = std::make_unique<apps::IntentFilter>();
+
+    intent_filter->AddSingleValueCondition(apps::ConditionType::kAction,
+                                           apps_util::kIntentActionView,
+                                           apps::PatternMatchType::kNone);
+
+    intent_filter->AddSingleValueCondition(apps::ConditionType::kScheme, scheme,
+                                           apps::PatternMatchType::kNone);
+
+    intent_filter->AddSingleValueCondition(apps::ConditionType::kHost, host,
+                                           apps::PatternMatchType::kNone);
+
+    intent_filter->AddSingleValueCondition(apps::ConditionType::kPattern, path,
+                                           pattern);
+
+    return intent_filter;
+  }
+
+  // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
   apps::mojom::IntentFilterPtr MakeFilter(
       std::string scheme,
       std::string host,
@@ -290,7 +313,8 @@
   EXPECT_EQ(links.count("m.youtube.com/.*/foo"), 1u);
 }
 
-TEST_F(IntentFilterUtilTest, IsSupportedLink) {
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentFilterUtilTest, IsSupportedLinkMojom) {
   auto filter = MakeFilter("https", "www.google.com", "/maps",
                            apps::mojom::PatternMatchType::kLiteral);
   ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter));
@@ -300,7 +324,18 @@
   ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter));
 }
 
-TEST_F(IntentFilterUtilTest, NotSupportedLink) {
+TEST_F(IntentFilterUtilTest, IsSupportedLink) {
+  auto filter = MakeFilter("https", "www.google.com", "/maps",
+                           apps::PatternMatchType::kLiteral);
+  ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter));
+
+  filter = MakeFilter("https", "www.google.com", ".*",
+                      apps::PatternMatchType::kGlob);
+  ASSERT_TRUE(apps_util::IsSupportedLinkForApp(kAppId, filter));
+}
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentFilterUtilTest, NotSupportedLinkMojom) {
   ASSERT_FALSE(apps_util::IsSupportedLinkForApp(
       kAppId, apps_util::CreateIntentFilterForMimeType("image/png")));
 
@@ -326,6 +361,30 @@
   ASSERT_FALSE(apps_util::IsSupportedLinkForApp(kAppId, browser_filter));
 }
 
+TEST_F(IntentFilterUtilTest, NotSupportedLink) {
+  ASSERT_FALSE(apps_util::IsSupportedLinkForApp(
+      kAppId, apps_util::MakeIntentFilterForMimeType("image/png")));
+
+  auto browser_filter = std::make_unique<apps::IntentFilter>();
+  browser_filter->AddSingleValueCondition(apps::ConditionType::kAction,
+                                          apps_util::kIntentActionView,
+                                          apps::PatternMatchType::kNone);
+  browser_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https",
+                                          apps::PatternMatchType::kNone);
+  ASSERT_FALSE(apps_util::IsSupportedLinkForApp(kAppId, browser_filter));
+
+  auto host_filter = std::make_unique<apps::IntentFilter>();
+  host_filter->AddSingleValueCondition(apps::ConditionType::kAction,
+                                       apps_util::kIntentActionView,
+                                       apps::PatternMatchType::kNone);
+  host_filter->AddSingleValueCondition(apps::ConditionType::kScheme, "https",
+                                       apps::PatternMatchType::kNone);
+  host_filter->AddSingleValueCondition(apps::ConditionType::kHost,
+                                       "www.example.com",
+                                       apps::PatternMatchType::kNone);
+  ASSERT_FALSE(apps_util::IsSupportedLinkForApp(kAppId, browser_filter));
+}
+
 TEST_F(IntentFilterUtilTest, HostMatchOverlapLiteralAndNone) {
   auto google_domain_filter = MakeFilter(
       "https", "www.google.com", "/", apps::mojom::PatternMatchType::kLiteral);
@@ -448,37 +507,30 @@
   base::flat_map<std::string, std::vector<apps::IntentFilterPtr>> filters;
 
   auto intent_filter1 = std::make_unique<apps::IntentFilter>();
-  apps_util::AddSingleValueCondition(apps::ConditionType::kScheme, "1",
-                                     apps::PatternMatchType::kNone,
-                                     intent_filter1);
+  intent_filter1->AddSingleValueCondition(apps::ConditionType::kScheme, "1",
+                                          apps::PatternMatchType::kNone);
   filters["1"].push_back(std::move(intent_filter1));
 
   auto intent_filter2 = std::make_unique<apps::IntentFilter>();
-  apps_util::AddSingleValueCondition(apps::ConditionType::kHost, "2",
-                                     apps::PatternMatchType::kLiteral,
-                                     intent_filter2);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kPattern, "3",
-                                     apps::PatternMatchType::kPrefix,
-                                     intent_filter2);
+  intent_filter2->AddSingleValueCondition(apps::ConditionType::kHost, "2",
+                                          apps::PatternMatchType::kLiteral);
+  intent_filter2->AddSingleValueCondition(apps::ConditionType::kPattern, "3",
+                                          apps::PatternMatchType::kPrefix);
   filters["1"].push_back(std::move(intent_filter2));
 
   apps::IntentFilters intent_filters2;
   auto intent_filter3 = std::make_unique<apps::IntentFilter>();
-  apps_util::AddSingleValueCondition(apps::ConditionType::kAction, "4",
-                                     apps::PatternMatchType::kGlob,
-                                     intent_filter3);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kMimeType, "5",
-                                     apps::PatternMatchType::kMimeType,
-                                     intent_filter3);
+  intent_filter3->AddSingleValueCondition(apps::ConditionType::kAction, "4",
+                                          apps::PatternMatchType::kGlob);
+  intent_filter3->AddSingleValueCondition(apps::ConditionType::kMimeType, "5",
+                                          apps::PatternMatchType::kMimeType);
   filters["2"].push_back(std::move(intent_filter3));
 
   auto intent_filter4 = std::make_unique<apps::IntentFilter>();
-  apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "6",
-                                     apps::PatternMatchType::kMimeType,
-                                     intent_filter4);
-  apps_util::AddSingleValueCondition(apps::ConditionType::kFile, "7",
-                                     apps::PatternMatchType::kFileExtension,
-                                     intent_filter4);
+  intent_filter4->AddSingleValueCondition(apps::ConditionType::kFile, "6",
+                                          apps::PatternMatchType::kMimeType);
+  intent_filter4->AddSingleValueCondition(
+      apps::ConditionType::kFile, "7", apps::PatternMatchType::kFileExtension);
   filters["2"].push_back(std::move(intent_filter4));
 
   auto output = apps::ConvertMojomIntentFiltersToIntentFilters(
diff --git a/components/services/app_service/public/cpp/intent_test_util.cc b/components/services/app_service/public/cpp/intent_test_util.cc
index 50b8c6b..30b14767 100644
--- a/components/services/app_service/public/cpp/intent_test_util.cc
+++ b/components/services/app_service/public/cpp/intent_test_util.cc
@@ -20,9 +20,8 @@
   DCHECK(!mime_type.empty() || !file_extension.empty() || !url_pattern.empty());
   auto intent_filter = std::make_unique<apps::IntentFilter>();
 
-  apps_util::AddSingleValueCondition(apps::ConditionType::kAction, action,
-                                     apps::PatternMatchType::kNone,
-                                     intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kAction, action,
+                                         apps::PatternMatchType::kNone);
 
   apps::ConditionValues condition_values;
   if (!mime_type.empty()) {
@@ -85,9 +84,9 @@
     const std::string& mime_type) {
   auto intent_filter = std::make_unique<apps::IntentFilter>();
 
-  apps_util::AddSingleValueCondition(
-      apps::ConditionType::kAction, kIntentActionSend,
-      apps::PatternMatchType::kNone, intent_filter);
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kAction,
+                                         kIntentActionSend,
+                                         apps::PatternMatchType::kNone);
 
   std::vector<apps::ConditionValuePtr> condition_values;
   condition_values.push_back(std::make_unique<apps::ConditionValue>(
@@ -124,6 +123,55 @@
                           activity_label);
 }
 
+apps::IntentFilterPtr MakeSchemeOnlyFilter(const std::string& scheme) {
+  apps::ConditionValues condition_values;
+  condition_values.push_back(std::make_unique<apps::ConditionValue>(
+      scheme, apps::PatternMatchType::kNone));
+  auto condition = std::make_unique<apps::Condition>(
+      apps::ConditionType::kScheme, std::move(condition_values));
+
+  auto intent_filter = std::make_unique<apps::IntentFilter>();
+  intent_filter->conditions.push_back(std::move(condition));
+
+  return intent_filter;
+}
+
+apps::IntentFilterPtr MakeSchemeAndHostOnlyFilter(const std::string& scheme,
+                                                  const std::string& host) {
+  apps::ConditionValues scheme_condition_values;
+  scheme_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+      scheme, apps::PatternMatchType::kNone));
+  auto scheme_condition = std::make_unique<apps::Condition>(
+      apps::ConditionType::kScheme, std::move(scheme_condition_values));
+
+  apps::ConditionValues host_condition_values;
+  host_condition_values.push_back(std::make_unique<apps::ConditionValue>(
+      host, apps::PatternMatchType::kNone));
+  auto host_condition = std::make_unique<apps::Condition>(
+      apps::ConditionType::kHost, std::move(host_condition_values));
+
+  auto intent_filter = std::make_unique<apps::IntentFilter>();
+  intent_filter->conditions.push_back(std::move(scheme_condition));
+  intent_filter->conditions.push_back(std::move(host_condition));
+
+  return intent_filter;
+}
+
+void AddConditionValue(apps::ConditionType condition_type,
+                       const std::string& value,
+                       apps::PatternMatchType pattern_match_type,
+                       apps::IntentFilterPtr& intent_filter) {
+  for (auto& condition : intent_filter->conditions) {
+    if (condition->condition_type == condition_type) {
+      condition->condition_values.push_back(
+          std::make_unique<apps::ConditionValue>(value, pattern_match_type));
+      return;
+    }
+  }
+  intent_filter->AddSingleValueCondition(condition_type, value,
+                                         pattern_match_type);
+}
+
 apps::mojom::IntentFilterPtr CreateSchemeOnlyFilter(const std::string& scheme) {
   std::vector<apps::mojom::ConditionValuePtr> condition_values;
   condition_values.push_back(apps_util::MakeConditionValue(
diff --git a/components/services/app_service/public/cpp/intent_test_util.h b/components/services/app_service/public/cpp/intent_test_util.h
index a149c3dc..651d64d 100644
--- a/components/services/app_service/public/cpp/intent_test_util.h
+++ b/components/services/app_service/public/cpp/intent_test_util.h
@@ -34,6 +34,21 @@
 apps::IntentFilterPtr MakeURLFilterForView(const std::string& url_pattern,
                                            const std::string& activity_label);
 
+// Creates intent filter that contains only the `scheme`.
+apps::IntentFilterPtr MakeSchemeOnlyFilter(const std::string& scheme);
+
+// Creates intent filter that contains only the `scheme` and `host`.
+apps::IntentFilterPtr MakeSchemeAndHostOnlyFilter(const std::string& scheme,
+                                                  const std::string& host);
+
+// Add a condition value to the |intent_filter|. If the |condition_type|
+// exists, add the condition value to the existing condition, otherwise
+// create new condition.
+void AddConditionValue(apps::ConditionType condition_type,
+                       const std::string& value,
+                       apps::PatternMatchType pattern_match_type,
+                       apps::IntentFilterPtr& intent_filter);
+
 // TODO(crbug.com/1253250): Remove below functions after migrating to non-mojo
 // AppService.
 
diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc
index e82df903e4..bfdd42d 100644
--- a/components/services/app_service/public/cpp/intent_util.cc
+++ b/components/services/app_service/public/cpp/intent_util.cc
@@ -91,6 +91,7 @@
 const char kIntentActionSend[] = "send";
 const char kIntentActionSendMultiple[] = "send_multiple";
 const char kIntentActionCreateNote[] = "create_note";
+const char kUseBrowserForLink[] = "use_browser";
 
 apps::mojom::IntentPtr CreateIntentFromUrl(const GURL& url) {
   auto intent = apps::mojom::Intent::New();
@@ -366,6 +367,43 @@
 
 }  // namespace
 
+bool IsGenericFileHandler(const apps::IntentPtr& intent,
+                          const apps::IntentFilterPtr& filter) {
+  if (!intent || !filter || intent->files.empty()) {
+    return false;
+  }
+
+  std::set<std::string> mime_types;
+  std::set<std::string> file_extensions;
+  filter->GetMimeTypesAndExtensions(mime_types, file_extensions);
+  if (file_extensions.count("*") > 0 || mime_types.count("*") > 0 ||
+      mime_types.count("*/*") > 0) {
+    return true;
+  }
+
+  // If a text/* file handler matches with an unsupported text mime type, we
+  // regard it as a generic match.
+  if (mime_types.count("text/*")) {
+    for (const auto& file : intent->files) {
+      DCHECK(file);
+      if (file->mime_type.has_value() &&
+          blink::IsUnsupportedTextMimeType(file->mime_type.value())) {
+        return true;
+      }
+    }
+  }
+
+  // If directory is selected, it is generic unless mime_types included
+  // 'inode/directory'.
+  for (const auto& file : intent->files) {
+    DCHECK(file);
+    if (file->is_directory.value_or(false)) {
+      return mime_types.count(kMimeTypeInodeDirectory) == 0;
+    }
+  }
+  return false;
+}
+
 bool IsGenericFileHandler(const apps::mojom::IntentPtr& intent,
                           const apps::mojom::IntentFilterPtr& filter) {
   if (!intent->files.has_value())
diff --git a/components/services/app_service/public/cpp/intent_util.h b/components/services/app_service/public/cpp/intent_util.h
index aff734b4..9f7b855 100644
--- a/components/services/app_service/public/cpp/intent_util.h
+++ b/components/services/app_service/public/cpp/intent_util.h
@@ -9,6 +9,7 @@
 
 #include <string>
 
+#include "components/services/app_service/public/cpp/intent.h"
 #include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -27,6 +28,10 @@
 extern const char kIntentActionSendMultiple[];
 extern const char kIntentActionCreateNote[];
 
+// App ID value which can be used as a Preferred App to denote that the browser
+// will open the link, and that we should not prompt the user about it.
+extern const char kUseBrowserForLink[];
+
 struct SharedText {
   std::string text;
   GURL url;
@@ -101,6 +106,11 @@
 // Return true if |filter| only contains file extension pattern matches.
 bool FilterIsForFileExtensions(const apps::mojom::IntentFilterPtr& filter);
 
+bool IsGenericFileHandler(const apps::IntentPtr& intent,
+                          const apps::IntentFilterPtr& filter);
+
+// TODO(crbug.com/1253250): Remove this function after migrating to non-mojo
+// AppService.
 bool IsGenericFileHandler(const apps::mojom::IntentPtr& intent,
                           const apps::mojom::IntentFilterPtr& filter);
 
diff --git a/components/services/app_service/public/cpp/intent_util_unittest.cc b/components/services/app_service/public/cpp/intent_util_unittest.cc
index eecc22b..4eaf6dd 100644
--- a/components/services/app_service/public/cpp/intent_util_unittest.cc
+++ b/components/services/app_service/public/cpp/intent_util_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/values.h"
 #include "components/services/app_service/public/cpp/intent.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
 #include "components/services/app_service/public/cpp/intent_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -380,7 +381,8 @@
       apps_util::ConditionValueMatches("/acb", condition_value_escape_star));
 }
 
-TEST_F(IntentUtilTest, FilterMatchLevel) {
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentUtilTest, FilterMatchLevelMojom) {
   auto filter_scheme_only = apps_util::CreateSchemeOnlyFilter("http");
   auto filter_scheme_and_host_only =
       apps_util::CreateSchemeAndHostOnlyFilter("https", "www.abc.com");
@@ -389,16 +391,16 @@
   auto filter_empty = apps::mojom::IntentFilter::New();
 
   EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_url),
-            apps_util::IntentFilterMatchLevel::kScheme +
-                apps_util::IntentFilterMatchLevel::kHost +
-                apps_util::IntentFilterMatchLevel::kPattern);
+            static_cast<int>(apps::IntentFilterMatchLevel::kScheme) +
+                static_cast<int>(apps::IntentFilterMatchLevel::kHost) +
+                static_cast<int>(apps::IntentFilterMatchLevel::kPattern));
   EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_scheme_and_host_only),
-            apps_util::IntentFilterMatchLevel::kScheme +
-                apps_util::IntentFilterMatchLevel::kHost);
+            static_cast<int>(apps::IntentFilterMatchLevel::kScheme) +
+                static_cast<int>(apps::IntentFilterMatchLevel::kHost));
   EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_scheme_only),
-            apps_util::IntentFilterMatchLevel::kScheme);
+            static_cast<int>(apps::IntentFilterMatchLevel::kScheme));
   EXPECT_EQ(apps_util::GetFilterMatchLevel(filter_empty),
-            apps_util::IntentFilterMatchLevel::kNone);
+            static_cast<int>(apps::IntentFilterMatchLevel::kNone));
 
   EXPECT_TRUE(apps_util::GetFilterMatchLevel(filter_url) >
               apps_util::GetFilterMatchLevel(filter_scheme_and_host_only));
@@ -408,6 +410,39 @@
               apps_util::GetFilterMatchLevel(filter_empty));
 }
 
+TEST_F(IntentUtilTest, FilterMatchLevel) {
+  auto filter_scheme_only = apps_util::MakeSchemeOnlyFilter("http");
+  auto filter_scheme_and_host_only =
+      apps_util::MakeSchemeAndHostOnlyFilter("https", "www.abc.com");
+  auto filter_url =
+      apps_util::MakeIntentFilterForUrlScope(GURL("https:://www.google.com/"));
+  auto filter_empty = std::make_unique<apps::IntentFilter>();
+
+  EXPECT_TRUE(filter_scheme_only->IsBrowserFilter());
+  EXPECT_FALSE(filter_scheme_and_host_only->IsBrowserFilter());
+  EXPECT_FALSE(filter_url->IsBrowserFilter());
+  EXPECT_FALSE(filter_empty->IsBrowserFilter());
+
+  EXPECT_EQ(filter_url->GetFilterMatchLevel(),
+            static_cast<int>(apps::IntentFilterMatchLevel::kScheme) +
+                static_cast<int>(apps::IntentFilterMatchLevel::kHost) +
+                static_cast<int>(apps::IntentFilterMatchLevel::kPattern));
+  EXPECT_EQ(filter_scheme_and_host_only->GetFilterMatchLevel(),
+            static_cast<int>(apps::IntentFilterMatchLevel::kScheme) +
+                static_cast<int>(apps::IntentFilterMatchLevel::kHost));
+  EXPECT_EQ(filter_scheme_only->GetFilterMatchLevel(),
+            static_cast<int>(apps::IntentFilterMatchLevel::kScheme));
+  EXPECT_EQ(filter_empty->GetFilterMatchLevel(),
+            static_cast<int>(apps::IntentFilterMatchLevel::kNone));
+
+  EXPECT_TRUE(filter_url->GetFilterMatchLevel() >
+              filter_scheme_and_host_only->GetFilterMatchLevel());
+  EXPECT_TRUE(filter_scheme_and_host_only->GetFilterMatchLevel() >
+              filter_scheme_only->GetFilterMatchLevel());
+  EXPECT_TRUE(filter_scheme_only->GetFilterMatchLevel() >
+              filter_empty->GetFilterMatchLevel());
+}
+
 // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
 TEST_F(IntentUtilTest, ActionMatchMojom) {
   GURL test_url("https://www.google.com/");
@@ -1336,7 +1371,8 @@
                        {"image/png", "image/jpeg", "text/plain"}));
 }
 
-TEST_F(IntentUtilTest, IsGenericFileHandler) {
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentUtilTest, IsGenericFileHandlerMojom) {
   using apps::mojom::IntentFile;
   using apps::mojom::IntentFilePtr;
   using apps::mojom::IntentFilterPtr;
@@ -1445,6 +1481,116 @@
   EXPECT_FALSE(apps_util::IsGenericFileHandler(intent3, filter12));
 }
 
+TEST_F(IntentUtilTest, IsGenericFileHandler) {
+  using apps::Intent;
+  using apps::IntentFile;
+  using apps::IntentFilePtr;
+  using apps::IntentFilterPtr;
+  using apps::IntentPtr;
+
+  std::vector<IntentFilePtr> intent_files;
+  IntentFilePtr foo = std::make_unique<IntentFile>(test_url("foo.jpg"));
+  foo->mime_type = "image/jpeg";
+  foo->is_directory = false;
+  intent_files.push_back(std::move(foo));
+
+  IntentFilePtr bar = std::make_unique<IntentFile>(test_url("bar.txt"));
+  bar->mime_type = "text/plain";
+  bar->is_directory = false;
+  intent_files.push_back(std::move(bar));
+
+  std::vector<IntentFilePtr> intent_files2;
+  IntentFilePtr foo2 = std::make_unique<IntentFile>(test_url("foo.ics"));
+  foo2->mime_type = "text/calendar";
+  foo2->is_directory = false;
+  intent_files2.push_back(std::move(foo2));
+
+  std::vector<IntentFilePtr> intent_files3;
+  IntentFilePtr foo_dir = std::make_unique<IntentFile>(test_url("foo/"));
+  foo_dir->mime_type = "";
+  foo_dir->is_directory = true;
+  intent_files3.push_back(std::move(foo_dir));
+
+  IntentPtr intent = std::make_unique<Intent>(std::move(intent_files));
+  IntentPtr intent2 = std::make_unique<Intent>(std::move(intent_files2));
+  IntentPtr intent3 = std::make_unique<Intent>(std::move(intent_files3));
+
+  const std::string kLabel = "";
+
+  // extensions: ["*"]
+  IntentFilterPtr filter1 = apps_util::MakeFileFilterForView("", "*", kLabel);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter1));
+  EXPECT_TRUE(filter1->IsFileExtensionsFilter());
+
+  // extensions: ["*", "jpg"]
+  IntentFilterPtr filter2 = apps_util::MakeFileFilterForView("", "*", kLabel);
+  apps_util::AddConditionValue(apps::ConditionType::kFile, "jpg",
+                               apps::PatternMatchType::kFileExtension, filter2);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter2));
+  EXPECT_TRUE(filter2->IsFileExtensionsFilter());
+
+  // extensions: ["jpg"]
+  IntentFilterPtr filter3 = apps_util::MakeFileFilterForView("", "jpg", kLabel);
+  EXPECT_FALSE(apps_util::IsGenericFileHandler(intent, filter3));
+  EXPECT_TRUE(filter3->IsFileExtensionsFilter());
+
+  // types: ["*"]
+  IntentFilterPtr filter4 = apps_util::MakeFileFilterForView("*", "", kLabel);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter4));
+  EXPECT_FALSE(filter4->IsFileExtensionsFilter());
+
+  // types: ["*/*"]
+  IntentFilterPtr filter5 = apps_util::MakeFileFilterForView("*/*", "", kLabel);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter5));
+  EXPECT_FALSE(filter5->IsFileExtensionsFilter());
+
+  // types: ["image/*"]
+  IntentFilterPtr filter6 =
+      apps_util::MakeFileFilterForView("image/*", "", kLabel);
+  // Partial wild card is not generic.
+  EXPECT_FALSE(apps_util::IsGenericFileHandler(intent, filter6));
+  EXPECT_FALSE(filter6->IsFileExtensionsFilter());
+
+  // types: ["*", "image/*"]
+  IntentFilterPtr filter7 = apps_util::MakeFileFilterForView("*", "", kLabel);
+  apps_util::AddConditionValue(apps::ConditionType::kFile, "image/*",
+                               apps::PatternMatchType::kMimeType, filter7);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter7));
+  EXPECT_FALSE(filter7->IsFileExtensionsFilter());
+
+  // extensions: ["*"], types: ["image/*"]
+  IntentFilterPtr filter8 =
+      apps_util::MakeFileFilterForView("image/*", "*", kLabel);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent, filter8));
+  EXPECT_FALSE(filter8->IsFileExtensionsFilter());
+
+  // types: ["text/*"] and target files contain unsupported text mime type, e.g.
+  // text/calendar.
+  IntentFilterPtr filter9 =
+      apps_util::MakeFileFilterForView("text/*", "", kLabel);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent2, filter9));
+  EXPECT_FALSE(filter9->IsFileExtensionsFilter());
+
+  // types: ["text/*"] and target files don't contain unsupported text mime
+  // type.
+  IntentFilterPtr filter10 =
+      apps_util::MakeFileFilterForView("text/*", "", kLabel);
+  EXPECT_FALSE(apps_util::IsGenericFileHandler(intent, filter10));
+  EXPECT_FALSE(filter10->IsFileExtensionsFilter());
+
+  // File is a directory.
+  IntentFilterPtr filter11 =
+      apps_util::MakeFileFilterForView("text/*", "", kLabel);
+  EXPECT_TRUE(apps_util::IsGenericFileHandler(intent3, filter11));
+  EXPECT_FALSE(filter11->IsFileExtensionsFilter());
+
+  // File is a directory, but filter is inode/directory.
+  IntentFilterPtr filter12 =
+      apps_util::MakeFileFilterForView("inode/directory", "", kLabel);
+  EXPECT_FALSE(apps_util::IsGenericFileHandler(intent3, filter12));
+  EXPECT_FALSE(filter12->IsFileExtensionsFilter());
+}
+
 // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
 TEST_F(IntentUtilTest, MojomConvert) {
   const std::string action = apps_util::kIntentActionSend;
diff --git a/components/services/app_service/public/cpp/preferred_apps_list.cc b/components/services/app_service/public/cpp/preferred_apps_list.cc
index 1fb545c6..82400e3 100644
--- a/components/services/app_service/public/cpp/preferred_apps_list.cc
+++ b/components/services/app_service/public/cpp/preferred_apps_list.cc
@@ -9,6 +9,7 @@
 #include "base/containers/contains.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_util.h"
+#include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "url/gurl.h"
@@ -264,7 +265,7 @@
 absl::optional<std::string> PreferredAppsList::FindPreferredAppForIntent(
     const apps::mojom::IntentPtr& intent) const {
   absl::optional<std::string> best_match_app_id = absl::nullopt;
-  int best_match_level = apps_util::IntentFilterMatchLevel::kNone;
+  int best_match_level = static_cast<int>(IntentFilterMatchLevel::kNone);
   for (auto& preferred_app : preferred_apps_) {
     if (apps_util::IntentMatchesFilter(intent, preferred_app->intent_filter)) {
       int match_level =
diff --git a/components/signin/public/identity_manager/account_capabilities.cc b/components/signin/public/identity_manager/account_capabilities.cc
index 58e5187..eb521b9f 100644
--- a/components/signin/public/identity_manager/account_capabilities.cc
+++ b/components/signin/public/identity_manager/account_capabilities.cc
@@ -34,8 +34,7 @@
 AccountCapabilities::GetSupportedAccountCapabilityNames() {
   static base::NoDestructor<std::vector<std::string>> kCapabilityNames{
       {kCanOfferExtendedChromeSyncPromosCapabilityName,
-       kCanRunChromePrivacySandboxTrialsCapabilityName,
-       kIsSubjectToParentalControlsCapabilityName}};
+       kCanRunChromePrivacySandboxTrialsCapabilityName}};
   return *kCapabilityNames;
 }
 
@@ -68,10 +67,6 @@
   return GetCapabilityByName(kCanRunChromePrivacySandboxTrialsCapabilityName);
 }
 
-signin::Tribool AccountCapabilities::is_subject_to_parental_controls() const {
-  return GetCapabilityByName(kIsSubjectToParentalControlsCapabilityName);
-}
-
 bool AccountCapabilities::UpdateWith(const AccountCapabilities& other) {
   bool modified = false;
 
diff --git a/components/signin/public/identity_manager/account_capabilities.h b/components/signin/public/identity_manager/account_capabilities.h
index 6006382..9f85a273 100644
--- a/components/signin/public/identity_manager/account_capabilities.h
+++ b/components/signin/public/identity_manager/account_capabilities.h
@@ -48,9 +48,6 @@
   // Chrome can run privacy sandbox trials for accounts with this capability.
   signin::Tribool can_run_chrome_privacy_sandbox_trials() const;
 
-  // Chrome applies parental controls to accounts with this capability.
-  signin::Tribool is_subject_to_parental_controls() const;
-
   // Whether none of the capabilities has `signin::Tribool::kUnknown`.
   bool AreAllCapabilitiesKnown() const;
 
diff --git a/components/signin/public/identity_manager/account_capabilities_test_mutator.cc b/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
index 3536410..c7db63b 100644
--- a/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
+++ b/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
@@ -30,12 +30,6 @@
       value;
 }
 
-void AccountCapabilitiesTestMutator::set_is_subject_to_parental_controls(
-    bool value) {
-  capabilities_->capabilities_map_[kIsSubjectToParentalControlsCapabilityName] =
-      value;
-}
-
 void AccountCapabilitiesTestMutator::SetAllSupportedCapabilities(bool value) {
   for (const std::string& name :
        AccountCapabilities::GetSupportedAccountCapabilityNames()) {
diff --git a/components/signin/public/identity_manager/account_capabilities_test_mutator.h b/components/signin/public/identity_manager/account_capabilities_test_mutator.h
index 182a197..28198b4 100644
--- a/components/signin/public/identity_manager/account_capabilities_test_mutator.h
+++ b/components/signin/public/identity_manager/account_capabilities_test_mutator.h
@@ -19,7 +19,6 @@
   // Exposes setters for the supported capabilities.
   void set_can_offer_extended_chrome_sync_promos(bool value);
   void set_can_run_chrome_privacy_sandbox_trials(bool value);
-  void set_is_subject_to_parental_controls(bool value);
 
   // Modifies all supported capabilities at once.
   void SetAllSupportedCapabilities(bool value);
diff --git a/components/signin/public/identity_manager/account_capabilities_unittest.cc b/components/signin/public/identity_manager/account_capabilities_unittest.cc
index 01fc029e..81980687 100644
--- a/components/signin/public/identity_manager/account_capabilities_unittest.cc
+++ b/components/signin/public/identity_manager/account_capabilities_unittest.cc
@@ -43,21 +43,6 @@
             signin::Tribool::kFalse);
 }
 
-TEST_F(AccountCapabilitiesTest, IsSubjectToParentalControls) {
-  AccountCapabilities capabilities;
-  EXPECT_EQ(capabilities.is_subject_to_parental_controls(),
-            signin::Tribool::kUnknown);
-
-  AccountCapabilitiesTestMutator mutator(&capabilities);
-  mutator.set_is_subject_to_parental_controls(true);
-  EXPECT_EQ(capabilities.is_subject_to_parental_controls(),
-            signin::Tribool::kTrue);
-
-  mutator.set_is_subject_to_parental_controls(false);
-  EXPECT_EQ(capabilities.is_subject_to_parental_controls(),
-            signin::Tribool::kFalse);
-}
-
 TEST_F(AccountCapabilitiesTest, AreAllCapabilitiesKnown_Empty) {
   AccountCapabilities capabilities;
   EXPECT_FALSE(capabilities.AreAllCapabilitiesKnown());
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc
index 01ad736..e61dbd6 100644
--- a/components/sync/driver/sync_service_crypto.cc
+++ b/components/sync/driver/sync_service_crypto.cc
@@ -368,16 +368,12 @@
       state_.passphrase_key_derivation_params, passphrase);
   DCHECK(nigori);
 
-  std::string bootstrap_token = SerializeNigoriAsBootstrapToken(*nigori);
-  if (SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori))) {
-    // Update the bootstrap token immediately, even if engine has new pending
-    // keys, which aren't decryptable with |nigori|, this is harmless as
-    // bootstrap token is ignored if it doesn't contain the right key.
-    delegate_->SetEncryptionBootstrapToken(bootstrap_token);
-    return true;
-  }
+  // Update the bootstrap token immediately, this is harmless as bootstrap token
+  // is ignored if it doesn't contain the right key.
+  delegate_->SetEncryptionBootstrapToken(
+      SerializeNigoriAsBootstrapToken(*nigori));
 
-  return false;
+  return SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori));
 }
 
 void SyncServiceCrypto::SetDecryptionNigoriKey(std::unique_ptr<Nigori> nigori) {
@@ -389,13 +385,12 @@
     return;
   }
 
-  std::string bootstrap_token = SerializeNigoriAsBootstrapToken(*nigori);
-  if (SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori))) {
-    // Update the bootstrap token immediately, even if engine has new pending
-    // keys, which aren't decryptable with |nigori|, this is harmless as
-    // bootstrap token is ignored if it doesn't contain the right key.
-    delegate_->SetEncryptionBootstrapToken(bootstrap_token);
-  }
+  // Update the bootstrap token immediately, this is harmless as bootstrap token
+  // is ignored if it doesn't contain the right key.
+  delegate_->SetEncryptionBootstrapToken(
+      SerializeNigoriAsBootstrapToken(*nigori));
+
+  SetDecryptionKeyWithoutUpdatingBootstrapToken(std::move(nigori));
 }
 
 std::unique_ptr<Nigori> SyncServiceCrypto::GetDecryptionNigoriKey() const {
diff --git a/components/sync/driver/sync_service_crypto_unittest.cc b/components/sync/driver/sync_service_crypto_unittest.cc
index d74978bf..a08238e8 100644
--- a/components/sync/driver/sync_service_crypto_unittest.cc
+++ b/components/sync/driver/sync_service_crypto_unittest.cc
@@ -463,6 +463,40 @@
   EXPECT_FALSE(crypto_.IsPassphraseRequired());
 }
 
+// Regression test for crbug.com/1306831.
+TEST_F(SyncServiceCryptoTest,
+       ShouldStoreBootstrapTokenBeforeReconfiguringDataTypes) {
+  const std::string kTestPassphrase = "somepassphrase";
+
+  crypto_.SetSyncEngine(CoreAccountInfo(), &engine_);
+  ASSERT_FALSE(crypto_.IsPassphraseRequired());
+
+  crypto_.OnPassphraseRequired(
+      KeyDerivationParams::CreateForPbkdf2(),
+      MakeEncryptedData(kTestPassphrase,
+                        KeyDerivationParams::CreateForPbkdf2()));
+  ASSERT_TRUE(crypto_.IsPassphraseRequired());
+
+  // Entering the correct passphrase should be accepted.
+  EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey(NotNull()))
+      .WillOnce(
+          [&](std::unique_ptr<Nigori>) { crypto_.OnPassphraseAccepted(); });
+
+  // Order of SetEncryptionBootstrapToken() and
+  // ReconfigureDataTypesDueToCrypto() (assuming passphrase is not required upon
+  // reconfiguration) is important as clients rely on this to detect whether
+  // GetDecryptionNigoriKey() can be called.
+  testing::InSequence seq;
+  EXPECT_CALL(delegate_,
+              SetEncryptionBootstrapToken(BootstrapTokenDerivedFrom(
+                  kTestPassphrase, KeyDerivationParams::CreateForPbkdf2())));
+  // The current implementation issues two reconfigurations: one immediately
+  // after checking the passphrase in the UI thread and a second time later when
+  // the engine confirms with OnPassphraseAccepted().
+  EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(2);
+  ASSERT_TRUE(crypto_.SetDecryptionPassphrase(kTestPassphrase));
+}
+
 TEST_F(SyncServiceCryptoTest, ShouldSetupDecryptionWithBootstrapToken) {
   const std::string kTestPassphrase = "somepassphrase";
 
@@ -607,7 +641,6 @@
   // Passing wrong decryption key should be ignored.
   EXPECT_CALL(delegate_, ReconfigureDataTypesDueToCrypto()).Times(0);
   EXPECT_CALL(engine_, SetExplicitPassphraseDecryptionKey).Times(0);
-  EXPECT_CALL(delegate_, SetEncryptionBootstrapToken).Times(0);
   crypto_.SetDecryptionNigoriKey(Nigori::CreateByDerivation(
       KeyDerivationParams::CreateForPbkdf2(), "wrongpassphrase"));
   EXPECT_TRUE(crypto_.IsPassphraseRequired());
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 2e3a5954..5d53c26 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -1429,34 +1429,20 @@
   // device space, it will be contained in in the original scissor.
   // Applying the scissor explicitly means avoiding a clipRect() call and
   // allows more quads to be batched together in a DrawEdgeAAImageSet call
-  float left_inset = local_scissor.x() - visible_rect.x();
-  float top_inset = local_scissor.y() - visible_rect.y();
-  float right_inset = visible_rect.right() - local_scissor.right();
-  float bottom_inset = visible_rect.bottom() - local_scissor.bottom();
+  float x_epsilon = kAAEpsilon / content_device_transform.matrix().rc(0, 0);
+  float y_epsilon = kAAEpsilon / content_device_transform.matrix().rc(1, 1);
 
-  // The scissor is a non-AA clip, so we unset the bit flag for clipped edges.
-  if (left_inset >= kAAEpsilon) {
+  // The scissor is a non-AA clip, so unset the bit flag for clipped edges.
+  if (local_scissor.x() - visible_rect.x() >= x_epsilon)
     aa_flags &= ~SkCanvas::kLeft_QuadAAFlag;
-  } else {
-    left_inset = 0;
-  }
-  if (top_inset >= kAAEpsilon) {
+  if (local_scissor.y() - visible_rect.y() >= y_epsilon)
     aa_flags &= ~SkCanvas::kTop_QuadAAFlag;
-  } else {
-    top_inset = 0;
-  }
-  if (right_inset >= kAAEpsilon) {
+  if (visible_rect.right() - local_scissor.right() >= x_epsilon)
     aa_flags &= ~SkCanvas::kRight_QuadAAFlag;
-  } else {
-    right_inset = 0;
-  }
-  if (bottom_inset >= kAAEpsilon) {
+  if (visible_rect.bottom() - local_scissor.bottom() >= y_epsilon)
     aa_flags &= ~SkCanvas::kBottom_QuadAAFlag;
-  } else {
-    bottom_inset = 0;
-  }
 
-  visible_rect.Inset(left_inset, top_inset, right_inset, bottom_inset);
+  visible_rect.Intersect(local_scissor);
   vis_tex_coords = visible_rect;
   scissor_rect.reset();
 }
diff --git a/content/browser/android/drop_data_android.cc b/content/browser/android/drop_data_android.cc
index c9a8e95..1fd7bc11 100644
--- a/content/browser/android/drop_data_android.cc
+++ b/content/browser/android/drop_data_android.cc
@@ -37,6 +37,7 @@
   ScopedJavaLocalRef<jobject> jgurl;
   if (!drop_data.url.is_empty()) {
     jgurl = url::GURLAndroid::FromNativeGURL(env, drop_data.url);
+    jtext = ConvertUTF16ToJavaString(env, drop_data.url_title);
   }
 
   // If file_contents is not empty, user is trying to drag image out of the
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index 349c873..580347c 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/check.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_trigger.h"
@@ -46,6 +47,21 @@
 
 }  // namespace
 
+// static
+void AttributionDataHostManagerImpl::RecordRegisteredDataPerDataHost(
+    const ReceiverData& receiver_data) {
+  int count = receiver_data.num_data_registered;
+  DCHECK_GE(count, 0);
+
+  if (receiver_data.source_declared_destination_origin.opaque()) {
+    base::UmaHistogramExactLinear("Conversions.RegisteredTriggersPerDataHost",
+                                  count, 101);
+  } else {
+    base::UmaHistogramExactLinear("Conversions.RegisteredSourcesPerDataHost",
+                                  count, 101);
+  }
+}
+
 AttributionDataHostManagerImpl::AttributionDataHostManagerImpl(
     AttributionManager* attribution_manager)
     : attribution_manager_(attribution_manager) {
@@ -58,7 +74,11 @@
       base::Unretained(this)));
 }
 
-AttributionDataHostManagerImpl::~AttributionDataHostManagerImpl() = default;
+AttributionDataHostManagerImpl::~AttributionDataHostManagerImpl() {
+  for (const auto& [id, data] : receiver_data_) {
+    RecordRegisteredDataPerDataHost(data);
+  }
+}
 
 void AttributionDataHostManagerImpl::RegisterDataHost(
     mojo::PendingReceiver<blink::mojom::AttributionDataHost> data_host,
@@ -109,9 +129,12 @@
   if (data->destination.opaque())
     return;
 
-  auto [it, inserted] =
-      receiver_data_.emplace(receivers_.current_receiver(), data->destination);
-  if (!inserted && data->destination != it->second)
+  auto [it, inserted] = receiver_data_.emplace(
+      receivers_.current_receiver(),
+      ReceiverData{.source_declared_destination_origin = data->destination,
+                   .num_data_registered = 0});
+  if (!inserted &&
+      data->destination != it->second.source_declared_destination_origin)
     return;
 
   const FrozenContext& context = receivers_.current_context();
@@ -128,9 +151,13 @@
       break;
     case AttributionSourceType::kEvent:
       // For event source verify that all sources are consistent.
-      auto result = receiver_data_.emplace(receivers_.current_receiver(),
-                                           data->destination);
-      if (!result.second && data->destination != result.first->second)
+      auto result = receiver_data_.emplace(
+          receivers_.current_receiver(),
+          ReceiverData{.source_declared_destination_origin = data->destination,
+                       .num_data_registered = 0});
+      if (!result.second &&
+          data->destination !=
+              result.first->second.source_declared_destination_origin)
         return;
       break;
   }
@@ -156,6 +183,8 @@
   if (!aggregatable_source.has_value())
     return;
 
+  it->second.num_data_registered++;
+
   StorableSource storable_source(CommonSourceInfo(
       data->source_event_id, context.context_origin, data->destination,
       reporting_origin, source_time,
@@ -173,9 +202,11 @@
     blink::mojom::AttributionTriggerDataPtr data) {
   // TODO(linnan): Log metrics for early returns.
 
-  auto [it, inserted] =
-      receiver_data_.emplace(receivers_.current_receiver(), url::Origin());
-  if (!it->second.opaque())
+  auto [it, inserted] = receiver_data_.emplace(
+      receivers_.current_receiver(),
+      ReceiverData{.source_declared_destination_origin = url::Origin(),
+                   .num_data_registered = 0});
+  if (!it->second.source_declared_destination_origin.opaque())
     return;
 
   const FrozenContext& context = receivers_.current_context();
@@ -231,6 +262,8 @@
   if (!aggregatable_trigger.has_value())
     return;
 
+  it->second.num_data_registered++;
+
   AttributionTrigger trigger(
       /*destination_origin=*/context.context_origin, reporting_origin,
       std::move(*filters),
@@ -242,7 +275,12 @@
 }
 
 void AttributionDataHostManagerImpl::OnDataHostDisconnected() {
-  receiver_data_.erase(receivers_.current_receiver());
+  auto iter = receiver_data_.find(receivers_.current_receiver());
+  DCHECK(iter != receiver_data_.end());
+
+  RecordRegisteredDataPerDataHost(iter->second);
+
+  receiver_data_.erase(iter);
 }
 
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
index 4949e90..3423e25 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
@@ -74,6 +74,14 @@
     url::Origin destination;
   };
 
+  struct ReceiverData {
+    url::Origin source_declared_destination_origin;
+    int num_data_registered;
+  };
+
+  static void RecordRegisteredDataPerDataHost(
+      const ReceiverData& receiver_data);
+
   // blink::mojom::AttributionDataHost:
   void SourceDataAvailable(
       blink::mojom::AttributionSourceDataPtr data) override;
@@ -94,7 +102,7 @@
   // `AttributionDataHost`.
   //
   // If the receiver is processing triggers, stores an opaque origin.
-  base::flat_map<mojo::ReceiverId, url::Origin> receiver_data_;
+  base::flat_map<mojo::ReceiverId, ReceiverData> receiver_data_;
 
   // Map which stores pending receivers for data hosts which are going to
   // register sources associated with a navigation. These are not added to
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index a321bb0..25d2655 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -13,6 +13,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "content/browser/attribution_reporting/attribution_aggregatable_source.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_source_type.h"
@@ -47,15 +48,18 @@
  public:
   AttributionDataHostManagerImplTest()
       : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
-        data_host_manager_(&mock_manager_) {}
+        data_host_manager_(
+            std::make_unique<AttributionDataHostManagerImpl>(&mock_manager_)) {}
 
  protected:
   BrowserTaskEnvironment task_environment_;
   MockAttributionManager mock_manager_;
-  AttributionDataHostManagerImpl data_host_manager_;
+  std::unique_ptr<AttributionDataHostManagerImpl> data_host_manager_;
 };
 
 TEST_F(AttributionDataHostManagerImplTest, SourceDataHost_SourceRegistered) {
+  base::HistogramTester histograms;
+
   auto page_origin = url::Origin::Create(GURL("https://page.example"));
   auto destination_origin =
       url::Origin::Create(GURL("https://trigger.example"));
@@ -75,7 +79,7 @@
                           .Build())))));
 
   mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-  data_host_manager_.RegisterDataHost(
+  data_host_manager_->RegisterDataHost(
       data_host_remote.BindNewPipeAndPassReceiver(), page_origin);
 
   auto source_data = blink::mojom::AttributionSourceData::New();
@@ -93,10 +97,17 @@
           .Build();
   data_host_remote->SourceDataAvailable(std::move(source_data));
   data_host_remote.FlushForTesting();
+
+  data_host_manager_.reset();
+
+  histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 1,
+                               1);
 }
 
 TEST_F(AttributionDataHostManagerImplTest,
        SourceDataHost_OriginTrustworthyChecksPerformed) {
+  base::HistogramTester histograms;
+
   const char kLocalHost[] = "http://localhost";
 
   struct {
@@ -135,7 +146,7 @@
     EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.source_expected);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL(test_case.source_origin)));
 
@@ -152,6 +163,14 @@
 
     Mock::VerifyAndClear(&mock_manager_);
   }
+
+  data_host_manager_.reset();
+
+  histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 1,
+                               3);
+  // Untrustworthy source origin doesn't register data host.
+  histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 0,
+                               2);
 }
 
 TEST_F(AttributionDataHostManagerImplTest,
@@ -161,7 +180,7 @@
     EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://page.example")));
 
@@ -205,7 +224,7 @@
     EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://page.example")));
 
@@ -227,6 +246,8 @@
 
 TEST_F(AttributionDataHostManagerImplTest,
        SourceDataHost_ReceiverDestinationCheckPerformed) {
+  base::HistogramTester histograms;
+
   Checkpoint checkpoint;
   {
     InSequence seq;
@@ -246,7 +267,7 @@
   auto reporting_origin = url::Origin::Create(GURL("https://reporter.example"));
 
   mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-  data_host_manager_.RegisterDataHost(
+  data_host_manager_->RegisterDataHost(
       data_host_remote.BindNewPipeAndPassReceiver(), page_origin);
 
   auto source_data = blink::mojom::AttributionSourceData::New();
@@ -273,6 +294,11 @@
   checkpoint.Call(3);
   data_host_remote->SourceDataAvailable(std::move(source_data));
   data_host_remote.FlushForTesting();
+
+  data_host_manager_.reset();
+
+  histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 2,
+                               1);
 }
 
 TEST_F(AttributionDataHostManagerImplTest,
@@ -314,7 +340,7 @@
     EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://page.example")));
 
@@ -333,6 +359,8 @@
 }
 
 TEST_F(AttributionDataHostManagerImplTest, TriggerDataHost_TriggerRegistered) {
+  base::HistogramTester histograms;
+
   auto destination_origin =
       url::Origin::Create(GURL("https://trigger.example"));
   auto reporting_origin = url::Origin::Create(GURL("https://reporter.example"));
@@ -368,7 +396,7 @@
       })));
 
   mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-  data_host_manager_.RegisterDataHost(
+  data_host_manager_->RegisterDataHost(
       data_host_remote.BindNewPipeAndPassReceiver(), destination_origin);
 
   auto trigger_data = blink::mojom::AttributionTriggerData::New();
@@ -400,10 +428,17 @@
 
   data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   data_host_remote.FlushForTesting();
+
+  data_host_manager_.reset();
+
+  histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 1,
+                               1);
 }
 
 TEST_F(AttributionDataHostManagerImplTest,
        TriggerDataHost_OriginTrustworthyChecksPerformed) {
+  base::HistogramTester histograms;
+
   const char kLocalHost[] = "http://localhost";
 
   struct {
@@ -432,7 +467,7 @@
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.trigger_expected);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL(test_case.destination_origin)));
 
@@ -449,6 +484,14 @@
 
     Mock::VerifyAndClear(&mock_manager_);
   }
+
+  data_host_manager_.reset();
+
+  histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 1,
+                               3);
+  // Untrustworthy destination origin doesn't register data host.
+  histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 0,
+                               1);
 }
 
 TEST_F(AttributionDataHostManagerImplTest,
@@ -458,7 +501,7 @@
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://trigger.example")));
 
@@ -486,7 +529,7 @@
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://trigger.example")));
 
@@ -520,7 +563,7 @@
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://trigger.example")));
 
@@ -562,7 +605,7 @@
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.expected);
 
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-    data_host_manager_.RegisterDataHost(
+    data_host_manager_->RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://trigger.example")));
 
@@ -593,6 +636,8 @@
 
 TEST_F(AttributionDataHostManagerImplTest,
        TriggerDataHost_ReceiverModeCheckPerformed) {
+  base::HistogramTester histograms;
+
   Checkpoint checkpoint;
   {
     InSequence seq;
@@ -611,7 +656,7 @@
   auto reporting_origin = url::Origin::Create(GURL("https://reporter.example"));
 
   mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-  data_host_manager_.RegisterDataHost(
+  data_host_manager_->RegisterDataHost(
       data_host_remote.BindNewPipeAndPassReceiver(), destination_origin);
 
   auto trigger_data = blink::mojom::AttributionTriggerData::New();
@@ -644,10 +689,18 @@
 
   data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   data_host_remote.FlushForTesting();
+
+  data_host_manager_.reset();
+
+  histograms.ExpectTotalCount("Conversions.RegisteredSourcesPerDataHost", 0);
+  histograms.ExpectBucketCount("Conversions.RegisteredTriggersPerDataHost", 3,
+                               1);
 }
 
 TEST_F(AttributionDataHostManagerImplTest,
        SourceDataHost_ReceiverModeCheckPerformed) {
+  base::HistogramTester histograms;
+
   Checkpoint checkpoint;
   {
     InSequence seq;
@@ -667,7 +720,7 @@
   auto reporting_origin = url::Origin::Create(GURL("https://reporter.example"));
 
   mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
-  data_host_manager_.RegisterDataHost(
+  data_host_manager_->RegisterDataHost(
       data_host_remote.BindNewPipeAndPassReceiver(), page_origin);
 
   auto source_data = blink::mojom::AttributionSourceData::New();
@@ -700,6 +753,12 @@
 
   data_host_remote->SourceDataAvailable(std::move(source_data));
   data_host_remote.FlushForTesting();
+
+  data_host_manager_.reset();
+
+  histograms.ExpectBucketCount("Conversions.RegisteredSourcesPerDataHost", 3,
+                               1);
+  histograms.ExpectTotalCount("Conversions.RegisteredTriggersPerDataHost", 0);
 }
 
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_report.cc b/content/browser/attribution_reporting/attribution_report.cc
index b6023208..0d30bf7 100644
--- a/content/browser/attribution_reporting/attribution_report.cc
+++ b/content/browser/attribution_reporting/attribution_report.cc
@@ -26,6 +26,16 @@
 
 namespace content {
 
+namespace {
+
+int64_t EncodeTimeRoundDownToWholeDayInSeconds(base::Time time) {
+  return (time - base::Time::UnixEpoch())
+      .FloorToMultiple(base::Days(1))
+      .InSeconds();
+}
+
+}  // namespace
+
 AttributionReport::EventLevelData::EventLevelData(
     uint64_t trigger_data,
     int64_t priority,
@@ -203,13 +213,10 @@
       dict.emplace("attribution_destination",
                    common_info.ConversionDestination().Serialize());
 
-      // source_registration_time is rounded to the nearest whole day and in
-      // seconds.
+      // source_registration_time is rounded down to whole day and in seconds.
       dict.emplace("source_registration_time",
-                   base::NumberToString(
-                       (common_info.impression_time() - base::Time::UnixEpoch())
-                           .RoundToMultiple(base::Days(1))
-                           .InSeconds()));
+                   base::NumberToString(EncodeTimeRoundDownToWholeDayInSeconds(
+                       common_info.impression_time())));
 
       if (absl::optional<uint64_t> debug_key = common_info.debug_key())
         dict.emplace("source_debug_key", base::NumberToString(*debug_key));
@@ -249,6 +256,10 @@
   static constexpr char kVersion[] = "";
   value.emplace("version", kVersion);
 
+  value.emplace("source_registration_time",
+                EncodeTimeRoundDownToWholeDayInSeconds(
+                    common_source_info.impression_time()));
+
   absl::optional<std::vector<uint8_t>> bytes =
       cbor::Writer::Write(cbor::Value(std::move(value)));
   DCHECK(bytes.has_value());
diff --git a/content/browser/attribution_reporting/attribution_report_unittest.cc b/content/browser/attribution_reporting/attribution_report_unittest.cc
index 7e9a20d4..d687e2ff 100644
--- a/content/browser/attribution_reporting/attribution_report_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_report_unittest.cc
@@ -32,9 +32,10 @@
       {"14288 * 86400000", 1234483200000, "1234483200"},
       {"14288 * 86400000 + 1", 1234483200001, "1234483200"},
       {"14288.5 * 86400000 - 1", 1234526399999, "1234483200"},
-      {"14288.5 * 86400000", 1234526400000, "1234569600"},
-      {"14288.5 * 86400000 + 1", 1234526400001, "1234569600"},
-      {"14289 * 86400000 -1", 1234569599999, "1234569600"},
+      {"14288.5 * 86400000", 1234526400000, "1234483200"},
+      {"14288.5 * 86400000 + 1", 1234526400001, "1234483200"},
+      {"14289 * 86400000 -1", 1234569599999, "1234483200"},
+      {"14289 * 86400000", 1234569600000, "1234569600"},
   };
 
   for (const auto& test_case : kTestCases) {
@@ -81,21 +82,14 @@
 }
 
 TEST(AttributionReportTest, PrivacyBudgetKey) {
-  // Pre-hashed CBOR bytes
-  // { 0xA4, 0x67, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x60, 0x6B, 0x64,
-  //   0x65, 0x73, 0x74, 0x69, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x77, 0x68,
-  //   0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x63, 0x6F, 0x6E, 0x76, 0x65,
-  //   0x72, 0x73, 0x69, 0x6F, 0x6E, 0x2E, 0x74, 0x65, 0x73, 0x74, 0x6B, 0x73,
-  //   0x6F, 0x75, 0x72, 0x63, 0x65, 0x5F, 0x73, 0x69, 0x74, 0x65, 0x77, 0x68,
-  //   0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x69, 0x6D, 0x70, 0x72, 0x65,
-  //   0x73, 0x73, 0x69, 0x6F, 0x6E, 0x2E, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72,
-  //   0x65, 0x70, 0x6F, 0x72, 0x74, 0x69, 0x6E, 0x67, 0x5F, 0x6F, 0x72, 0x69,
-  //   0x67, 0x69, 0x6E, 0x73, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F,
-  //   0x72, 0x65, 0x70, 0x6F, 0x72, 0x74, 0x2E, 0x74, 0x65, 0x73, 0x74 }
+  // Pre-hashed CBOR bytes, base64-encoded for brevity:
+  // "pWd2ZXJzaW9uYGtkZXN0aW5hdGlvbndodHRwczovL2NvbnZlcnNpb24udGVzdGtzb3VyY2Vf"
+  // "c2l0ZXdodHRwczovL2ltcHJlc3Npb24udGVzdHByZXBvcnRpbmdfb3JpZ2luc2h0dHBzOi8v"
+  // "cmVwb3J0LnRlc3R4GHNvdXJjZV9yZWdpc3RyYXRpb25fdGltZRpJlLgA"
 
-  // base64 encoded SHA256 hash string of the bytes above.
+  // base64-encoded SHA256 hash string of the bytes above.
   const std::string kExpectedPrivacyBudgetKey(
-      "NOM7HGJIb2ReR2jRlz1E0WIywBdUB/qLC6nCFyDqmRQ=");
+      "aOEbtVxG8dYzAR2K/xuW/OppNaQikp5RdjAXshOQ9w8=");
 
   AttributionReport report =
       ReportBuilder(AttributionInfoBuilder(
diff --git a/content/browser/attribution_reporting/attribution_src_browsertest.cc b/content/browser/attribution_reporting/attribution_src_browsertest.cc
index 65a223b..7ceadbef 100644
--- a/content/browser/attribution_reporting/attribution_src_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_src_browsertest.cc
@@ -79,11 +79,6 @@
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
-    embedded_test_server()->ServeFilesFromSourceDirectory(
-        "content/test/data/attribution_reporting");
-    embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
-    content::SetupCrossSiteRedirector(embedded_test_server());
-    ASSERT_TRUE(embedded_test_server()->Start());
 
     https_server_ = std::make_unique<net::EmbeddedTestServer>(
         net::EmbeddedTestServer::TYPE_HTTPS);
@@ -91,7 +86,6 @@
     net::test_server::RegisterDefaultHandlers(https_server_.get());
     https_server_->ServeFilesFromSourceDirectory(
         "content/test/data/attribution_reporting");
-    https_server_->ServeFilesFromSourceDirectory("content/test/data");
     SetupCrossSiteRedirector(https_server_.get());
     ASSERT_TRUE(https_server_->Start());
   }
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index acaa8d3..e80c191 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -159,6 +159,30 @@
   ExpectRestored(FROM_HERE);
 }
 
+// Confirms that an active page using a dedicated worker that calls
+// importScripts won't trigger an eviction IPC, causing the page to reload.
+// Regression test for https://crbug.com/1305041.
+IN_PROC_BROWSER_TEST_P(
+    BackForwardCacheWithDedicatedWorkerBrowserTest,
+    PageWithDedicatedWorkerAndImportScriptsWontTriggerReload) {
+  CreateHttpsServer();
+  ASSERT_TRUE(https_server()->Start());
+
+  EXPECT_TRUE(NavigateToURL(
+      shell(), https_server()->GetURL(
+                   "a.test",
+                   "/back_forward_cache/"
+                   "page_with_dedicated_worker_and_importscripts.html")));
+  // Wait until the importScripts() call finished running.
+  EXPECT_EQ(42, EvalJs(current_frame_host(), "window.receivedMessagePromise"));
+
+  // If the importScripts() call triggered an eviction, a reload will be
+  // triggered due to the "evict after docment is restored" will be hit, as the
+  // page is not in back/forward cache.
+  EXPECT_FALSE(
+      web_contents()->GetPrimaryFrameTree().root()->navigation_request());
+}
+
 // Confirms that a page using a dedicated worker with WebTransport is not
 // cached.
 // TODO(crbug.com/1299018): Flakes on Linux.
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc
index 63319aa..157ca86 100644
--- a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc
+++ b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.cc
@@ -53,11 +53,11 @@
 }
 
 void BrowsingTopicsSiteDataManagerImpl::OnBrowsingTopicsApiUsed(
-    const browsing_topics::HashedHost& hashed_top_host,
+    const browsing_topics::HashedHost& hashed_main_frame_host,
     const base::flat_set<browsing_topics::HashedDomain>&
         hashed_context_domains) {
   storage_.AsyncCall(&BrowsingTopicsSiteDataStorage::OnBrowsingTopicsApiUsed)
-      .WithArgs(hashed_top_host, hashed_context_domains);
+      .WithArgs(hashed_main_frame_host, hashed_context_domains);
 }
 
 }  // namespace content
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h
index d81c2bd7..10dc0a5 100644
--- a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h
+++ b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h
@@ -39,7 +39,7 @@
       GetBrowsingTopicsApiUsageCallback callback) override;
 
   void OnBrowsingTopicsApiUsed(
-      const browsing_topics::HashedHost& hashed_top_host,
+      const browsing_topics::HashedHost& hashed_main_frame_host,
       const base::flat_set<browsing_topics::HashedDomain>&
           hashed_context_domains) override;
 
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc
index 11f0161..43990e01 100644
--- a/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc
+++ b/content/browser/browsing_topics/browsing_topics_site_data_manager_impl_unittest.cc
@@ -36,7 +36,7 @@
   base::Time initial_time = base::Time::Now();
 
   topics_manager_->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)});
 
   size_t query_result_count = 0;
@@ -72,7 +72,7 @@
             EXPECT_TRUE(result.success);
             EXPECT_EQ(result.api_usage_contexts.size(), 1u);
 
-            EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host,
+            EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host,
                       browsing_topics::HashedHost(123));
             EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain,
                       browsing_topics::HashedDomain(456));
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage.cc b/content/browser/browsing_topics/browsing_topics_site_data_storage.cc
index c6ef8ea..2454ae7 100644
--- a/content/browser/browsing_topics/browsing_topics_site_data_storage.cc
+++ b/content/browser/browsing_topics/browsing_topics_site_data_storage.cc
@@ -68,7 +68,7 @@
 
   static constexpr char kGetApiUsageSql[] =
       // clang-format off
-      "SELECT hashed_context_domain,hashed_top_host,last_usage_time "
+      "SELECT hashed_context_domain,hashed_main_frame_host,last_usage_time "
           "FROM browsing_topics_api_usages "
           "WHERE last_usage_time>=? AND last_usage_time<? "
           "ORDER BY last_usage_time DESC "
@@ -90,7 +90,7 @@
     browsing_topics::ApiUsageContext usage_context;
     usage_context.hashed_context_domain =
         browsing_topics::HashedDomain(statement.ColumnInt64(0));
-    usage_context.hashed_top_host =
+    usage_context.hashed_main_frame_host =
         browsing_topics::HashedHost(statement.ColumnInt64(1));
     usage_context.time = statement.ColumnTime(2);
 
@@ -104,7 +104,7 @@
 }
 
 void BrowsingTopicsSiteDataStorage::OnBrowsingTopicsApiUsed(
-    const browsing_topics::HashedHost& hashed_top_host,
+    const browsing_topics::HashedHost& hashed_main_frame_host,
     const base::flat_set<browsing_topics::HashedDomain>&
         hashed_context_domains) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -123,14 +123,14 @@
     static constexpr char kInsertApiUsageSql[] =
         // clang-format off
         "INSERT OR REPLACE INTO browsing_topics_api_usages "
-            "(hashed_context_domain,hashed_top_host,last_usage_time) "
+            "(hashed_context_domain,hashed_main_frame_host,last_usage_time) "
             "VALUES (?,?,?)";
     // clang-format on
 
     sql::Statement insert_api_usage_statement(
         db_->GetCachedStatement(SQL_FROM_HERE, kInsertApiUsageSql));
     insert_api_usage_statement.BindInt64(0, hashed_context_domain.value());
-    insert_api_usage_statement.BindInt64(1, hashed_top_host.value());
+    insert_api_usage_statement.BindInt64(1, hashed_main_frame_host.value());
     insert_api_usage_statement.BindTime(2, current_time);
 
     if (!insert_api_usage_statement.Run())
@@ -211,9 +211,9 @@
       // clang-format off
       "CREATE TABLE IF NOT EXISTS browsing_topics_api_usages("
           "hashed_context_domain INTEGER NOT NULL,"
-          "hashed_top_host INTEGER NOT NULL,"
+          "hashed_main_frame_host INTEGER NOT NULL,"
           "last_usage_time INTEGER NOT NULL,"
-          "PRIMARY KEY (hashed_context_domain,hashed_top_host))";
+          "PRIMARY KEY (hashed_context_domain,hashed_main_frame_host))";
   // clang-format on
   if (!db_->Execute(kBrowsingTopicsApiUsagesTableSql))
     return false;
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage.h b/content/browser/browsing_topics/browsing_topics_site_data_storage.h
index 5e0815c3..590d3b58 100644
--- a/content/browser/browsing_topics/browsing_topics_site_data_storage.h
+++ b/content/browser/browsing_topics/browsing_topics_site_data_storage.h
@@ -56,7 +56,7 @@
   // Persist the browsing topics api usage context to storage. Called when the
   // usage is detected in a context on a page.
   void OnBrowsingTopicsApiUsed(
-      const browsing_topics::HashedHost& hashed_top_host,
+      const browsing_topics::HashedHost& hashed_main_frame_host,
       const base::flat_set<browsing_topics::HashedDomain>&
           hashed_context_domains);
 
diff --git a/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc b/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc
index 76e8d204..1e3bec1 100644
--- a/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc
+++ b/content/browser/browsing_topics/browsing_topics_site_data_storage_unittest.cc
@@ -123,7 +123,7 @@
   // and [sqlite_autoindex_meta_1].
   EXPECT_EQ(3u, sql::test::CountSQLIndices(&db));
 
-  // `hashed_context_domain`, `hashed_top_host`, and `last_usage_time`.
+  // `hashed_context_domain`, `hashed_main_frame_host`, and `last_usage_time`.
   EXPECT_EQ(3u,
             sql::test::CountTableColumns(&db, "browsing_topics_api_usages"));
 
@@ -206,7 +206,7 @@
 TEST_F(BrowsingTopicsSiteDataStorageTest, OnBrowsingTopicsApiUsed_SingleEntry) {
   OpenDatabase();
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)});
   CloseDatabase();
 
@@ -215,17 +215,18 @@
   EXPECT_EQ(1u, CountApiUsagesEntries(db));
 
   const char kGetAllEntriesSql[] =
-      "SELECT hashed_context_domain, hashed_top_host, last_usage_time FROM "
+      "SELECT hashed_context_domain, hashed_main_frame_host, last_usage_time "
+      "FROM "
       "browsing_topics_api_usages";
   sql::Statement s(db.GetUniqueStatement(kGetAllEntriesSql));
   EXPECT_TRUE(s.Step());
 
   int64_t hashed_context_domain = s.ColumnInt64(0);
-  int64_t hashed_top_host = s.ColumnInt64(1);
+  int64_t hashed_main_frame_host = s.ColumnInt64(1);
   base::Time time = s.ColumnTime(2);
 
   EXPECT_EQ(hashed_context_domain, 456);
-  EXPECT_EQ(hashed_top_host, 123);
+  EXPECT_EQ(hashed_main_frame_host, 123);
   EXPECT_EQ(time, base::Time::Now());
 
   EXPECT_FALSE(s.Step());
@@ -235,17 +236,17 @@
        OnBrowsingTopicsApiUsed_MultipleEntries) {
   OpenDatabase();
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(456),
                                   browsing_topics::HashedDomain(789)});
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(456),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(456),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(789)});
   CloseDatabase();
 
@@ -254,9 +255,10 @@
   EXPECT_EQ(4u, CountApiUsagesEntries(db));
 
   const char kGetAllEntriesSql[] =
-      "SELECT hashed_context_domain, hashed_top_host, last_usage_time FROM "
+      "SELECT hashed_context_domain, hashed_main_frame_host, last_usage_time "
+      "FROM "
       "browsing_topics_api_usages "
-      "ORDER BY last_usage_time, hashed_top_host, hashed_context_domain";
+      "ORDER BY last_usage_time, hashed_main_frame_host, hashed_context_domain";
 
   sql::Statement s(db.GetUniqueStatement(kGetAllEntriesSql));
 
@@ -264,11 +266,11 @@
     EXPECT_TRUE(s.Step());
 
     int64_t hashed_context_domain = s.ColumnInt64(0);
-    int64_t hashed_top_host = s.ColumnInt64(1);
+    int64_t hashed_main_frame_host = s.ColumnInt64(1);
     base::Time time = s.ColumnTime(2);
 
     EXPECT_EQ(hashed_context_domain, 123);
-    EXPECT_EQ(hashed_top_host, 123);
+    EXPECT_EQ(hashed_main_frame_host, 123);
     EXPECT_EQ(time, base::Time::Now() - base::Seconds(1));
   }
 
@@ -276,11 +278,11 @@
     EXPECT_TRUE(s.Step());
 
     int64_t hashed_context_domain = s.ColumnInt64(0);
-    int64_t hashed_top_host = s.ColumnInt64(1);
+    int64_t hashed_main_frame_host = s.ColumnInt64(1);
     base::Time time = s.ColumnTime(2);
 
     EXPECT_EQ(hashed_context_domain, 456);
-    EXPECT_EQ(hashed_top_host, 123);
+    EXPECT_EQ(hashed_main_frame_host, 123);
     EXPECT_EQ(time, base::Time::Now());
   }
 
@@ -288,11 +290,11 @@
     EXPECT_TRUE(s.Step());
 
     int64_t hashed_context_domain = s.ColumnInt64(0);
-    int64_t hashed_top_host = s.ColumnInt64(1);
+    int64_t hashed_main_frame_host = s.ColumnInt64(1);
     base::Time time = s.ColumnTime(2);
 
     EXPECT_EQ(hashed_context_domain, 789u);
-    EXPECT_EQ(hashed_top_host, 123);
+    EXPECT_EQ(hashed_main_frame_host, 123);
     EXPECT_EQ(time, base::Time::Now());
   }
 
@@ -300,11 +302,11 @@
     EXPECT_TRUE(s.Step());
 
     int64_t hashed_context_domain = s.ColumnInt64(0);
-    int64_t hashed_top_host = s.ColumnInt64(1);
+    int64_t hashed_main_frame_host = s.ColumnInt64(1);
     base::Time time = s.ColumnTime(2);
 
     EXPECT_EQ(hashed_context_domain, 789u);
-    EXPECT_EQ(hashed_top_host, 456);
+    EXPECT_EQ(hashed_main_frame_host, 456);
     EXPECT_EQ(time, base::Time::Now());
   }
 
@@ -315,13 +317,13 @@
   OpenDatabase();
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
@@ -335,14 +337,14 @@
   EXPECT_TRUE(result.success);
   EXPECT_EQ(result.api_usage_contexts.size(), 2u);
 
-  EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host,
+  EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host,
             browsing_topics::HashedHost(123));
   EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain,
             browsing_topics::HashedDomain(456));
   EXPECT_EQ(result.api_usage_contexts[0].time,
             base::Time::Now() - base::Seconds(1));
 
-  EXPECT_EQ(result.api_usage_contexts[1].hashed_top_host,
+  EXPECT_EQ(result.api_usage_contexts[1].hashed_main_frame_host,
             browsing_topics::HashedHost(123));
   EXPECT_EQ(result.api_usage_contexts[1].hashed_context_domain,
             browsing_topics::HashedDomain(123));
@@ -355,13 +357,13 @@
   OpenDatabase();
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
@@ -374,7 +376,7 @@
 
   EXPECT_TRUE(result.success);
   EXPECT_EQ(result.api_usage_contexts.size(), 1u);
-  EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host,
+  EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host,
             browsing_topics::HashedHost(123));
   EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain,
             browsing_topics::HashedDomain(456));
@@ -392,13 +394,13 @@
   OpenDatabase();
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
@@ -412,17 +414,18 @@
 
   // The `ExpireDataBefore()` should have deleted the first inserted entry.
   const char kGetAllEntriesSql[] =
-      "SELECT hashed_context_domain, hashed_top_host, last_usage_time FROM "
+      "SELECT hashed_context_domain, hashed_main_frame_host, last_usage_time "
+      "FROM "
       "browsing_topics_api_usages";
   sql::Statement s(db.GetUniqueStatement(kGetAllEntriesSql));
   EXPECT_TRUE(s.Step());
 
   int64_t hashed_context_domain = s.ColumnInt64(0);
-  int64_t hashed_top_host = s.ColumnInt64(1);
+  int64_t hashed_main_frame_host = s.ColumnInt64(1);
   base::Time time = s.ColumnTime(2);
 
   EXPECT_EQ(hashed_context_domain, 456);
-  EXPECT_EQ(hashed_top_host, 123);
+  EXPECT_EQ(hashed_main_frame_host, 123);
   EXPECT_EQ(time, base::Time::Now() - base::Seconds(1));
 
   EXPECT_FALSE(s.Step());
@@ -445,13 +448,13 @@
   OpenDatabase();
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(123)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
 
   topics_storage()->OnBrowsingTopicsApiUsed(
-      /*hashed_top_host=*/browsing_topics::HashedHost(123),
+      /*hashed_main_frame_host=*/browsing_topics::HashedHost(123),
       /*hashed_context_domains=*/{browsing_topics::HashedDomain(456)});
 
   task_environment_.FastForwardBy(base::Seconds(1));
@@ -466,7 +469,7 @@
   EXPECT_TRUE(result.success);
   EXPECT_EQ(result.api_usage_contexts.size(), 1u);
 
-  EXPECT_EQ(result.api_usage_contexts[0].hashed_top_host,
+  EXPECT_EQ(result.api_usage_contexts[0].hashed_main_frame_host,
             browsing_topics::HashedHost(123));
   EXPECT_EQ(result.api_usage_contexts[0].hashed_context_domain,
             browsing_topics::HashedDomain(456));
diff --git a/content/browser/client_hints/client_hints.cc b/content/browser/client_hints/client_hints.cc
index 03c0d36b..0c5fe1b 100644
--- a/content/browser/client_hints/client_hints.cc
+++ b/content/browser/client_hints/client_hints.cc
@@ -1063,6 +1063,8 @@
       enabled_hints.GetEnabledHints();
   PersistAcceptCH(origin, delegate, persisted_hints);
   if (base::FeatureList::IsEnabled(net::features::kPartitionedCookies) &&
+      !base::FeatureList::IsEnabled(
+          net::features::kPartitionedCookiesBypassOriginTrial) &&
       std::find(persisted_hints.begin(), persisted_hints.end(),
                 WebClientHintsType::kPartitionedCookies) ==
           persisted_hints.end()) {
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 0cc5d1f..00ded60 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -1339,7 +1339,8 @@
 
 void OnServiceWorkerMainScriptRequestWillBeSent(
     const GlobalRenderFrameHostId& requesting_frame_id,
-    const base::UnguessableToken& token,
+    const ServiceWorkerContextWrapper* context_wrapper,
+    int64_t version_id,
     const network::ResourceRequest& request) {
   // Currently, `requesting_frame_id` is invalid when payment apps and
   // extensions register a service worker. See the callers of
@@ -1352,17 +1353,25 @@
   if (!requesting_frame)
     return;
 
-  FrameTreeNode* ftn = requesting_frame->frame_tree_node();
-  DCHECK(ftn);
-
   auto timestamp = base::TimeTicks::Now();
   network::mojom::URLRequestDevToolsInfoPtr request_info =
       network::ExtractDevToolsInfo(request);
-  DispatchToAgents(
-      ftn, &protocol::NetworkHandler::RequestSent, token.ToString(),
-      /*loader_id=*/"", request.headers, *request_info,
-      protocol::Network::Initiator::TypeEnum::Other, ftn->current_url(),
-      /*initiator_devtools_request_id=*/"", timestamp);
+
+  ServiceWorkerDevToolsAgentHost* agent_host =
+      ServiceWorkerDevToolsManager::GetInstance()
+          ->GetDevToolsAgentHostForNewInstallingWorker(context_wrapper,
+                                                       version_id);
+  DCHECK(agent_host);
+  DCHECK(request.devtools_request_id.has_value());
+  for (auto* network_handler :
+       protocol::NetworkHandler::ForAgentHost(agent_host)) {
+    network_handler->RequestSent(
+        request.devtools_request_id.value(),
+        /*loader_id=*/"", request.headers, *request_info,
+        protocol::Network::Initiator::TypeEnum::Other,
+        requesting_frame->GetLastCommittedURL(),
+        /*initiator_devtools_request_id=*/"", timestamp);
+  }
 }
 
 void OnWorkerMainScriptLoadingFailed(
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index bc709e1..e9ba449 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -294,7 +294,8 @@
     const std::string& error);
 void OnServiceWorkerMainScriptRequestWillBeSent(
     const GlobalRenderFrameHostId& requesting_frame_id,
-    const base::UnguessableToken& token,
+    const ServiceWorkerContextWrapper* context_wrapper,
+    int64_t version_id,
     const network::ResourceRequest& request);
 
 // Fires `Network.onLoadingFailed` event for a dedicated worker main script.
diff --git a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc
index b56d4ec..eee1fc8 100644
--- a/content/browser/devtools/protocol/devtools_download_manager_delegate.cc
+++ b/content/browser/devtools/protocol/devtools_download_manager_delegate.cc
@@ -79,7 +79,7 @@
         empty_path, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
         download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
         download::DownloadItem::MixedContentStatus::UNKNOWN, empty_path,
-        absl::nullopt /*download_schedule*/,
+        empty_path, absl::nullopt /*download_schedule*/,
         download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
     return true;
   }
@@ -161,7 +161,7 @@
       download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
       download::DownloadItem::MixedContentStatus::UNKNOWN,
       suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")),
-      absl::nullopt /*download_schedule*/,
+      suggested_path.BaseName(), absl::nullopt /*download_schedule*/,
       download::DOWNLOAD_INTERRUPT_REASON_NONE);
 }
 
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index f74eab50..147bb55f 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -499,7 +499,7 @@
         target_path, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
         download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
         download::DownloadItem::MixedContentStatus::UNKNOWN, target_path,
-        absl::nullopt /*download_schedule*/,
+        base::FilePath(), absl::nullopt /*download_schedule*/,
         download::DOWNLOAD_INTERRUPT_REASON_NONE);
   }
 }
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc
index b433e8f..1c137b4 100644
--- a/content/browser/download/download_manager_impl_unittest.cc
+++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -539,6 +539,7 @@
       download::DownloadDangerType danger_type,
       download::DownloadItem::MixedContentStatus mixed_content_status,
       const base::FilePath& intermediate_path,
+      const base::FilePath& display_name,
       absl::optional<download::DownloadSchedule> download_schedule,
       download::DownloadInterruptReason interrupt_reason) {
     callback_called_ = true;
diff --git a/content/browser/media/cdm_registry_impl.cc b/content/browser/media/cdm_registry_impl.cc
index 147f19c0..186caf12 100644
--- a/content/browser/media/cdm_registry_impl.cc
+++ b/content/browser/media/cdm_registry_impl.cc
@@ -218,7 +218,8 @@
 }
 
 void CdmRegistryImpl::RegisterCdm(const CdmInfo& info) {
-  DVLOG(1) << __func__;
+  DVLOG(1) << __func__ << "key_system=" << info.key_system
+           << ", robustness=" << static_cast<int>(info.robustness);
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Always register new CDMs at the end of the list, so that the behavior is
diff --git a/content/browser/media/key_system_support_impl.cc b/content/browser/media/key_system_support_impl.cc
index 6cb7b9a..6b8824c 100644
--- a/content/browser/media/key_system_support_impl.cc
+++ b/content/browser/media/key_system_support_impl.cc
@@ -9,6 +9,24 @@
 
 namespace content {
 
+namespace {
+
+// All key systems must have either software or hardware secure capability
+// supported.
+bool IsValidKeySystemCapabilities(KeySystemCapabilities capabilities) {
+  for (const auto& entry : capabilities) {
+    auto& capability = entry.second;
+    if (!capability.sw_secure_capability.has_value() &&
+        !capability.hw_secure_capability.has_value()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
 // static
 KeySystemSupportImpl* KeySystemSupportImpl::GetInstance() {
   static base::NoDestructor<KeySystemSupportImpl> impl;
@@ -21,14 +39,49 @@
   KeySystemSupportImpl::GetInstance()->Bind(std::move(receiver));
 }
 
-KeySystemSupportImpl::KeySystemSupportImpl(
+KeySystemSupportImpl::KeySystemSupportImpl() = default;
+KeySystemSupportImpl::~KeySystemSupportImpl() = default;
+
+void KeySystemSupportImpl::SetGetKeySystemCapabilitiesUpdateCbForTesting(
     GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing) {
+  get_support_cb_for_testing_ = std::move(get_support_cb_for_testing);
+}
+
+void KeySystemSupportImpl::Bind(
+    mojo::PendingReceiver<media::mojom::KeySystemSupport> receiver) {
+  key_system_support_receivers_.Add(this, std::move(receiver));
+}
+
+void KeySystemSupportImpl::AddObserver(
+    mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer) {
+  DVLOG(3) << __func__;
+
+  auto id = observer_remotes_.Add(std::move(observer));
+
+  // If `key_system_support_` is already available, notify the new observer
+  // immediately. All observers will be notified if there are updates later.
+  if (key_system_capabilities_.has_value()) {
+    auto* observer = observer_remotes_.Get(id);
+    observer->OnKeySystemSupportUpdated(CloneKeySystemCapabilities());
+    return;
+  }
+
+  // Observe key system capabilities if not have done so.
+  if (!is_observing_)
+    ObserveKeySystemCapabilities();
+}
+
+void KeySystemSupportImpl::ObserveKeySystemCapabilities() {
+  DCHECK(!is_observing_);
+
+  is_observing_ = true;
+
   auto result_cb =
       base::BindRepeating(&KeySystemSupportImpl::OnKeySystemCapabilitiesUpdated,
                           weak_ptr_factory_.GetWeakPtr());
 
-  if (get_support_cb_for_testing) {
-    get_support_cb_for_testing.Run(std::move(result_cb));
+  if (get_support_cb_for_testing_) {
+    get_support_cb_for_testing_.Run(std::move(result_cb));
     return;
   }
 
@@ -36,65 +89,29 @@
       std::move(result_cb));
 }
 
-KeySystemSupportImpl::~KeySystemSupportImpl() = default;
-
-void KeySystemSupportImpl::Bind(
-    mojo::PendingReceiver<media::mojom::KeySystemSupport> receiver) {
-  key_system_support_receivers_.Add(this, std::move(receiver));
-}
-
-void KeySystemSupportImpl::IsKeySystemSupported(
-    const std::string& key_system,
-    IsKeySystemSupportedCallback callback) {
-  DVLOG(3) << __func__ << ": key_system=" << key_system;
-
-  if (!key_system_capabilities_.has_value()) {
-    pending_callbacks_.emplace_back(key_system, std::move(callback));
-    return;
-  }
-
-  DCHECK(pending_callbacks_.empty());
-
-  NotifyIsKeySystemSupportedCallback(key_system, std::move(callback));
-}
-
 void KeySystemSupportImpl::OnKeySystemCapabilitiesUpdated(
     KeySystemCapabilities key_system_capabilities) {
   DVLOG(3) << __func__;
+  DCHECK(IsValidKeySystemCapabilities(key_system_capabilities));
+  DCHECK(!key_system_capabilities_.has_value() ||
+         key_system_capabilities_.value() != key_system_capabilities)
+      << "Should not be updated with the same key system capabilities";
+
   key_system_capabilities_ = std::move(key_system_capabilities);
 
-  PendingCallbacks callbacks;
-  pending_callbacks_.swap(callbacks);
-
-  for (auto& entry : callbacks) {
-    auto& key_system = entry.first;
-    auto& callback = entry.second;
-    NotifyIsKeySystemSupportedCallback(key_system, std::move(callback));
-  }
+  for (auto& observer : observer_remotes_)
+    observer->OnKeySystemSupportUpdated(CloneKeySystemCapabilities());
 }
 
-void KeySystemSupportImpl::NotifyIsKeySystemSupportedCallback(
-    const std::string& key_system,
-    IsKeySystemSupportedCallback callback) {
-  DVLOG(3) << __func__ << ": key_system=" << key_system;
+KeySystemCapabilityPtrMap KeySystemSupportImpl::CloneKeySystemCapabilities() {
   DCHECK(key_system_capabilities_.has_value());
 
-  if (!key_system_capabilities_->count(key_system)) {
-    std::move(callback).Run(false, nullptr);
-    return;
+  base::flat_map<std::string, media::mojom::KeySystemCapabilityPtr> result;
+  for (const auto& [key_system, capability] :
+       key_system_capabilities_.value()) {
+    result[key_system] = capability.Clone();
   }
-
-  auto key_system_capabilities =
-      key_system_capabilities_.value()[key_system].Clone();
-  DCHECK(key_system_capabilities);
-
-  if (!key_system_capabilities->sw_secure_capability.has_value() &&
-      !key_system_capabilities->hw_secure_capability.has_value()) {
-    std::move(callback).Run(false, nullptr);
-    return;
-  }
-
-  std::move(callback).Run(true, std::move(key_system_capabilities));
+  return result;
 }
 
 }  // namespace content
diff --git a/content/browser/media/key_system_support_impl.h b/content/browser/media/key_system_support_impl.h
index 8def233..1fc1965 100644
--- a/content/browser/media/key_system_support_impl.h
+++ b/content/browser/media/key_system_support_impl.h
@@ -17,10 +17,16 @@
 #include "media/mojo/mojom/key_system_support.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace content {
 
+// TODO(xhwang): Use StructTraits to convert to KeySystemCapabilities to avoid
+// this type.
+using KeySystemCapabilityPtrMap =
+    base::flat_map<std::string, media::mojom::KeySystemCapabilityPtr>;
+
 // A singleton class living in the browser process handling all KeySystemSupport
 // requests.
 class CONTENT_EXPORT KeySystemSupportImpl final
@@ -34,43 +40,43 @@
   KeySystemSupportImpl(const KeySystemSupportImpl&) = delete;
   KeySystemSupportImpl& operator=(const KeySystemSupportImpl&) = delete;
 
+  // `get_support_cb_for_testing` is used to get support update for testing.
+  // If null, we'll use `CdmRegistryImpl` to get the update.
+  using GetKeySystemCapabilitiesUpdateCB =
+      base::RepeatingCallback<void(KeySystemCapabilitiesUpdateCB)>;
+  void SetGetKeySystemCapabilitiesUpdateCbForTesting(
+      GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing);
+
   // Binds the `receiver` to `this`.
   void Bind(mojo::PendingReceiver<media::mojom::KeySystemSupport> receiver);
 
   // media::mojom::KeySystemSupport implementation.
-  void IsKeySystemSupported(const std::string& key_system,
-                            IsKeySystemSupportedCallback callback) final;
+  void AddObserver(mojo::PendingRemote<media::mojom::KeySystemSupportObserver>
+                       observer) final;
 
  private:
   friend class base::NoDestructor<KeySystemSupportImpl>;
   friend class KeySystemSupportImplTest;
 
-  using GetKeySystemCapabilitiesUpdateCB =
-      base::RepeatingCallback<void(KeySystemCapabilitiesUpdateCB)>;
-
-  // `get_support_cb_for_testing` is used to get support update for testing.
-  // If null, we'll use `CdmRegistryImpl` to get the update.
-  explicit KeySystemSupportImpl(
-      GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing =
-          base::NullCallback());
+  KeySystemSupportImpl();
   ~KeySystemSupportImpl() final;
 
+  void ObserveKeySystemCapabilities();
+
   void OnKeySystemCapabilitiesUpdated(
       KeySystemCapabilities key_system_capabilities);
-  void NotifyIsKeySystemSupportedCallback(
-      const std::string& key_system,
-      IsKeySystemSupportedCallback callback);
 
+  // `KeySystemSupport` uses `media::mojom::KeySystemCapability` while the mojom
+  // interface uses `media::mojom::KeySystemCapabilityPtr`. This function does
+  // the conversion.
+  KeySystemCapabilityPtrMap CloneKeySystemCapabilities();
+
+  GetKeySystemCapabilitiesUpdateCB get_support_cb_for_testing_;
+  bool is_observing_ = false;
+  mojo::ReceiverSet<KeySystemSupport> key_system_support_receivers_;
+  mojo::RemoteSet<media::mojom::KeySystemSupportObserver> observer_remotes_;
   absl::optional<KeySystemCapabilities> key_system_capabilities_;
 
-  mojo::ReceiverSet<media::mojom::KeySystemSupport>
-      key_system_support_receivers_;
-
-  // Key system to IsKeySystemSupportedCallback map.
-  using PendingCallbacks =
-      std::vector<std::pair<std::string, IsKeySystemSupportedCallback>>;
-  PendingCallbacks pending_callbacks_;
-
   base::WeakPtrFactory<KeySystemSupportImpl> weak_ptr_factory_{this};
 };
 
diff --git a/content/browser/media/key_system_support_impl_unittest.cc b/content/browser/media/key_system_support_impl_unittest.cc
index 65bbe10..980e408 100644
--- a/content/browser/media/key_system_support_impl_unittest.cc
+++ b/content/browser/media/key_system_support_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/callback_helpers.h"
 #include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/test/gmock_callback_support.h"
@@ -15,6 +16,7 @@
 #include "media/base/video_codecs.h"
 #include "media/cdm/cdm_capability.h"
 #include "media/cdm/cdm_type.h"
+#include "mojo/public/cpp/bindings/equals_traits.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -30,6 +32,13 @@
 using media::CdmCapability;
 using media::mojom::KeySystemCapability;
 using testing::_;
+using testing::SaveArg;
+
+const char kTestKeySystem[] = "com.example.somesystem";
+
+// Ids to keep track of observers.
+const int kObserver1 = 1;
+const int kObserver2 = 2;
 
 ACTION_TEMPLATE(PostOnceCallback,
                 HAS_1_TEMPLATE_PARAMS(int, k),
@@ -38,114 +47,194 @@
       FROM_HERE, base::BindOnce(std::move(std::get<k>(args)), p0));
 }
 
+namespace {
+
+using KeySystemSupportCB =
+    base::RepeatingCallback<void(KeySystemCapabilityPtrMap)>;
+
+class KeySystemSupportObserverImpl
+    : public media::mojom::KeySystemSupportObserver {
+ public:
+  explicit KeySystemSupportObserverImpl(KeySystemSupportCB cb)
+      : key_system_support_cb_(std::move(cb)) {}
+  KeySystemSupportObserverImpl(const KeySystemSupportObserverImpl&) = delete;
+  KeySystemSupportObserverImpl& operator=(const KeySystemSupportObserverImpl&) =
+      delete;
+  ~KeySystemSupportObserverImpl() override = default;
+
+  // media::mojom::KeySystemSupportObserver
+  void OnKeySystemSupportUpdated(KeySystemCapabilityPtrMap capabilities) final {
+    key_system_support_cb_.Run(std::move(capabilities));
+  }
+
+ private:
+  KeySystemSupportCB key_system_support_cb_;
+};
+
+CdmCapability TestCdmCapability() {
+  return CdmCapability(
+      {AudioCodec::kVorbis}, {{VideoCodec::kVP8, {}}, {VideoCodec::kVP9, {}}},
+      {EncryptionScheme::kCenc, EncryptionScheme::kCbcs},
+      {CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense});
+}
+
+KeySystemCapabilities TestKeySystemCapabilities(
+    absl::optional<CdmCapability> sw_secure_capability,
+    absl::optional<CdmCapability> hw_secure_capability) {
+  KeySystemCapabilities key_system_capabilities;
+  key_system_capabilities[kTestKeySystem] = KeySystemCapability(
+      std::move(sw_secure_capability), std::move(hw_secure_capability));
+  return key_system_capabilities;
+}
+
+}  // namespace
+
 class KeySystemSupportImplTest : public testing::Test {
- protected:
-  CdmCapability TestCdmCapability() {
-    return CdmCapability(
-        {AudioCodec::kVorbis}, {{VideoCodec::kVP8, {}}, {VideoCodec::kVP9, {}}},
-        {EncryptionScheme::kCenc, EncryptionScheme::kCbcs},
-        {CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense});
+ public:
+  KeySystemSupportImplTest() {
+    LOG(ERROR) << __func__;
+    key_system_support_impl_.SetGetKeySystemCapabilitiesUpdateCbForTesting(
+        get_support_cb_.Get());
+    key_system_support_impl_.Bind(
+        key_system_support_.BindNewPipeAndPassReceiver());
   }
 
-  KeySystemCapabilities TestKeySystemCapabilities(
-      absl::optional<CdmCapability> sw_secure_capability,
-      absl::optional<CdmCapability> hw_secure_capability) {
-    KeySystemCapabilities key_system_capabilities;
-    key_system_capabilities["KeySystem"] = KeySystemCapability(
-        std::move(sw_secure_capability), std::move(hw_secure_capability));
-    return key_system_capabilities;
-  }
-
-  void OnIsKeySystemSupported(base::OnceClosure done_cb,
-                              bool is_supported,
-                              media::mojom::KeySystemCapabilityPtr capability) {
-    is_supported_ = is_supported;
-    capability_ = std::move(capability);
+  void OnKeySystemSupportUpdated(int observer_id,
+                                 base::OnceClosure done_cb,
+                                 KeySystemCapabilityPtrMap capabilities) {
+    results_[observer_id].push_back(std::move(capabilities));
     std::move(done_cb).Run();
   }
 
-  // Determines if |key_system| is registered. If it is, updates |codecs_|
-  // and |persistent_|.
-  bool IsSupported(const std::string& key_system) {
+ protected:
+  void GetKeySystemSupport() {
     DVLOG(1) << __func__;
 
-    mojo::Remote<media::mojom::KeySystemSupport> key_system_support;
-    KeySystemSupportImpl key_system_support_impl(get_support_cb_.Get());
-    key_system_support_impl.Bind(
-        key_system_support.BindNewPipeAndPassReceiver());
-
     base::RunLoop run_loop;
-    key_system_support->IsKeySystemSupported(
-        key_system,
-        base::BindOnce(&KeySystemSupportImplTest::OnIsKeySystemSupported,
-                       base::Unretained(this), run_loop.QuitClosure()));
+    mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote;
+    mojo::MakeSelfOwnedReceiver(
+        std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
+            &KeySystemSupportImplTest::OnKeySystemSupportUpdated,
+            base::Unretained(this), kObserver1, run_loop.QuitClosure())),
+        observer_remote.InitWithNewPipeAndPassReceiver());
+    key_system_support_->AddObserver(std::move(observer_remote));
     run_loop.Run();
-
-    return is_supported_;
-  }
-
-  // Same as `IsSupported()`, but calling into KeySystemSupportImpl directly
-  // instead of using the mojo interface. This is to avoid the complication of
-  // posted callbacks in async tests.
-  bool IsSupportedWithoutMojo(const std::string& key_system) {
-    DVLOG(1) << __func__;
-
-    KeySystemSupportImpl key_system_support_impl(get_support_cb_.Get());
-
-    base::RunLoop run_loop;
-    key_system_support_impl.IsKeySystemSupported(
-        key_system,
-        base::BindOnce(&KeySystemSupportImplTest::OnIsKeySystemSupported,
-                       base::Unretained(this), run_loop.QuitClosure()));
-    run_loop.Run();
-
-    return is_supported_;
   }
 
   BrowserTaskEnvironment task_environment_;
-
+  KeySystemSupportImpl key_system_support_impl_;
+  mojo::Remote<media::mojom::KeySystemSupport> key_system_support_;
   base::MockCallback<KeySystemSupportImpl::GetKeySystemCapabilitiesUpdateCB>
       get_support_cb_;
 
-  // Updated by IsSupported().
-  bool is_supported_ = false;
-  media::mojom::KeySystemCapabilityPtr capability_;
+  // KeySystemSupport update results. It's a map from the "observer ID" to the
+  // list of updates received by that observer.
+  std::map<int, std::vector<KeySystemCapabilityPtrMap>> results_;
 };
 
 TEST_F(KeySystemSupportImplTest, NoKeySystems) {
   EXPECT_CALL(get_support_cb_, Run(_))
       .WillOnce(RunOnceCallback<0>(KeySystemCapabilities()));
-  EXPECT_FALSE(IsSupported("KeySystem"));
-  EXPECT_FALSE(capability_);
+  GetKeySystemSupport();
+
+  EXPECT_EQ(results_.size(), 1u);              // One observer
+  EXPECT_TRUE(results_.count(kObserver1));     // Observer 1
+  EXPECT_EQ(results_[kObserver1].size(), 1u);  // One update for observer 1
+
+  const auto& capabilities = results_[kObserver1][0];
+  EXPECT_TRUE(capabilities.empty());  // No capabilities
 }
 
-TEST_F(KeySystemSupportImplTest, NoCapabilities) {
-  EXPECT_CALL(get_support_cb_, Run(_))
-      .WillOnce(RunOnceCallback<0>(
-          TestKeySystemCapabilities(absl::nullopt, absl::nullopt)));
-  EXPECT_FALSE(IsSupported("KeySystem"));
-  EXPECT_FALSE(capability_);
-}
-
-TEST_F(KeySystemSupportImplTest, SoftwareSecureCapability_Sync) {
+TEST_F(KeySystemSupportImplTest, OneObserver) {
   EXPECT_CALL(get_support_cb_, Run(_))
       .WillOnce(RunOnceCallback<0>(
           TestKeySystemCapabilities(TestCdmCapability(), absl::nullopt)));
-  ASSERT_TRUE(IsSupported("KeySystem"));
-  EXPECT_TRUE(capability_->sw_secure_capability);
-  EXPECT_FALSE(capability_->hw_secure_capability);
+  GetKeySystemSupport();
+
+  EXPECT_EQ(results_.size(), 1u);              // One observer
+  EXPECT_TRUE(results_.count(kObserver1));     // Observer 1
+  EXPECT_EQ(results_[kObserver1].size(), 1u);  // One update for observer 1
+
+  auto& capabilities = results_[kObserver1][0];
+  ASSERT_TRUE(capabilities.count(kTestKeySystem));
+  const auto& capability = capabilities[kTestKeySystem];
+  EXPECT_TRUE(capability->sw_secure_capability);
+  EXPECT_FALSE(capability->hw_secure_capability);
 }
 
-// Same as above, but post the callback instead of running it directly, and uses
-// `IsSupportedWithoutMojo`, to simulate the case where `CdmRegistryImpl`
-// resolves the callback asynchronously.
-TEST_F(KeySystemSupportImplTest, SoftwareSecureCapability_Async) {
+TEST_F(KeySystemSupportImplTest, TwoObservers) {
   EXPECT_CALL(get_support_cb_, Run(_))
-      .WillOnce(PostOnceCallback<0>(
+      .WillOnce(RunOnceCallback<0>(
           TestKeySystemCapabilities(TestCdmCapability(), absl::nullopt)));
-  ASSERT_TRUE(IsSupportedWithoutMojo("KeySystem"));
-  EXPECT_TRUE(capability_->sw_secure_capability);
-  EXPECT_FALSE(capability_->hw_secure_capability);
+
+  base::RunLoop run_loop;
+  mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_1_remote;
+  mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_2_remote;
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
+          &KeySystemSupportImplTest::OnKeySystemSupportUpdated,
+          base::Unretained(this), kObserver1, base::DoNothing())),
+      observer_1_remote.InitWithNewPipeAndPassReceiver());
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
+          &KeySystemSupportImplTest::OnKeySystemSupportUpdated,
+          base::Unretained(this), kObserver2, run_loop.QuitClosure())),
+      observer_2_remote.InitWithNewPipeAndPassReceiver());
+  key_system_support_->AddObserver(std::move(observer_1_remote));
+  key_system_support_->AddObserver(std::move(observer_2_remote));
+  run_loop.Run();
+
+  EXPECT_EQ(results_.size(), 2u);  // Two observers
+
+  EXPECT_TRUE(results_.count(kObserver1));     // Observer 1
+  EXPECT_EQ(results_[kObserver1].size(), 1u);  // One update for observer 1
+  auto& capabilities = results_[kObserver1][0];
+  ASSERT_TRUE(capabilities.count(kTestKeySystem));
+  const auto& capability = capabilities[kTestKeySystem];
+  EXPECT_TRUE(capability->sw_secure_capability);
+  EXPECT_FALSE(capability->hw_secure_capability);
+
+  EXPECT_TRUE(results_.count(kObserver2));     // Observer 2
+  EXPECT_EQ(results_[kObserver2].size(), 1u);  // One update for observer 1
+  EXPECT_TRUE(mojo::Equals(results_[kObserver1][0], results_[kObserver2][0]));
+}
+
+TEST_F(KeySystemSupportImplTest, TwoUpdates) {
+  KeySystemCapabilitiesUpdateCB callback;
+  EXPECT_CALL(get_support_cb_, Run(_)).WillOnce(SaveArg<0>(&callback));
+
+  base::RunLoop run_loop_1;
+  mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote;
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<KeySystemSupportObserverImpl>(base::BindRepeating(
+          &KeySystemSupportImplTest::OnKeySystemSupportUpdated,
+          base::Unretained(this), kObserver1, base::DoNothing())),
+      observer_remote.InitWithNewPipeAndPassReceiver());
+  key_system_support_->AddObserver(std::move(observer_remote));
+  run_loop_1.RunUntilIdle();
+
+  // Update twice, one with hardware capability, one without.
+  base::RunLoop run_loop_2;
+  callback.Run(TestKeySystemCapabilities(TestCdmCapability(), absl::nullopt));
+  callback.Run(
+      TestKeySystemCapabilities(TestCdmCapability(), TestCdmCapability()));
+  run_loop_2.RunUntilIdle();
+
+  EXPECT_EQ(results_.size(), 1u);              // One observer
+  EXPECT_TRUE(results_.count(kObserver1));     // Observer 1
+  EXPECT_EQ(results_[kObserver1].size(), 2u);  // Two updates for observer 1
+
+  auto& capabilities_1 = results_[kObserver1][0];
+  ASSERT_TRUE(capabilities_1.count(kTestKeySystem));
+  const auto& capability_1 = capabilities_1[kTestKeySystem];
+  EXPECT_TRUE(capability_1->sw_secure_capability);
+  EXPECT_FALSE(capability_1->hw_secure_capability);
+
+  auto& capabilities_2 = results_[kObserver1][1];
+  ASSERT_TRUE(capabilities_2.count(kTestKeySystem));
+  const auto& capability_2 = capabilities_2[kTestKeySystem];
+  EXPECT_TRUE(capability_2->sw_secure_capability);
+  EXPECT_TRUE(capability_2->hw_secure_capability);
 }
 
 }  // namespace content
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index 94be77f4..9b74786 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -177,6 +177,7 @@
 
 base::Thread& GetNetworkServiceDedicatedThread() {
   static base::NoDestructor<base::Thread> thread{"NetworkService"};
+  DCHECK(base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread));
   return *thread;
 }
 
@@ -546,11 +547,7 @@
 void CreateInProcessNetworkService(
     mojo::PendingReceiver<network::mojom::NetworkService> receiver) {
   scoped_refptr<base::SingleThreadTaskRunner> task_runner;
-  // If it's specified to run a separate thread for the in-process network
-  // service, or if the IO thread isn't initialized because we're in Android's
-  // minimal browser mode, then use a dedicated thread.
-  if (base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread) ||
-      !BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
+  if (base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread)) {
     base::Thread::Options options(base::MessagePumpType::IO, 0);
     GetNetworkServiceDedicatedThread().StartWithOptions(std::move(options));
     task_runner = GetNetworkServiceDedicatedThread().task_runner();
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h
index 1ba97bd..b102dd0 100644
--- a/content/browser/renderer_host/frame_tree_node.h
+++ b/content/browser/renderer_host/frame_tree_node.h
@@ -332,7 +332,7 @@
   // A RenderFrameHost in this node started loading.
   // |should_show_loading_ui| indicates whether this navigation should be
   // visible in the UI. True for cross-document navigations and navigations
-  // intercepted by appHistory's transitionWhile().
+  // intercepted by the navigation API's transitionWhile().
   // |was_previously_loading| is false if the FrameTree was not loading before.
   // The caller is required to provide this boolean as the delegate should only
   // be notified if the FrameTree went from non-loading to loading state.
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 464d92cb..a9d83b4 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -3738,7 +3738,7 @@
           absl::nullopt /* ad_auction_components */,
           // This timestamp will be populated when the commit IPC is sent.
           base::TimeTicks() /* commit_sent */, false /* anonymous */,
-          std::string() /* srcdoc_value */);
+          std::string() /* srcdoc_value */, false /* should_load_data_url */);
 #if BUILDFLAG(IS_ANDROID)
   if (ValidateDataURLAsString(params.data_url_as_string)) {
     commit_params->data_url_as_string = params.data_url_as_string->data();
diff --git a/content/browser/renderer_host/navigation_entry_impl.cc b/content/browser/renderer_host/navigation_entry_impl.cc
index 2987e91..35319ea 100644
--- a/content/browser/renderer_host/navigation_entry_impl.cc
+++ b/content/browser/renderer_host/navigation_entry_impl.cc
@@ -929,7 +929,7 @@
           absl::nullopt /* ad_auction_components */,
           // This timestamp will be populated when the commit IPC is sent.
           base::TimeTicks() /* commit_sent */, false /* anonymous */,
-          std::string() /* srcdoc_value */);
+          std::string() /* srcdoc_value */, false /* should_load_data_url */);
 #if BUILDFLAG(IS_ANDROID)
   // `data_url_as_string` is saved in NavigationEntry but should only be used by
   // main frames, because loadData* navigations can only happen on the main
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 45af0f5..15a834a 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -961,7 +961,9 @@
     PersistAcceptCH(url::Origin::Create(url), delegate, client_hints);
   }
 
-  if (base::FeatureList::IsEnabled(net::features::kPartitionedCookies)) {
+  if (base::FeatureList::IsEnabled(net::features::kPartitionedCookies) &&
+      !base::FeatureList::IsEnabled(
+          net::features::kPartitionedCookiesBypassOriginTrial)) {
     if (auto* cookie_manager = frame_tree_node->current_frame_host()
                                    ->GetStoragePartition()
                                    ->GetCookieManagerForBrowserProcess()) {
@@ -1171,7 +1173,7 @@
           std::vector<GURL>(), absl::nullopt /* ad_auction_components */,
           // This timestamp will be populated when the commit IPC is sent.
           base::TimeTicks() /* commit_sent */, false /* anonymous */,
-          std::string() /* srcdoc_value */);
+          std::string() /* srcdoc_value */, false /* should_load_data_url */);
 
   // CreateRendererInitiated() should only be triggered when the navigation is
   // initiated by a frame in the same process.
@@ -1295,7 +1297,7 @@
           absl::nullopt /* ad_auction_components */,
           // This timestamp will be populated when the commit IPC is sent.
           base::TimeTicks() /* commit_sent */, false /* anonymous */,
-          std::string() /* srcdoc_value */);
+          std::string() /* srcdoc_value */, false /* should_load_data_url */);
   blink::mojom::BeginNavigationParamsPtr begin_params =
       blink::mojom::BeginNavigationParams::New();
   std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest(
@@ -5990,6 +5992,8 @@
 
   SetExpectedProcess(render_frame_host_->GetProcess());
 
+  commit_params_->is_load_data_with_base_url = IsLoadDataWithBaseURL();
+
   if (!IsSameDocument()) {
 #if DCHECK_IS_ON()
     DCHECK(is_safe_to_delete_);
@@ -6027,11 +6031,9 @@
 }
 
 bool NavigationRequest::IsLoadDataWithBaseURL() const {
-  // A navigation is a loadDataWithBaseURL navigation if it's a successful main
-  // frame navigation, and its base URL is valid. This function should be kept
-  // in sync with the ShouldLoadDataWithBaseURL() function in
-  // render_frame_impl.cc.
-  return IsInMainFrame() && !DidEncounterError() &&
+  // A navigation is a loadDataWithBaseURL navigation if it's a successful
+  // primary main frame navigation to a data: URL, and its base URL is valid.
+  return IsInPrimaryMainFrame() && !DidEncounterError() &&
          common_params_->url.SchemeIs(url::kDataScheme) &&
          common_params_->base_url_for_data_url.is_valid();
 }
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index b35619c..bf7c547c 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -10867,13 +10867,14 @@
     // In general, loading ui is only shown for cross-document navigations,
     // because same-document navigations are already complete by the time the
     // renderer notifies the browser process of the navigation.
-    // AppHistory transitionWhile(), however, is an asynchronous same-document
-    // navigation, and should therefore show loading UI until load completion.
+    // The navigation API's transitionWhile(), however, is an asynchronous
+    // same-document navigation, and should therefore show loading UI until load
+    // completion.
     bool should_show_loading_ui =
         !is_same_document_navigation ||
         same_document_params->same_document_navigation_type ==
             blink::mojom::SameDocumentNavigationType::
-                kAppHistoryTransitionWhile;
+                kNavigationApiTransitionWhile;
 
     bool was_loading = frame_tree()->LoadingTree()->IsLoading();
     is_loading_ = true;
diff --git a/content/browser/renderer_host/render_frame_host_impl_unittest.cc b/content/browser/renderer_host/render_frame_host_impl_unittest.cc
index b51de60..e52a0f7 100644
--- a/content/browser/renderer_host/render_frame_host_impl_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_unittest.cc
@@ -510,7 +510,7 @@
   ASSERT_FALSE(contents()->IsLoading());
   ASSERT_FALSE(contents()->ShouldShowLoadingUI());
 
-  // Emulate appHistory.transitionWhile().
+  // Emulate navigateEvent.transitionWhile().
   const GURL url2("http://foo#a");
   auto params = mojom::DidCommitProvisionalLoadParams::New();
   params->did_create_new_entry = false;
@@ -525,10 +525,10 @@
   params->post_id = -1;
   main_test_rfh()->SendDidCommitSameDocumentNavigation(
       std::move(params),
-      blink::mojom::SameDocumentNavigationType::kAppHistoryTransitionWhile);
+      blink::mojom::SameDocumentNavigationType::kNavigationApiTransitionWhile);
 
-  // appHistory.transitionWhile() should leave WebContents in the loading state
-  // and showing loading UI, unlike other same-document navigations.
+  // navigateEvent.transitionWhile() should leave WebContents in the loading
+  // state and showing loading UI, unlike other same-document navigations.
   EXPECT_TRUE(delegate->should_show_loading_ui());
   EXPECT_TRUE(contents()->IsLoading());
   EXPECT_TRUE(contents()->ShouldShowLoadingUI());
diff --git a/content/browser/resources/service_worker/serviceworker_internals.css b/content/browser/resources/service_worker/serviceworker_internals.css
index 373f576..20a8e804 100644
--- a/content/browser/resources/service_worker/serviceworker_internals.css
+++ b/content/browser/resources/service_worker/serviceworker_internals.css
@@ -3,44 +3,54 @@
  * found in the LICENSE file. */
 
 .serviceworker-summary {
-    background-color: rgb(235, 239, 249);
-    border-top: 1px solid rgb(156, 194, 239);
-    margin-bottom: 6px;
-    margin-top: 12px;
-    padding: 3px;
-    font-weight: bold;
+  background-color: rgb(235, 239, 249);
+  border-top: 1px solid rgb(156, 194, 239);
+  font-weight: bold;
+  margin-bottom: 6px;
+  margin-top: 12px;
+  padding: 3px;
 }
 
 .serviceworker-item {
-    margin-bottom: 15px;
-    margin-top: 6px;
-    position: relative;
+  margin-bottom: 15px;
+  margin-top: 6px;
+  position: relative;
 }
 
 .serviceworker-registration {
-    padding: 5px;
+  padding: 5px;
 }
 
 .serviceworker-scope {
-    color: rgb(85, 102, 221);
-    display: inline-block;
-    padding-bottom: 1px;
-    padding-top: 4px;
-    text-decoration: none;
-    white-space: nowrap;
+  color: rgb(85, 102, 221);
+  display: inline-block;
+  padding-bottom: 1px;
+  padding-top: 4px;
+  text-decoration: none;
+  white-space: nowrap;
 }
 
 .serviceworker-version {
-    padding-bottom: 3px;
-    padding-left: 10px;
+  padding-bottom: 3px;
+  padding-inline-start: 10px;
 }
 
 .serviceworker-client {
-    padding-bottom: 3px;
-    padding-left: 10px;
+  padding-bottom: 3px;
+  padding-inline-start: 10px;
 }
 
 .controls a {
-    margin-block-end: 16px;
-    color: #777;
+  color: #777;
+  margin-block-end: 16px;
+}
+
+.serviceworker-storage-key {
+  padding-bottom: 3px;
+  padding-inline-start: 10px;
+}
+
+.serviceworker-storage-key-wrapper {
+  color: rgb(85, 102, 221);
+  white-space: nowrap;
 }
diff --git a/content/browser/resources/service_worker/serviceworker_internals.html b/content/browser/resources/service_worker/serviceworker_internals.html
index f486ea9..5829829 100644
--- a/content/browser/resources/service_worker/serviceworker_internals.html
+++ b/content/browser/resources/service_worker/serviceworker_internals.html
@@ -69,6 +69,33 @@
         <span>Scope:</span>
         <span jscontent="scope"></span>
       </div>
+      <!-- Storage Partitioning -->
+      <div class="serviceworker-storage-key-wrapper"
+        jsdisplay="$this.third_party_storage_partitioning_enabled">
+        Storage key:
+        <div class="serviceworker-storage-key">
+          <div class="serviceworker-origin"
+            jsdisplay="$this.ancestor_chain_bit == 'CrossSite'">
+            <span>origin:</span>
+            <span jscontent="$this.origin"></span>
+          </div>
+          <div class="serviceworker-top-level-site"
+            jsdisplay="$this.ancestor_chain_bit == 'CrossSite'">
+            <span>Top level site:</span>
+            <span jscontent="$this.top_level_site"></span>
+          </div>
+          <div class="serviceworker-ancestor-chain-bit">
+            <span>Ancestor chain bit:</span>
+            <span jscontent="$this.ancestor_chain_bit"></span>
+          </div>
+          <div class="serviceworker-nonce"
+            jsdisplay="$this.ancestor_chain_bit == 'CrossSite'">
+            <span>nonce:</span>
+            <span jscontent="$this.active"></span>
+          </div>
+        </div>
+      </div>
+      <!-- Storage Partitioning ends -->
       <div class="serviceworker-rid">
         <span>Registration ID:</span>
         <span jscontent="registration_id"></span>
@@ -147,7 +174,8 @@
         <span>URL: </span><span jscontent="$this.url"></span>
       </div>
     </div>
-  </div> <!-- templates end -->
+  </div>
+  <!-- templates end -->
   <h1>ServiceWorker</h1>
   <div class="content">
     <div id="serviceworker-options"></div>
diff --git a/content/browser/sandbox_mac_unittest.mm b/content/browser/sandbox_mac_unittest.mm
index a69810d9..05e9b29 100644
--- a/content/browser/sandbox_mac_unittest.mm
+++ b/content/browser/sandbox_mac_unittest.mm
@@ -15,7 +15,9 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #include "base/mac/scoped_cftyperef.h"
+#include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory_mapping.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/process/kill.h"
 #include "base/strings/strcat.h"
@@ -204,24 +206,19 @@
   size_t font_data_length = font_data.length();
   CHECK(font_data_length > 0);
 
-  auto font_shmem = mojo::SharedBufferHandle::Create(font_data_length);
-  CHECK(font_shmem.is_valid());
+  auto shmem_region_and_mapping =
+      base::ReadOnlySharedMemoryRegion::Create(font_data_length);
+  CHECK(shmem_region_and_mapping.IsValid());
 
-  mojo::ScopedSharedBufferMapping mapping = font_shmem->Map(font_data_length);
-  CHECK(mapping);
-
-  memcpy(mapping.get(), font_data.c_str(), font_data_length);
+  memcpy(shmem_region_and_mapping.mapping.memory(), font_data.c_str(),
+         font_data_length);
 
   // Now init the sandbox.
   CheckCreateSeatbeltServer();
 
-  mojo::ScopedSharedBufferHandle shmem_handle =
-      font_shmem->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY);
-  CHECK(shmem_handle.is_valid());
-
   base::ScopedCFTypeRef<CTFontDescriptorRef> data_descriptor;
   CHECK(FontLoader::CTFontDescriptorFromBuffer(
-      std::move(shmem_handle), font_data_length, &data_descriptor));
+      std::move(shmem_region_and_mapping.region), &data_descriptor));
   CHECK(data_descriptor);
 
   base::ScopedCFTypeRef<CTFontRef> sized_ctfont(
@@ -244,18 +241,18 @@
   std::unique_ptr<FontLoader::ResultInternal> result =
       FontLoader::LoadFontForTesting(u"Geeza Pro", 16);
   ASSERT_TRUE(result);
-  ASSERT_TRUE(result->font_data.is_valid());
-  uint64_t font_data_size = result->font_data->GetSize();
+  ASSERT_TRUE(result->font_data.IsValid());
+  uint64_t font_data_size = result->font_data.GetSize();
   EXPECT_GT(font_data_size, 0U);
   EXPECT_GT(result->font_id, 0U);
 
-  mojo::ScopedSharedBufferMapping mapping =
-      result->font_data->Map(font_data_size);
-  ASSERT_TRUE(mapping);
+  base::ReadOnlySharedMemoryMapping mapping = result->font_data.Map();
+  ASSERT_TRUE(mapping.IsValid());
+  ASSERT_EQ(font_data_size, mapping.size());
 
   base::WriteFileDescriptor(
       fileno(temp_file.get()),
-      base::StringPiece(static_cast<const char*>(mapping.get()),
+      base::StringPiece(static_cast<const char*>(mapping.memory()),
                         font_data_size));
 
   extra_data_ = temp_file_path.value();
diff --git a/content/browser/scheduler/responsiveness/README b/content/browser/scheduler/responsiveness/README
index c134599..4260fcc 100644
--- a/content/browser/scheduler/responsiveness/README
+++ b/content/browser/scheduler/responsiveness/README
@@ -4,7 +4,7 @@
 
 There are four types of work items executed on the UI and IO threads.
 1) Both the UI and IO threads can have tasks posted to them via the Task
-   Scheduler [e.g. via base::PostTask with a BrowserThread::ID].
+   Scheduler [e.g. via content::Get(UI|IO)ThreadTaskRunner()].
 2) The UI thread processes native events directly from the message loop
    [NSEvents on macOS, MSGs on Windows, InputEvents on Android, XEvents on
    X11, etc.]
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index d5615a6..89e6875 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -162,6 +162,22 @@
   for (const auto& registration : registrations) {
     base::Value registration_info(base::Value::Type::DICTIONARY);
     registration_info.SetStringKey("scope", registration.scope.spec());
+    registration_info.SetBoolKey(
+        "third_party_storage_partitioning_enabled",
+        registration.key.IsThirdPartyStoragePartitioningEnabled());
+    registration_info.SetStringKey(
+        "ancestor_chain_bit", registration.key.ancestor_chain_bit() ==
+                                      blink::mojom::AncestorChainBit::kCrossSite
+                                  ? "CrossSite"
+                                  : "SameSite");
+    registration_info.SetStringKey("nonce",
+                                   registration.key.nonce().has_value()
+                                       ? registration.key.nonce()->ToString()
+                                       : "<null>");
+    registration_info.SetStringKey("origin",
+                                   registration.key.origin().GetDebugString());
+    registration_info.SetStringKey(
+        "top_level_site", registration.key.top_level_site().Serialize());
     registration_info.SetStringKey(
         "registration_id", base::NumberToString(registration.registration_id));
     registration_info.SetBoolKey("navigation_preload_enabled",
diff --git a/content/browser/service_worker/service_worker_internals_ui_browsertest.cc b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
new file mode 100644
index 0000000..74dd04c
--- /dev/null
+++ b/content/browser/service_worker/service_worker_internals_ui_browsertest.cc
@@ -0,0 +1,358 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
+
+namespace content {
+namespace {
+const char kServiceWorkerInternalsUrl[] = "chrome://serviceworker-internals";
+const char kServiceWorkerSetupPage[] = "/service_worker/empty.html";
+const char kServiceWorkerUrl[] = "/service_worker/fetch_event.js";
+const char kServiceWorkerScope[] = "/service_worker/";
+const std::u16string kCompleteTitle = u"Complete";
+
+void ExpectRegisterResultAndRun(blink::ServiceWorkerStatusCode expected,
+                                base::RepeatingClosure continuation,
+                                blink::ServiceWorkerStatusCode actual) {
+  ASSERT_EQ(expected, actual);
+  continuation.Run();
+}
+
+void ExpectUnregisterResultAndRun(bool expected,
+                                  base::RepeatingClosure continuation,
+                                  bool actual) {
+  ASSERT_EQ(expected, actual);
+  continuation.Run();
+}
+}  // namespace
+
+static int CountRenderProcessHosts() {
+  return RenderProcessHost::GetCurrentRenderProcessCountForTesting();
+}
+class ServiceWorkerInternalsUIBrowserTest : public ContentBrowserTest {
+ public:
+  ServiceWorkerInternalsUIBrowserTest() = default;
+
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+    ContentBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    StartServer();
+    StoragePartition* partition = shell()
+                                      ->web_contents()
+                                      ->GetBrowserContext()
+                                      ->GetDefaultStoragePartition();
+    wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
+        partition->GetServiceWorkerContext());
+  }
+
+  void TearDownOnMainThread() override {
+    // Flush remote storage control so that all pending callbacks are executed.
+    wrapper()
+        ->context()
+        ->registry()
+        ->GetRemoteStorageControl()
+        .FlushForTesting();
+    content::RunAllTasksUntilIdle();
+    wrapper_ = nullptr;
+  }
+
+  void StartServer() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    embedded_test_server()->StartAcceptingConnections();
+  }
+
+  ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
+  ServiceWorkerContext* public_context() { return wrapper(); }
+
+  blink::ServiceWorkerStatusCode FindRegistration() {
+    const GURL& document_url =
+        embedded_test_server()->GetURL(kServiceWorkerSetupPage);
+    blink::ServiceWorkerStatusCode status;
+    base::RunLoop loop;
+    wrapper()->FindReadyRegistrationForClientUrl(
+        document_url, blink::StorageKey(url::Origin::Create(document_url)),
+        base::BindLambdaForTesting(
+            [&](blink::ServiceWorkerStatusCode find_status,
+                scoped_refptr<ServiceWorkerRegistration> registration) {
+              status = find_status;
+              if (!registration.get())
+                EXPECT_NE(blink::ServiceWorkerStatusCode::kOk, status);
+              loop.Quit();
+            }));
+    loop.Run();
+    return status;
+  }
+
+  std::vector<ServiceWorkerRegistrationInfo> GetAllRegistrations() {
+    return wrapper()->GetAllLiveRegistrationInfo();
+  }
+
+  // Navigate to the page to set up a renderer page to embed a worker
+  void NavigateToServiceWorkerSetupPage() {
+    NavigateToURLBlockUntilNavigationsComplete(
+        active_shell_, embedded_test_server()->GetURL(kServiceWorkerSetupPage),
+        1);
+    FocusContent(FROM_HERE);
+  }
+
+  void NavigateToServiceWorkerInternalUI() {
+    ASSERT_TRUE(NavigateToURL(active_shell_, GURL(kServiceWorkerInternalsUrl)));
+    // Ensure the window has focus after the navigation.
+    FocusContent(FROM_HERE);
+  }
+
+  void FocusContent(const base::Location& from_here) {
+    RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
+        web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost());
+    host->GotFocus();
+    host->SetActive(true);
+
+    ASSERT_TRUE(web_contents()->GetRenderWidgetHostView()->HasFocus())
+        << "Location: " << from_here.ToString();
+  }
+
+  WebContentsImpl* web_contents() const {
+    return static_cast<WebContentsImpl*>(active_shell_->web_contents());
+  }
+
+  // Create a new window and navigate to about::blank.
+  Shell* CreateNewWindow() {
+    active_shell_ = CreateBrowser();
+    return active_shell_;
+  }
+
+  // Tear down the page.
+  void TearDownWindow() {
+    active_shell_->Close();
+    active_shell_ = shell();
+  }
+
+  void MoveToWindow(Shell* window) { active_shell_ = window; }
+  void ReloadWindow() { active_shell_->Reload(); }
+
+  // Registers a service worker and then tears down the process it used, for a
+  // clean slate going forward.
+  void RegisterServiceWorker() {
+    NavigateToServiceWorkerSetupPage();
+    {
+      base::RunLoop run_loop;
+      blink::mojom::ServiceWorkerRegistrationOptions options(
+          embedded_test_server()->GetURL(kServiceWorkerScope),
+          blink::mojom::ScriptType::kClassic,
+          blink::mojom::ServiceWorkerUpdateViaCache::kImports);
+      // Set up the storage key for the service worker
+      blink::StorageKey key(url::Origin::Create(options.scope));
+      // Register returns when the promise is resolved.
+      public_context()->RegisterServiceWorker(
+          embedded_test_server()->GetURL(kServiceWorkerUrl), key, options,
+          base::BindOnce(&ExpectRegisterResultAndRun,
+                         blink::ServiceWorkerStatusCode::kOk,
+                         run_loop.QuitClosure()));
+      run_loop.Run();
+    }
+  }
+
+  void UnRegisterServiceWorker() {
+    {
+      base::RunLoop run_loop;
+      blink::StorageKey key(url::Origin::Create(
+          embedded_test_server()->GetURL(kServiceWorkerScope)));
+      // Unregistering something should return true.
+      public_context()->UnregisterServiceWorker(
+          embedded_test_server()->GetURL(kServiceWorkerScope), key,
+          base::BindOnce(&ExpectUnregisterResultAndRun, true,
+                         run_loop.QuitClosure()));
+      run_loop.Run();
+    }
+    EXPECT_EQ(FindRegistration(),
+              blink::ServiceWorkerStatusCode::kErrorNotFound)
+        << "Should not be able to find any Service Worker.";
+  }
+
+  testing::AssertionResult setMutationObserverSWPopulated() {
+    static constexpr char kScript[] = R"(
+     const elementToObserve = document.getElementById("serviceworker-list");
+     const options = { childList: true, subtree: true };
+     let placeholder = document.createElement("browserTestResult_SWPopulated");
+     document.body.appendChild(placeholder);
+
+     const callback = function (mutations, observer) {
+       mutations.forEach((mutation) => {
+         if (mutation.type === "childList") {
+           mutation.addedNodes.forEach((node) => {
+             if (
+               node.classList &&
+               node.classList.contains("serviceworker-registration")
+             ) {
+               placeholder.innerText = "done";
+               document.title = $1;
+               observer.disconnect();
+             }
+           });
+         }
+       });
+     };
+
+     const observer = new MutationObserver(callback);
+     observer.observe(elementToObserve, options);
+   )";
+    return ExecJs(web_contents()->GetMainFrame(),
+                  JsReplace(kScript, kCompleteTitle),
+                  EXECUTE_SCRIPT_DEFAULT_OPTIONS, /*world_id=*/1);
+  }
+
+  std::string cleanMutationObserver(std::string mutation_observer) {
+    static constexpr char kScript[] = R"(
+     const task = $1;
+     const result = document.querySelector("browserTestResult_"+task).innerText;
+     document.querySelector("browserTestResult_"+task).remove();
+     document.title = null;
+     result;
+   )";
+    return EvalJs(web_contents()->GetMainFrame(),
+                  JsReplace(kScript, mutation_observer),
+                  EXECUTE_SCRIPT_DEFAULT_OPTIONS,
+                  /*world_id=*/1)
+        .ExtractString();
+  }
+
+  std::string getServiceWorkerInfoFromInternalUI(
+      int64_t registration_id,
+      std::string service_worker_info) {
+    static constexpr char kScript[] = R"(
+     var serviceworkers = document.querySelectorAll(
+       "div#serviceworker-list > div:not([style='display: none;'])\
+             > div:not([class='serviceworker-summary']) > \
+             div.serviceworker-registration"
+     );
+
+     let result = "";
+     serviceworkers.forEach((serviceworker) => {
+       let target;
+       Array.prototype.forEach.call(
+         serviceworker.querySelectorAll("*"),
+         (node) =>
+       {
+           if (
+             node.attributes.jscontent &&
+             RegExp("registration_id").test(node.attributes.jscontent.value) &&
+             node.innerText === $1
+           ) {
+             target = serviceworker;
+           }
+       });
+       if (target) {
+         Array.prototype.forEach.call(target.querySelectorAll("*"), (node) => {
+           if (
+             node.attributes.jscontent &&
+             RegExp($2+$3+$4).test(node.attributes.jscontent.value)
+           ) {
+             result = node.innerText;
+           }
+         });
+       }
+     });
+     result;
+   )";
+    return EvalJs(web_contents()->GetMainFrame(),
+                  JsReplace(kScript, base::NumberToString(registration_id),
+                            "^(.this\\.)?(", service_worker_info, ")$"),
+                  EXECUTE_SCRIPT_DEFAULT_OPTIONS, /*world_id=*/1)
+        .ExtractString();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
+  Shell* active_shell_ = shell();
+};
+
+// Tests
+IN_PROC_BROWSER_TEST_F(ServiceWorkerInternalsUIBrowserTest,
+                       NoRegisteredServiceWorker) {
+  EXPECT_TRUE(CreateNewWindow());
+  EXPECT_EQ(1, CountRenderProcessHosts());
+
+  NavigateToServiceWorkerInternalUI();
+
+  EXPECT_EQ(0, EvalJs(web_contents()->GetMainFrame(),
+                      R"(document.querySelectorAll(
+                 "div#serviceworker-list > div:not([style='display: none;'])\
+                 > div:not([class='serviceworker-summary'])\
+                 > div.serviceworker-registration"
+               ).length)",
+                      EXECUTE_SCRIPT_DEFAULT_OPTIONS, /*world_id=*/1)
+                   .ExtractInt());
+
+  TearDownWindow();
+  EXPECT_EQ(0, CountRenderProcessHosts());
+}
+
+IN_PROC_BROWSER_TEST_F(ServiceWorkerInternalsUIBrowserTest,
+                       RegisteredSWReflectedOnInternalUI) {
+  Shell* SWInternalUIWindow = CreateNewWindow();
+  NavigateToServiceWorkerInternalUI();
+  EXPECT_EQ(1, CountRenderProcessHosts());
+
+  setMutationObserverSWPopulated();
+
+  Shell* SWREgistrationWindow = CreateNewWindow();
+  RegisterServiceWorker();
+  EXPECT_EQ(2, CountRenderProcessHosts());
+
+  MoveToWindow(SWInternalUIWindow);
+
+  TitleWatcher title_watcher(web_contents(), kCompleteTitle);
+  EXPECT_EQ(kCompleteTitle, title_watcher.WaitAndGetTitle());
+  EXPECT_EQ("done", cleanMutationObserver("SWPopulated"));
+
+  std::vector<ServiceWorkerRegistrationInfo> registrations =
+      GetAllRegistrations();
+  EXPECT_EQ(1u, registrations.size())
+      << "There should be exactly one registration";
+  EXPECT_EQ(registrations[0].scope.spec(),
+            getServiceWorkerInfoFromInternalUI(registrations[0].registration_id,
+                                               "scope"));
+  EXPECT_EQ("ACTIVATED", getServiceWorkerInfoFromInternalUI(
+                             registrations[0].registration_id, "status"));
+  UnRegisterServiceWorker();
+  EXPECT_GE(2, CountRenderProcessHosts()) << "Unregistering doesn't stop the"
+                                             "workers eagerly, so their RPHs"
+                                             "can still be running.";
+  MoveToWindow(SWREgistrationWindow);
+  TearDownWindow();
+
+  MoveToWindow(SWInternalUIWindow);
+  TearDownWindow();
+  EXPECT_GE(1, CountRenderProcessHosts());
+}
+}  // namespace content
diff --git a/content/browser/service_worker/service_worker_new_script_fetcher.cc b/content/browser/service_worker/service_worker_new_script_fetcher.cc
index d0c6181..373cceb 100644
--- a/content/browser/service_worker/service_worker_new_script_fetcher.cc
+++ b/content/browser/service_worker/service_worker_new_script_fetcher.cc
@@ -108,7 +108,8 @@
   // is about to start. It fires `Network.onRequestWillBeSent` event.
   request.devtools_request_id = version_->reporting_source().ToString();
   devtools_instrumentation::OnServiceWorkerMainScriptRequestWillBeSent(
-      requesting_frame_id_, version_->reporting_source(), request);
+      requesting_frame_id_, context_.wrapper(), version_->version_id(),
+      request);
 
   mojo::MakeSelfOwnedReceiver(
       ServiceWorkerNewScriptLoader::CreateAndStart(
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index befddcf..2229bff8 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -100,6 +100,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/content_mock_cert_verifier.h"
+#include "content/public/test/fenced_frame_test_util.h"
 #include "content/public/test/hit_test_region_observer.h"
 #include "content/public/test/navigation_handle_observer.h"
 #include "content/public/test/policy_container_utils.h"
@@ -3768,6 +3769,98 @@
   EXPECT_EQ(named_frame_url, foo_root->current_url());
 }
 
+class SitePerProcessFencedFrameTest
+    : public SitePerProcessBrowserTestBase,
+      public testing::WithParamInterface<
+          blink::features::FencedFramesImplementationType> {
+ public:
+  SitePerProcessFencedFrameTest() {
+    if (GetParam() ==
+        blink::features::FencedFramesImplementationType::kMPArch) {
+      fenced_frame_helper_ =
+          std::make_unique<content::test::FencedFrameTestHelper>();
+    } else {
+      feature_list_.InitAndEnableFeatureWithParameters(
+          blink::features::kFencedFrames,
+          {{"implementation_type", "shadow_dom"}});
+    }
+  }
+
+ protected:
+  content::RenderFrameHost* CreateFencedFrame(content::RenderFrameHost* parent,
+                                              const GURL& url) {
+    if (fenced_frame_helper_) {
+      return fenced_frame_helper_->CreateFencedFrame(parent, url);
+    }
+
+    // FencedFrameTestHelper only supports the MPArch version of fenced frames.
+    // So need to maually create a fenced frame for the ShadowDOM version.
+    content::TestNavigationManager navigation(web_contents(), url);
+
+    constexpr char kAddFencedFrameScript[] = R"({
+        const fenced_frame = document.createElement('fencedframe');
+        fenced_frame.src = $1;
+        document.body.appendChild(fenced_frame);
+    })";
+    EXPECT_TRUE(ExecJs(parent, content::JsReplace(kAddFencedFrameScript, url)));
+    navigation.WaitForNavigationFinished();
+
+    return ChildFrameAt(parent, 0);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<content::test::FencedFrameTestHelper> fenced_frame_helper_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    SitePerProcessFencedFrameTest,
+    SitePerProcessFencedFrameTest,
+    testing::Values(blink::features::FencedFramesImplementationType::kShadowDOM,
+                    blink::features::FencedFramesImplementationType::kMPArch));
+
+IN_PROC_BROWSER_TEST_P(SitePerProcessFencedFrameTest,
+                       PopupFromFencedFrameDoesNotCreateProxy) {
+  GURL main_url(embedded_test_server()->GetURL("/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+
+  // Create a fenced frame.
+  GURL fenced_frame_url(
+      embedded_test_server()->GetURL("/fenced_frames/title1.html"));
+  RenderFrameHost* fenced_frame_host =
+      CreateFencedFrame(web_contents()->GetMainFrame(), fenced_frame_url);
+  EXPECT_NE(nullptr, fenced_frame_host);
+
+  // Open a popup named "foo" from the fenced frame.
+  Shell* popup_shell =
+      OpenPopup(fenced_frame_host, GURL(url::kAboutBlankURL), "foo", "", false);
+  EXPECT_TRUE(popup_shell);
+
+  // Check that the popup from the fenced frame didn't create a proxy.
+  // Opening popups from fenced frames forces noopener, which makes named
+  // frames not discoverable.
+  FrameTreeNode* popup_root =
+      static_cast<WebContentsImpl*>(popup_shell->web_contents())
+          ->GetPrimaryFrameTree()
+          .root();
+  EXPECT_EQ(nullptr, popup_root->opener());
+
+  SiteInstanceImpl* site_instance =
+      root->current_frame_host()->GetSiteInstance();
+  EXPECT_FALSE(popup_root->current_frame_host()
+                   ->browsing_context_state()
+                   ->GetRenderFrameProxyHost(site_instance->group()));
+
+  SiteInstanceImpl* embedder_site_instance =
+      static_cast<RenderFrameHostImpl*>(fenced_frame_host)->GetSiteInstance();
+  EXPECT_FALSE(popup_root->current_frame_host()
+                   ->browsing_context_state()
+                   ->GetRenderFrameProxyHost(embedder_site_instance->group()));
+}
+
 // Similar to DiscoverNamedFrameFromAncestorOfOpener, but check that if a
 // window is created without a name and acquires window.name later, it will
 // still be discoverable from its opener's ancestors.  Also, instead of using
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 62818b1..61bc6305 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -151,6 +151,7 @@
 #include "services/network/public/cpp/web_sandbox_flags.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/loader/resource_type_util.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
@@ -310,10 +311,23 @@
   if (policy->IsPseudoScheme(protocol))
     return false;
 
+  // Implementation of the protocol handler arguments normalization steps defined
+  // in the spec.
+  // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
+  //
+  // Verify custom handler schemes for errors as described in steps 1 and 2
+  if (!blink::IsValidCustomHandlerScheme(protocol, security_level))
+    return false;
+
+  // TODO(jfernandez): Should we include syntax checks (step 3) as we do in the
+  // renderer process ?
+
+  // Verify custom handler URL security as described in steps 6 and 7
+  if (!blink::IsAllowedCustomHandlerURL(url, security_level))
+    return false;
   url::Origin url_origin = url::Origin::Create(url);
   if (url_origin.opaque())
     return false;
-
   if (security_level < blink::ProtocolHandlerSecurityLevel::kUntrustedOrigins &&
       !origin.IsSameOriginWith(url))
     return false;
@@ -6180,6 +6194,9 @@
   blink::ProtocolHandlerSecurityLevel security_level =
       delegate_->GetProtocolHandlerSecurityLevel(source);
 
+  // Run the protocol handler arguments normalization process defined in the
+  // spec.
+  // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
   if (!AreValidRegisterProtocolHandlerArguments(
           protocol, url, source->GetLastCommittedOrigin(), security_level)) {
     ReceivedBadMessage(source->GetProcess(),
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index ecab8fcc..2f39947 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -135,19 +135,11 @@
 // absl::optional fields should be nullopt to prevent the corresponding
 // methods from having EXPECT_CALL set on the mocks.
 typedef struct {
-  std::string test_name;
   RequestParameters inputs;
   RequestExpectations expected;
   MockConfiguration config;
 } AuthRequestTestCase;
 
-std::ostream& operator<<(std::ostream& os,
-                         const AuthRequestTestCase& testcase) {
-  std::string name;
-  base::ReplaceChars(testcase.test_name, " ", "", &name);
-  return os << name;
-}
-
 static const MockMediatedConfiguration kMediatedNoop{absl::nullopt, kAccounts,
                                                      absl::nullopt};
 static const MockClientIdConfiguration kSuccessfulClientId{
@@ -165,8 +157,8 @@
 static const MockClientIdConfiguration kClientMetadataNoPrivacyPolicyUrl{
     FetchStatus::kSuccess, "", ""};
 
+// Error parsing FedCM manifest due to missing token endpoint.
 static const AuthRequestTestCase kMissingTokenEndpoint{
-    "Error parsing FedCM manifest for Mediated mode missing token endpoint",
     {kIdpTestOrigin, kClientId, kNonce},
     {RequestIdTokenStatus::kError,
      FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
@@ -174,8 +166,8 @@
     {kToken, FetchStatus::kSuccess, absl::nullopt, kAccountsEndpoint, "",
      kClientMetadataEndpoint, kMediatedNoop}};
 
+// Error parsing FedCM manifest due to missing accounts endpoint.
 static const AuthRequestTestCase kMissingAccountsEndpoint{
-    "Error parsing FedCM manifest for Mediated mode missing accounts endpoint",
     {kIdpTestOrigin, kClientId, kNonce},
     {RequestIdTokenStatus::kError,
      FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
@@ -183,9 +175,8 @@
     {kToken, FetchStatus::kSuccess, absl::nullopt, "", kTokenEndpoint,
      kClientMetadataEndpoint, kMediatedNoop}};
 
+// Error parsing FedCM manifest due to missing client metadata endpoint.
 static const AuthRequestTestCase kMissingClientMetadata{
-    "Error parsing FedCM manifest for Mediated mode missing client metadata "
-    "endpoint",
     {kIdpTestOrigin, kClientId, kNonce},
     {RequestIdTokenStatus::kError,
      FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
@@ -193,96 +184,6 @@
     {kToken, FetchStatus::kSuccess, absl::nullopt, kAccountsEndpoint,
      kTokenEndpoint, "", kMediatedNoop}};
 
-static const AuthRequestTestCase kMediatedTestCases[]{
-    kMissingTokenEndpoint,
-    kMissingAccountsEndpoint,
-    kMissingClientMetadata,
-
-    {"Error due to accounts endpoint in different origin than identity "
-     "provider",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kError,
-      FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
-      kEmptyToken},
-     {kToken, FetchStatus::kSuccess, absl::nullopt,
-      kCrossOriginAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
-      kMediatedNoop}},
-
-    {"Error reaching Accounts endpoint",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kError,
-      FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse,
-      kEmptyToken},
-     {kEmptyToken,
-      FetchStatus::kSuccess,
-      kSuccessfulClientId,
-      kAccountsEndpoint,
-      kTokenEndpoint,
-      kClientMetadataEndpoint,
-      {FetchStatus::kNoResponseError, kAccounts, absl::nullopt}}},
-
-    {"Error parsing Accounts response",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kError,
-      FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse,
-      kEmptyToken},
-     {kToken,
-      FetchStatus::kSuccess,
-      kSuccessfulClientId,
-      kAccountsEndpoint,
-      kTokenEndpoint,
-      kClientMetadataEndpoint,
-      {FetchStatus::kInvalidResponseError, kAccounts, absl::nullopt}}},
-
-    {"Successful Mediated flow",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess,
-      kToken},
-     {kToken,
-      FetchStatus::kSuccess,
-      kSuccessfulClientId,
-      kAccountsEndpoint,
-      kTokenEndpoint,
-      kClientMetadataEndpoint,
-      {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}},
-
-    {"Client metadata file not found",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kError,
-      FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound,
-      kEmptyToken},
-     {kToken, FetchStatus::kSuccess, kClientMetadataHttpNotFound,
-      kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
-      kMediatedNoop}},
-
-    {"Client metadata empty response",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kError,
-      FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse,
-      kEmptyToken},
-     {kToken, FetchStatus::kSuccess, kClientMetadataNoResponse,
-      kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
-      kMediatedNoop}},
-
-    {"Client metadata invalid response",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kError,
-      FederatedAuthRequestResult::kErrorFetchingClientMetadataInvalidResponse,
-      kEmptyToken},
-     {kToken, FetchStatus::kSuccess, kClientMetadataInvalidResponse,
-      kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
-      kMediatedNoop}},
-
-    {"Client metadata has no privacy policy url",
-     {kIdpTestOrigin, kClientId, kNonce},
-     {RequestIdTokenStatus::kError,
-      FederatedAuthRequestResult::kErrorClientMetadataMissingPrivacyPolicyUrl,
-      kEmptyToken},
-     {kToken, FetchStatus::kSuccess, kClientMetadataNoPrivacyPolicyUrl,
-      kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
-      kMediatedNoop}},
-};
-
 // Helper class for receiving the mojo method callback.
 class AuthRequestCallbackHelper {
  public:
@@ -421,6 +322,74 @@
   }
   ~FederatedAuthRequestImplTest() override = default;
 
+  void RunAuthTest(const AuthRequestTestCase& test_case) {
+    CreateAuthRequest(GURL(test_case.inputs.provider));
+    SetMockExpectations(test_case);
+    auto auth_response =
+        PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce,
+                           test_case.inputs.prefer_auto_sign_in);
+    EXPECT_EQ(auth_response.first, test_case.expected.return_status);
+    EXPECT_EQ(auth_response.second, test_case.expected.token);
+
+    EXPECT_EQ(main_test_rfh()->GetFederatedAuthRequestIssueCount(
+                  test_case.expected.devtools_issue_status),
+              auth_response.first == RequestIdTokenStatus::kSuccess ? 0 : 1);
+    CheckConsoleMessages(test_case.expected.devtools_issue_status);
+  }
+
+  void CheckConsoleMessages(FederatedAuthRequestResult devtools_issue_status) {
+    static std::unordered_map<FederatedAuthRequestResult,
+                              absl::optional<std::string>>
+        status_to_message = {
+            {FederatedAuthRequestResult::kSuccess, absl::nullopt},
+            {FederatedAuthRequestResult::kApprovalDeclined,
+             "User declined the sign-in attempt."},
+            {FederatedAuthRequestResult::kErrorFetchingManifestHttpNotFound,
+             "The provider's FedCM manifest configuration cannot be found."},
+            {FederatedAuthRequestResult::kErrorFetchingManifestNoResponse,
+             "The provider's FedCM manifest configuration fetch resulted in an "
+             "error response code."},
+            {FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
+             "Provider's FedCM manifest configuration is invalid."},
+            {FederatedAuthRequestResult::kErrorFetchingSignin,
+             "Error attempting to reach the provider's sign-in endpoint."},
+            {FederatedAuthRequestResult::kErrorInvalidSigninResponse,
+             "Provider's sign-in response is invalid."},
+            {FederatedAuthRequestResult::kError,
+             "Error retrieving an id token."},
+            {FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse,
+             "The provider's accounts list fetch resulted in an error response "
+             "code."},
+            {FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse,
+             "Provider's accounts list is invalid. Should have received an "
+             "\"accounts\" list, where each account must "
+             "have at least \"id\", \"name\", and \"email\"."},
+            {FederatedAuthRequestResult::
+                 kErrorFetchingClientMetadataHttpNotFound,
+             "The provider's client metadata endpoint cannot be found."},
+            {FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse,
+             "The provider's client metadata fetch resulted in an error "
+             "response "
+             "code."},
+            {FederatedAuthRequestResult::
+                 kErrorFetchingClientMetadataInvalidResponse,
+             "Provider's client metadata is invalid."},
+            {FederatedAuthRequestResult::
+                 kErrorClientMetadataMissingPrivacyPolicyUrl,
+             "Provider's client metadata is missing or has an invalid privacy "
+             "policy url."}};
+    std::vector<std::string> messages =
+        RenderFrameHostTester::For(main_rfh())->GetConsoleMessages();
+    absl::optional<std::string> expected_message =
+        status_to_message[devtools_issue_status];
+    if (!expected_message) {
+      EXPECT_EQ(0u, messages.size());
+    } else {
+      ASSERT_LE(1u, messages.size());
+      EXPECT_EQ(expected_message.value(), messages[messages.size() - 1]);
+    }
+  }
+
   FederatedAuthRequestImpl& CreateAuthRequest(const GURL& provider) {
     provider_ = provider;
     // `FederatedAuthRequestService` derives from `DocumentService` and
@@ -727,93 +696,29 @@
   std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
 };
 
-class BasicFederatedAuthRequestImplTest
-    : public FederatedAuthRequestImplTest,
-      public ::testing::WithParamInterface<AuthRequestTestCase> {};
+class BasicFederatedAuthRequestImplTest : public FederatedAuthRequestImplTest {
+};
 
-INSTANTIATE_TEST_SUITE_P(MediatedTests,
-                         BasicFederatedAuthRequestImplTest,
-                         ::testing::ValuesIn(kMediatedTestCases),
-                         ::testing::PrintToStringParamName());
-
-// Exercise the auth test case give the configuration.
-TEST_P(BasicFederatedAuthRequestImplTest, FederatedAuthRequests) {
-  AuthRequestTestCase test_case = GetParam();
-  CreateAuthRequest(GURL(test_case.inputs.provider));
-  SetMockExpectations(test_case);
-  auto auth_response =
-      PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce,
-                         test_case.inputs.prefer_auto_sign_in);
-  EXPECT_EQ(auth_response.first, test_case.expected.return_status);
-  EXPECT_EQ(auth_response.second, test_case.expected.token);
+// Test successful FedCM request.
+TEST_F(BasicFederatedAuthRequestImplTest, SuccessfulRequest) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess,
+       kToken},
+      {kToken,
+       FetchStatus::kSuccess,
+       kSuccessfulClientId,
+       kAccountsEndpoint,
+       kTokenEndpoint,
+       kClientMetadataEndpoint,
+       {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}};
+  RunAuthTest(test_case);
 }
 
-TEST_P(BasicFederatedAuthRequestImplTest, FederatedAuthRequestIssue) {
-  AuthRequestTestCase test_case = GetParam();
-  CreateAuthRequest(GURL(test_case.inputs.provider));
-  SetMockExpectations(test_case);
-  auto auth_response =
-      PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce,
-                         test_case.inputs.prefer_auto_sign_in);
-  EXPECT_EQ(main_test_rfh()->GetFederatedAuthRequestIssueCount(
-                test_case.expected.devtools_issue_status),
-            auth_response.first == RequestIdTokenStatus::kSuccess ? 0 : 1);
-  static std::unordered_map<FederatedAuthRequestResult,
-                            absl::optional<std::string>>
-      status_to_message = {
-          {FederatedAuthRequestResult::kSuccess, absl::nullopt},
-          {FederatedAuthRequestResult::kApprovalDeclined,
-           "User declined the sign-in attempt."},
-          {FederatedAuthRequestResult::kErrorFetchingManifestHttpNotFound,
-           "The provider's FedCM manifest configuration cannot be found."},
-          {FederatedAuthRequestResult::kErrorFetchingManifestNoResponse,
-           "The provider's FedCM manifest configuration fetch resulted in an "
-           "error response code."},
-          {FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
-           "Provider's FedCM manifest configuration is invalid."},
-          {FederatedAuthRequestResult::kErrorFetchingSignin,
-           "Error attempting to reach the provider's sign-in endpoint."},
-          {FederatedAuthRequestResult::kErrorInvalidSigninResponse,
-           "Provider's sign-in response is invalid."},
-          {FederatedAuthRequestResult::kError, "Error retrieving an id token."},
-          {FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse,
-           "The provider's accounts list fetch resulted in an error response "
-           "code."},
-          {FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse,
-           "Provider's accounts list is invalid. Should have received an "
-           "\"accounts\" list, where each account must "
-           "have at least \"id\", \"name\", and \"email\"."},
-          {FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound,
-           "The provider's client metadata endpoint cannot be found."},
-          {FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse,
-           "The provider's client metadata fetch resulted in an error response "
-           "code."},
-          {FederatedAuthRequestResult::
-               kErrorFetchingClientMetadataInvalidResponse,
-           "Provider's client metadata is invalid."},
-          {FederatedAuthRequestResult::
-               kErrorClientMetadataMissingPrivacyPolicyUrl,
-           "Provider's client metadata is missing or has an invalid privacy "
-           "policy url."}};
-  std::vector<std::string> messages =
-      RenderFrameHostTester::For(main_rfh())->GetConsoleMessages();
-  absl::optional<std::string> expected_message =
-      status_to_message[test_case.expected.devtools_issue_status];
-  if (!expected_message) {
-    EXPECT_EQ(0u, messages.size());
-  } else {
-    ASSERT_LE(1u, messages.size());
-    EXPECT_EQ(expected_message.value(), messages[messages.size() - 1]);
-  }
-}
-
+// Test that request fails if manifest is missing token endpoint.
 TEST_F(BasicFederatedAuthRequestImplTest, MissingTokenEndpoint) {
-  const auto& test_case = kMissingTokenEndpoint;
-  CreateAuthRequest(GURL(test_case.inputs.provider));
-  SetMockExpectations(test_case);
-  auto auth_response =
-      PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce,
-                         test_case.inputs.prefer_auto_sign_in);
+  RunAuthTest(kMissingTokenEndpoint);
+
   std::vector<std::string> messages =
       RenderFrameHostTester::For(main_rfh())->GetConsoleMessages();
   ASSERT_EQ(2U, messages.size());
@@ -825,13 +730,10 @@
   EXPECT_EQ("Provider's FedCM manifest configuration is invalid.", messages[1]);
 }
 
+// Test that request fails if manifest is missing accounts endpoint.
 TEST_F(BasicFederatedAuthRequestImplTest, MissingAccountsEndpoint) {
-  const auto& test_case = kMissingAccountsEndpoint;
-  CreateAuthRequest(GURL(test_case.inputs.provider));
-  SetMockExpectations(test_case);
-  auto auth_response =
-      PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce,
-                         test_case.inputs.prefer_auto_sign_in);
+  RunAuthTest(kMissingAccountsEndpoint);
+
   std::vector<std::string> messages =
       RenderFrameHostTester::For(main_rfh())->GetConsoleMessages();
   ASSERT_EQ(2U, messages.size());
@@ -843,13 +745,10 @@
   EXPECT_EQ("Provider's FedCM manifest configuration is invalid.", messages[1]);
 }
 
+// Test that request fails if manifest is missing client metadata endpoint.
 TEST_F(BasicFederatedAuthRequestImplTest, MissingClientMetadataEndpoint) {
-  const auto& test_case = kMissingClientMetadata;
-  CreateAuthRequest(GURL(test_case.inputs.provider));
-  SetMockExpectations(test_case);
-  auto auth_response =
-      PerformAuthRequest(test_case.inputs.client_id, test_case.inputs.nonce,
-                         test_case.inputs.prefer_auto_sign_in);
+  RunAuthTest(kMissingClientMetadata);
+
   std::vector<std::string> messages =
       RenderFrameHostTester::For(main_rfh())->GetConsoleMessages();
   ASSERT_EQ(2U, messages.size());
@@ -861,10 +760,111 @@
   EXPECT_EQ("Provider's FedCM manifest configuration is invalid.", messages[1]);
 }
 
+// Test that request fails if the accounts endpoint is in a different origin
+// than identity provider.
+TEST_F(BasicFederatedAuthRequestImplTest, AccountEndpointDifferentOriginIdp) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kError,
+       FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
+       kEmptyToken},
+      {kToken, FetchStatus::kSuccess, absl::nullopt,
+       kCrossOriginAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
+       kMediatedNoop}};
+  RunAuthTest(test_case);
+}
+
+// Test that request fails if accounts endpoint cannot be reached.
+TEST_F(BasicFederatedAuthRequestImplTest, AccountEndpointCannotBeReached) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kError,
+       FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse,
+       kEmptyToken},
+      {kEmptyToken,
+       FetchStatus::kSuccess,
+       kSuccessfulClientId,
+       kAccountsEndpoint,
+       kTokenEndpoint,
+       kClientMetadataEndpoint,
+       {FetchStatus::kNoResponseError, kAccounts, absl::nullopt}}};
+  RunAuthTest(test_case);
+}
+
+// Test that request fails if account endpoint response cannot be parsed.
+TEST_F(BasicFederatedAuthRequestImplTest, AccountsCannotBeParsed) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kError,
+       FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse,
+       kEmptyToken},
+      {kToken,
+       FetchStatus::kSuccess,
+       kSuccessfulClientId,
+       kAccountsEndpoint,
+       kTokenEndpoint,
+       kClientMetadataEndpoint,
+       {FetchStatus::kInvalidResponseError, kAccounts, absl::nullopt}}};
+  RunAuthTest(test_case);
+}
+
+// Test that request fails if client metadata cannot be found.
+TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataNotFound) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kError,
+       FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound,
+       kEmptyToken},
+      {kToken, FetchStatus::kSuccess, kClientMetadataHttpNotFound,
+       kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
+       kMediatedNoop}};
+  RunAuthTest(test_case);
+}
+
+// Test that request fails if client metadata endpoint returns empty response.
+TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataEmptyResponse) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kError,
+       FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse,
+       kEmptyToken},
+      {kToken, FetchStatus::kSuccess, kClientMetadataNoResponse,
+       kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
+       kMediatedNoop}};
+  RunAuthTest(test_case);
+}
+
+// Test that request fails if client metadata returns invalid response.
+TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataInvalidResponse) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kError,
+       FederatedAuthRequestResult::kErrorFetchingClientMetadataInvalidResponse,
+       kEmptyToken},
+      {kToken, FetchStatus::kSuccess, kClientMetadataInvalidResponse,
+       kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
+       kMediatedNoop}};
+  RunAuthTest(test_case);
+}
+
+// Test that request fails if client metadata does not contain a privacy policy
+// URL.
+TEST_F(BasicFederatedAuthRequestImplTest, ClientMetadataNoPrivacyUrl) {
+  AuthRequestTestCase test_case = {
+      {kIdpTestOrigin, kClientId, kNonce},
+      {RequestIdTokenStatus::kError,
+       FederatedAuthRequestResult::kErrorClientMetadataMissingPrivacyPolicyUrl,
+       kEmptyToken},
+      {kToken, FetchStatus::kSuccess, kClientMetadataNoPrivacyPolicyUrl,
+       kAccountsEndpoint, kTokenEndpoint, kClientMetadataEndpoint,
+       kMediatedNoop}};
+  RunAuthTest(test_case);
+}
+
+// Test that request fails if all of the endpoints in the manifest are invalid.
 TEST_F(BasicFederatedAuthRequestImplTest, AllInvalidEndpoints) {
   // Both an empty url and cross origin urls are invalid endpoints.
   AuthRequestTestCase test_case = {
-      "FedCM manifest missing all endpoints",
       {kIdpTestOrigin, kClientId, kNonce},
       {RequestIdTokenStatus::kError,
        FederatedAuthRequestResult::kErrorFetchingManifestInvalidResponse,
@@ -946,8 +946,8 @@
 
 // Tests for Login State
 
+// Successful mediated flow with one account.
 static const AuthRequestTestCase kSuccessfulMediatedSignUpTestCase{
-    "Successful mediated flow with one account",
     {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn},
     {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess,
      kToken},
@@ -959,8 +959,8 @@
      kClientMetadataEndpoint,
      {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}};
 
+// Failed mediated flow with one account.
 static const AuthRequestTestCase kFailedMediatedSignUpTestCase{
-    "Failed mediated flow with one account",
     {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn},
     {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess,
      kToken},
@@ -972,8 +972,8 @@
      kClientMetadataEndpoint,
      {FetchStatus::kSuccess, kAccounts, FetchStatus::kInvalidResponseError}}};
 
+// Successful mediated flow with one account.
 static const AuthRequestTestCase kSuccessfulMediatedAutoSignInTestCase{
-    "Successful mediated flow with one account",
     {kIdpTestOrigin, kClientId, kNonce, kPreferAutoSignIn},
     {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess,
      kToken},
@@ -1346,12 +1346,12 @@
   ExpectRequestIdTokenStatusUKM(IdTokenStatus::kSuccess);
 }
 
+// Test that request fails if user does not select an account.
 TEST_F(BasicFederatedAuthRequestImplTest, MetricsForNotSelectingAccount) {
   base::HistogramTester histogram_tester_;
 
   AccountList displayed_accounts;
   const AuthRequestTestCase test_case = {
-      "Failed mediated flow due to user not selecting an account",
       {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn},
       {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess,
        kToken},
@@ -1440,6 +1440,7 @@
   histogram_tester_.ExpectUniqueSample("Blink.FedCm.WebContentsVisible", 1, 1);
 }
 
+// Test that request fails if the web contents are hidden.
 TEST_F(BasicFederatedAuthRequestImplTest, MetricsForWebContentsInvisible) {
   base::HistogramTester histogram_tester;
   WebContentsImpl* web_contents_impl =
@@ -1448,7 +1449,6 @@
   ASSERT_EQ(web_contents_impl->GetVisibility(), Visibility::VISIBLE);
 
   const AuthRequestTestCase test_case = {
-      "Failed mediated flow due to user leaving the page",
       {kIdpTestOrigin, kClientId, kNonce, kNotPreferAutoSignIn},
       {RequestIdTokenStatus::kSuccess, FederatedAuthRequestResult::kSuccess,
        kToken},
diff --git a/content/child/child_process_sandbox_support_impl_mac.cc b/content/child/child_process_sandbox_support_impl_mac.cc
index fc7f56d..dc7a9464 100644
--- a/content/child/child_process_sandbox_support_impl_mac.cc
+++ b/content/child/child_process_sandbox_support_impl_mac.cc
@@ -9,11 +9,10 @@
 
 #include "base/bind.h"
 #include "base/mac/scoped_cftyperef.h"
-#include "base/numerics/safe_conversions.h"
+#include "base/memory/read_only_shared_memory_region.h"
 #include "base/strings/sys_string_conversions.h"
 #include "content/common/mac/font_loader.h"
 #include "content/public/child/child_thread.h"
-#include "mojo/public/cpp/system/buffer.h"
 
 namespace content {
 
@@ -36,10 +35,10 @@
   base::ScopedCFTypeRef<CFStringRef> name_ref(CTFontCopyPostScriptName(font));
   std::u16string font_name = SysCFStringRefToUTF16(name_ref);
   float font_point_size = CTFontGetSize(font);
-  mojo::ScopedSharedBufferHandle font_data;
+  base::ReadOnlySharedMemoryRegion font_data;
   bool success = sandbox_support_->LoadFont(font_name, font_point_size,
                                             &font_data, font_id) &&
-                 *font_id > 0 && font_data.is_valid();
+                 *font_id > 0 && font_data.IsValid();
   if (!success) {
     DLOG(ERROR) << "Bad response from LoadFont() for " << font_name;
     out_descriptor->reset();
@@ -47,16 +46,14 @@
     return false;
   }
 
-  uint64_t font_data_size = font_data->GetSize();
+  size_t font_data_size = font_data.GetSize();
   DCHECK_GT(font_data_size, 0U);
-  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(font_data_size));
 
   // TODO(jeremy): Need to call back into the requesting process to make sure
   // that the font isn't already activated, based on the font id.  If it's
   // already activated, don't reactivate it here - https://crbug.com/72727 .
-  return FontLoader::CTFontDescriptorFromBuffer(
-      std::move(font_data), static_cast<uint32_t>(font_data_size),
-      out_descriptor);
+  return FontLoader::CTFontDescriptorFromBuffer(std::move(font_data),
+                                                out_descriptor);
 }
 
 SkColor WebSandboxSupportMac::GetSystemColor(
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 81ea9545..e9db09d 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -376,6 +376,7 @@
           {"ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes",
            blink::features::
                kThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes},
+          {"TopicsAPI", blink::features::kBrowsingTopics},
           {"TrustedDOMTypes", features::kTrustedDOMTypes},
           {"UserAgentClientHint", blink::features::kUserAgentClientHint},
           {"ViewportHeightClientHintHeader",
diff --git a/content/common/mac/font_loader.h b/content/common/mac/font_loader.h
index 4d3f3c8a..e0ccbb3 100644
--- a/content/common/mac/font_loader.h
+++ b/content/common/mac/font_loader.h
@@ -13,8 +13,8 @@
 
 #include "base/callback_forward.h"
 #include "base/mac/scoped_cftyperef.h"
+#include "base/memory/read_only_shared_memory_region.h"
 #include "content/common/content_export.h"
-#include "mojo/public/cpp/system/buffer.h"
 
 namespace content {
 
@@ -30,17 +30,17 @@
     ResultInternal();
     ~ResultInternal();
 
-    mojo::ScopedSharedBufferHandle font_data;
+    base::ReadOnlySharedMemoryRegion font_data;
     uint32_t font_id = 0;
   };
 
   // Callback for the reporting result of LoadFont().
-  // - The ScopedSharedBufferHandle points to a shared memory buffer containing
-  //   the raw data for the font file.
+  // - The base::ReadOnlySharedMemoryRegion points to a shared memory region
+  //   containing the raw data for the font file.
   // - The last argument is the font_id: a unique identifier for the on-disk
   //   file we load for the font.
   using LoadedCallback =
-      base::OnceCallback<void(mojo::ScopedSharedBufferHandle, uint32_t)>;
+      base::OnceCallback<void(base::ReadOnlySharedMemoryRegion, uint32_t)>;
 
   // Load a font specified by |font| into a shared memory buffer suitable for
   // sending over IPC. On failure, zeroes and an invalid handle are reported
@@ -50,10 +50,10 @@
                        float font_point_size,
                        LoadedCallback callback);
 
-  // Given a shared memory buffer containing the raw data for a font file, load
+  // Given a shared memory region containing the raw data for a font file, load
   // the font and turn them into a CTFontDescriptor.
   //
-  // |data| - A shared memory handle pointing to the raw data from a font file.
+  // |data| - A shared memory region contained the raw data from a font file.
   // |data_size| - Size of |data|.
   //
   // On return:
@@ -62,8 +62,7 @@
   //  The caller is responsible for releasing this value via CFRelease().
   CONTENT_EXPORT
   static bool CTFontDescriptorFromBuffer(
-      mojo::ScopedSharedBufferHandle font_data,
-      uint32_t font_data_size,
+      base::ReadOnlySharedMemoryRegion font_data,
       base::ScopedCFTypeRef<CTFontDescriptorRef>* out_descriptor);
 
   CONTENT_EXPORT
diff --git a/content/common/mac/font_loader.mm b/content/common/mac/font_loader.mm
index 51c0bf2..fca241f0 100644
--- a/content/common/mac/font_loader.mm
+++ b/content/common/mac/font_loader.mm
@@ -18,6 +18,8 @@
 #import "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #import "base/mac/scoped_nsobject.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -76,22 +78,22 @@
 
   auto result = std::make_unique<FontLoader::ResultInternal>();
 
-  int32_t font_file_size_32 = static_cast<int32_t>(font_file_size_64);
-  result->font_data = mojo::SharedBufferHandle::Create(font_file_size_32);
-  if (!result->font_data.is_valid()) {
+  // Note: despite the fact that CTFontDescriptorFromBuffer() takes a uint32_t
+  // size, this intentionally works with int32_t since base::ReadFile()'s max
+  // size arg is an int...
+  int32_t font_file_size_32 = base::checked_cast<int32_t>(font_file_size_64);
+  auto region_and_mapping =
+      base::ReadOnlySharedMemoryRegion::Create(font_file_size_32);
+
+  if (!region_and_mapping.IsValid()) {
     DLOG(ERROR) << "Failed to create shmem area for " << font_name;
     return nullptr;
   }
+  result->font_data = std::move(region_and_mapping.region);
 
-  mojo::ScopedSharedBufferMapping mapping =
-      result->font_data->Map(font_file_size_32);
-  if (!mapping) {
-    DLOG(ERROR) << "Failed to create shmem mapping for " << font_name;
-    return nullptr;
-  }
-
-  int32_t amt_read = base::ReadFile(
-      font_path, static_cast<char*>(mapping.get()), font_file_size_32);
+  int32_t amt_read =
+      base::ReadFile(font_path, region_and_mapping.mapping.GetMemoryAs<char>(),
+                     font_file_size_32);
   if (amt_read != font_file_size_32) {
     DLOG(ERROR) << "Failed to read font data for " << font_path.value();
     return nullptr;
@@ -112,7 +114,7 @@
 void ReplyOnUIThread(FontLoader::LoadedCallback callback,
                      std::unique_ptr<FontLoader::ResultInternal> result) {
   if (!result) {
-    std::move(callback).Run(mojo::ScopedSharedBufferHandle(), 0);
+    std::move(callback).Run(base::ReadOnlySharedMemoryRegion(), 0);
     return;
   }
 
@@ -144,15 +146,14 @@
 
 // static
 bool FontLoader::CTFontDescriptorFromBuffer(
-    mojo::ScopedSharedBufferHandle font_data,
-    uint32_t font_data_size,
+    base::ReadOnlySharedMemoryRegion font_data,
     base::ScopedCFTypeRef<CTFontDescriptorRef>* out_descriptor) {
   out_descriptor->reset();
-  mojo::ScopedSharedBufferMapping mapping = font_data->Map(font_data_size);
-  if (!mapping)
+  base::ReadOnlySharedMemoryMapping mapping = font_data.Map();
+  if (!mapping.IsValid())
     return false;
 
-  NSData* data = [NSData dataWithBytes:mapping.get() length:font_data_size];
+  NSData* data = [NSData dataWithBytes:mapping.memory() length:mapping.size()];
   base::ScopedCFTypeRef<CTFontDescriptorRef> data_descriptor(
       CTFontManagerCreateFontDescriptorFromData(base::mac::NSToCFCast(data)));
 
diff --git a/content/common/sandbox_support_mac.mojom b/content/common/sandbox_support_mac.mojom
index 4bc19e2a..f7062a2 100644
--- a/content/common/sandbox_support_mac.mojom
+++ b/content/common/sandbox_support_mac.mojom
@@ -17,5 +17,6 @@
   // Request the browser to load a font into a shared memory buffer.
   [Sync]
   LoadFont(mojo_base.mojom.String16 font_name, float font_point_size)
-      => (handle<shared_buffer>? font_data, uint32 font_id);
+      => (mojo_base.mojom.ReadOnlySharedMemoryRegion? font_data,
+          uint32 font_id);
 };
diff --git a/content/public/browser/browsing_topics_site_data_manager.h b/content/public/browser/browsing_topics_site_data_manager.h
index bb81bb2f..0b194814 100644
--- a/content/public/browser/browsing_topics_site_data_manager.h
+++ b/content/public/browser/browsing_topics_site_data_manager.h
@@ -40,7 +40,7 @@
   // Persist the browsing topics api usage context to storage. Called when the
   // usage is detected in a context on a page.
   virtual void OnBrowsingTopicsApiUsed(
-      const browsing_topics::HashedHost& hashed_top_host,
+      const browsing_topics::HashedHost& hashed_main_frame_host,
       const base::flat_set<browsing_topics::HashedDomain>&
           hashed_context_domains) = 0;
 };
diff --git a/content/public/browser/download_manager_delegate.h b/content/public/browser/download_manager_delegate.h
index ee482dfe..20b2e89 100644
--- a/content/public/browser/download_manager_delegate.h
+++ b/content/public/browser/download_manager_delegate.h
@@ -56,6 +56,9 @@
 //     |intermediate_path| could be the same as |target_path|. Both paths must
 //     be in the same directory.
 //
+// |display_name| specifies the suggested file name in case the file name cannot
+//     be determined from target_path. Could be empty.
+//
 // |interrupt_reason| should be set to DOWNLOAD_INTERRUPT_REASON_NONE in
 //     order to proceed with the download. DOWNLOAD_INTERRUPT_REASON_USER_CANCEL
 //     results in the download being marked cancelled. Any other value results
@@ -67,6 +70,7 @@
     download::DownloadDangerType danger_type,
     download::DownloadItem::MixedContentStatus mixed_content_status,
     const base::FilePath& intermediate_path,
+    const base::FilePath& display_name,
     absl::optional<download::DownloadSchedule> download_schedule,
     download::DownloadInterruptReason interrupt_reason)>;
 
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 68b521f..cb672eb 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -585,7 +585,7 @@
 
   // Returns whether a navigation is currently in progress that should show
   // loading UI if such UI exists (progress bar, loading spinner, stop button,
-  // etc.) True for different-document navigations and appHistory's
+  // etc.) True for different-document navigations and the navigation API's
   // transitionWhile(). This being true implies that IsLoading() is also true.
   virtual bool ShouldShowLoadingUI() = 0;
 
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index f889d0b..9c70cc9 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -166,9 +166,9 @@
   // in UI elements. It is generally true for different-document navigations and
   // false for most same-document navigations (because same-documents are
   // typically instantaneous so there's no point in flickering the UI). The
-  // exception is appHistory's transitionWhile, which is the sole type of
-  // same-document navigation that is asynchronous, and therefore a UI change is
-  // sensible.
+  // exception is the navigation API's transitionWhile(), which is the sole type
+  // of same-document navigation that is asynchronous, and therefore a UI change
+  // is sensible.
   virtual void LoadingStateChanged(WebContents* source,
                                    bool should_show_loading_ui) {}
 
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index 5bd1b99..48a524a7 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -161,10 +161,6 @@
   std::move(cb).Run({});
 }
 
-bool ContentRendererClient::IsKeySystemsUpdateNeeded() {
-  return false;
-}
-
 bool ContentRendererClient::IsSupportedAudioType(const media::AudioType& type) {
   // Defer to media's default support.
   return ::media::IsDefaultSupportedAudioType(type);
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 63ead7f8..eb8968c 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -254,11 +254,6 @@
   // Allows embedder to register the key system(s) it supports.
   virtual void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb);
 
-  // Signal that embedder has changed key systems.
-  // TODO(chcunningham): Refactor this to a proper change "observer" API that is
-  // less fragile (don't assume GetSupportedKeySystems has just one caller).
-  virtual bool IsKeySystemsUpdateNeeded();
-
   // Allows embedder to describe customized audio capabilities.
   virtual bool IsSupportedAudioType(const media::AudioType& type);
 
diff --git a/content/public/renderer/key_system_support.cc b/content/public/renderer/key_system_support.cc
index 7ffed2be..7aee5af 100644
--- a/content/public/renderer/key_system_support.cc
+++ b/content/public/renderer/key_system_support.cc
@@ -6,36 +6,50 @@
 
 #include "base/logging.h"
 #include "content/public/renderer/render_thread.h"
-#include "media/mojo/mojom/key_system_support.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace content {
 
 namespace {
 
-// Helper function to help hold the `key_system_support` remote.
-void OnIsKeySystemSupportedResult(
-    mojo::Remote<media::mojom::KeySystemSupport> key_system_support,
-    IsKeySystemSupportedCB cb,
-    bool is_supported,
-    media::mojom::KeySystemCapabilityPtr capability) {
-  std::move(cb).Run(is_supported, std::move(capability));
-}
+class KeySystemSupportObserverImpl
+    : public media::mojom::KeySystemSupportObserver {
+ public:
+  explicit KeySystemSupportObserverImpl(KeySystemSupportCB cb)
+      : key_system_support_cb_(std::move(cb)) {}
+  KeySystemSupportObserverImpl(const KeySystemSupportObserverImpl&) = delete;
+  KeySystemSupportObserverImpl& operator=(const KeySystemSupportObserverImpl&) =
+      delete;
+  ~KeySystemSupportObserverImpl() override = default;
+
+  // media::mojom::KeySystemSupportObserver
+  void OnKeySystemSupportUpdated(
+      KeySystemCapabilityPtrMap key_system_capabilities) final {
+    key_system_support_cb_.Run(std::move(key_system_capabilities));
+  }
+
+ private:
+  KeySystemSupportCB key_system_support_cb_;
+};
 
 }  // namespace
 
-void IsKeySystemSupported(const std::string& key_system,
-                          IsKeySystemSupportedCB cb) {
-  DVLOG(3) << __func__ << ": key_system=" << key_system;
+void ObserveKeySystemSupportUpdate(KeySystemSupportCB cb) {
+  DVLOG(1) << __func__;
 
+  // `key_system_support` will be destructed after this function returns. This
+  // is fine since the observer will stay registered in KeySystemSupportImpl,
+  // which is a singleton in the browser process.
   mojo::Remote<media::mojom::KeySystemSupport> key_system_support;
   content::RenderThread::Get()->BindHostReceiver(
       key_system_support.BindNewPipeAndPassReceiver());
 
-  auto* key_system_support_raw = key_system_support.get();
-  key_system_support_raw->IsKeySystemSupported(
-      key_system, base::BindOnce(&OnIsKeySystemSupportedResult,
-                                 std::move(key_system_support), std::move(cb)));
+  mojo::PendingRemote<media::mojom::KeySystemSupportObserver> observer_remote;
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<KeySystemSupportObserverImpl>(std::move(cb)),
+      observer_remote.InitWithNewPipeAndPassReceiver());
+  key_system_support->AddObserver(std::move(observer_remote));
 }
 
 }  // namespace content
diff --git a/content/public/renderer/key_system_support.h b/content/public/renderer/key_system_support.h
index 911799c..b82376d 100644
--- a/content/public/renderer/key_system_support.h
+++ b/content/public/renderer/key_system_support.h
@@ -6,20 +6,23 @@
 #define CONTENT_PUBLIC_RENDERER_KEY_SYSTEM_SUPPORT_H_
 
 #include <string>
-#include <vector>
 
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "content/common/content_export.h"
 #include "media/mojo/mojom/key_system_support.mojom.h"
 
 namespace content {
 
-using IsKeySystemSupportedCB =
-    media::mojom::KeySystemSupport::IsKeySystemSupportedCallback;
+using KeySystemCapabilityPtrMap =
+    base::flat_map<std::string, media::mojom::KeySystemCapabilityPtr>;
+using KeySystemSupportCB =
+    base::RepeatingCallback<void(KeySystemCapabilityPtrMap)>;
 
-// Determines if |key_system| is supported by calling into the browser and
-// calls the `cb` with the result.
-CONTENT_EXPORT void IsKeySystemSupported(const std::string& key_system,
-                                         IsKeySystemSupportedCB cb);
+// Observes key system support updates. The callback `cb` will be called with
+// the current key system support, then called every time the key system support
+// changes.
+CONTENT_EXPORT void ObserveKeySystemSupportUpdate(KeySystemSupportCB cb);
 
 }  // namespace content
 
diff --git a/content/public/test/browsing_topics_test_util.cc b/content/public/test/browsing_topics_test_util.cc
new file mode 100644
index 0000000..bba7d29c
--- /dev/null
+++ b/content/public/test/browsing_topics_test_util.cc
@@ -0,0 +1,44 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/test/browsing_topics_test_util.h"
+
+#include "content/browser/browsing_topics/browsing_topics_site_data_manager_impl.h"
+
+namespace content {
+
+TesterBrowsingTopicsSiteDataManager::TesterBrowsingTopicsSiteDataManager(
+    const base::FilePath& user_data_directory)
+    : manager_impl_(
+          new BrowsingTopicsSiteDataManagerImpl(user_data_directory)) {}
+
+void TesterBrowsingTopicsSiteDataManager::ExpireDataBefore(base::Time time) {
+  manager_impl_->ExpireDataBefore(time);
+}
+
+TesterBrowsingTopicsSiteDataManager::~TesterBrowsingTopicsSiteDataManager() =
+    default;
+
+void TesterBrowsingTopicsSiteDataManager::OnBrowsingTopicsApiUsed(
+    const browsing_topics::HashedHost& hashed_top_host,
+    const base::flat_set<browsing_topics::HashedDomain>&
+        hashed_context_domains) {
+  manager_impl_->OnBrowsingTopicsApiUsed(hashed_top_host,
+                                         hashed_context_domains);
+}
+
+void TesterBrowsingTopicsSiteDataManager::GetBrowsingTopicsApiUsage(
+    base::Time begin_time,
+    base::Time end_time,
+    GetBrowsingTopicsApiUsageCallback callback) {
+  if (!query_failure_override_) {
+    manager_impl_->GetBrowsingTopicsApiUsage(begin_time, end_time,
+                                             std::move(callback));
+    return;
+  }
+
+  std::move(callback).Run(browsing_topics::ApiUsageContextQueryResult());
+}
+
+}  // namespace content
diff --git a/content/public/test/browsing_topics_test_util.h b/content/public/test/browsing_topics_test_util.h
new file mode 100644
index 0000000..eab0e193
--- /dev/null
+++ b/content/public/test/browsing_topics_test_util.h
@@ -0,0 +1,60 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_TEST_BROWSING_TOPICS_TEST_UTIL_H_
+#define CONTENT_PUBLIC_TEST_BROWSING_TOPICS_TEST_UTIL_H_
+
+#include "base/files/file_path.h"
+#include "content/public/browser/browsing_topics_site_data_manager.h"
+
+namespace content {
+
+class BrowsingTopicsSiteDataManagerImpl;
+
+// A tester class that allows mocking a query failure (e.g. database error).
+class TesterBrowsingTopicsSiteDataManager
+    : public BrowsingTopicsSiteDataManager {
+ public:
+  explicit TesterBrowsingTopicsSiteDataManager(
+      const base::FilePath& user_data_directory);
+
+  ~TesterBrowsingTopicsSiteDataManager() override;
+
+  TesterBrowsingTopicsSiteDataManager(
+      const TesterBrowsingTopicsSiteDataManager&) = delete;
+  TesterBrowsingTopicsSiteDataManager& operator=(
+      const TesterBrowsingTopicsSiteDataManager&) = delete;
+  TesterBrowsingTopicsSiteDataManager(TesterBrowsingTopicsSiteDataManager&&) =
+      delete;
+  TesterBrowsingTopicsSiteDataManager& operator=(
+      TesterBrowsingTopicsSiteDataManager&&) = delete;
+
+  // Use the default handling from `BrowsingTopicsSiteDataManagerImpl`.
+  void ExpireDataBefore(base::Time time) override;
+
+  // Use the default handling from `BrowsingTopicsSiteDataManagerImpl`.
+  void OnBrowsingTopicsApiUsed(
+      const browsing_topics::HashedHost& hashed_top_host,
+      const base::flat_set<browsing_topics::HashedDomain>&
+          hashed_context_domains) override;
+
+  void SetQueryFailureOverride() { query_failure_override_ = true; }
+
+  // Return a default/failed `ApiUsageContextQueryResult` if
+  // `query_failure_override_` is true; otherwise, sse the default handling from
+  // `BrowsingTopicsSiteDataManagerImpl`.
+  void GetBrowsingTopicsApiUsage(
+      base::Time begin_time,
+      base::Time end_time,
+      GetBrowsingTopicsApiUsageCallback callback) override;
+
+ private:
+  std::unique_ptr<BrowsingTopicsSiteDataManagerImpl> manager_impl_;
+
+  bool query_failure_override_ = false;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_TEST_BROWSING_TOPICS_TEST_UTIL_H_
diff --git a/content/renderer/media/render_media_client.cc b/content/renderer/media/render_media_client.cc
index 9d8112c..a2c7b34 100644
--- a/content/renderer/media/render_media_client.cc
+++ b/content/renderer/media/render_media_client.cc
@@ -30,10 +30,6 @@
   GetContentClient()->renderer()->GetSupportedKeySystems(std::move(cb));
 }
 
-bool RenderMediaClient::IsKeySystemsUpdateNeeded() {
-  return GetContentClient()->renderer()->IsKeySystemsUpdateNeeded();
-}
-
 bool RenderMediaClient::IsSupportedAudioType(const media::AudioType& type) {
   return GetContentClient()->renderer()->IsSupportedAudioType(type);
 }
diff --git a/content/renderer/media/render_media_client.h b/content/renderer/media/render_media_client.h
index 6997146..189f7344 100644
--- a/content/renderer/media/render_media_client.h
+++ b/content/renderer/media/render_media_client.h
@@ -23,7 +23,6 @@
 
   // MediaClient implementation.
   void GetSupportedKeySystems(media::GetSupportedKeySystemsCB cb) final;
-  bool IsKeySystemsUpdateNeeded() final;
   bool IsSupportedAudioType(const media::AudioType& type) final;
   bool IsSupportedVideoType(const media::VideoType& type) final;
   bool IsSupportedBitstreamAudioCodec(media::AudioCodec codec) final;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index e4db74c..47dfca7 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -852,26 +852,6 @@
   return cloned_token;
 }
 
-// Whether the navigation should be treated as a "loadDataWithBaseURL"
-// navigation, where the "document URL" is set to the supplied base URL instead
-// of the data URL set in CommonNavigationParams' `url` and the "unreachable
-// URL" is set to the supplied history URL. If this returns false, the data:
-// URL will still be loaded, but we won't try to use the supplied base URL and
-// history URL.
-// This should be kept in sync with NavigationRequest::IsLoadDataWithBaseURL().
-bool ShouldLoadDataWithBaseURL(
-    bool is_main_frame,
-    const blink::mojom::CommonNavigationParams& common_params,
-    const blink::mojom::CommitNavigationParams& commit_params) {
-  if (!is_main_frame)
-    return false;
-  // If no base URL is supplied, we should treat this as a normal data: URL
-  // navigation.
-  bool should_load_data_url = !common_params.base_url_for_data_url.is_empty();
-  DCHECK(!should_load_data_url || common_params.url.SchemeIs(url::kDataScheme));
-  return should_load_data_url;
-}
-
 // Creates a fully functional DocumentState in the case where we do not have
 // navigation parameters available.
 std::unique_ptr<DocumentState> BuildDocumentState() {
@@ -890,8 +870,7 @@
     mojom::NavigationClient::CommitNavigationCallback commit_callback,
     std::unique_ptr<NavigationClient> navigation_client,
     int request_id,
-    bool was_initiated_in_this_frame,
-    bool is_main_frame) {
+    bool was_initiated_in_this_frame) {
   std::unique_ptr<DocumentState> document_state(new DocumentState());
 
   DCHECK(!common_params.navigation_start.is_null());
@@ -907,10 +886,9 @@
   // If this is a loadDataWithBaseURL request, save the commit URL so that we
   // can send a DidCommit message with the URL that was originally sent by the
   // browser in CommonNavigationParams (See MaybeGetOverriddenURL()).
-  bool load_data =
-      ShouldLoadDataWithBaseURL(is_main_frame, common_params, commit_params);
-  document_state->set_was_load_data_with_base_url_request(load_data);
-  if (load_data)
+  document_state->set_was_load_data_with_base_url_request(
+      commit_params.is_load_data_with_base_url);
+  if (commit_params.is_load_data_with_base_url)
     document_state->set_data_url(common_params.url);
 
   document_state->set_navigation_state(NavigationState::Create(
@@ -2680,7 +2658,7 @@
   std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams(
       *common_params, *commit_params, std::move(commit_callback),
       std::move(navigation_client_impl_), request_id,
-      was_initiated_in_this_frame, is_main_frame_);
+      was_initiated_in_this_frame);
 
   // Check if the navigation being committed originated as a client redirect.
   bool is_client_redirect =
@@ -2716,8 +2694,7 @@
 #endif
 
   if (should_handle_data_url_as_string ||
-      ShouldLoadDataWithBaseURL(is_main_frame_, *common_params,
-                                *commit_params)) {
+      commit_params->is_load_data_with_base_url) {
     std::string mime_type, charset, data;
     // `base_url` will be set to `base_url_for_data_url` from `common_params`,
     // unless it's empty (the `data_url_as_string` handling case), in which
@@ -3059,7 +3036,7 @@
       *common_params, *commit_params, std::move(callback),
       std::move(navigation_client_impl_),
       blink::WebResourceRequestSender::MakeRequestID(),
-      false /* was_initiated_in_this_frame */, is_main_frame_);
+      false /* was_initiated_in_this_frame */);
 
   DCHECK(!pending_loader_factories_);
   pending_loader_factories_ = std::move(new_loader_factories);
@@ -5911,7 +5888,7 @@
 // - For loadDataWithBaseURL() navigations, it will return the data: URL
 // used to commit, instead of the "base URL" used as the document URL in the
 // DocumentLoader. See comments in  BuildDocumentStateFromParams() and
-// ShouldLoadDataWithBaseURL() for more details.
+// in navigation_params.mojom's `is_load_data_with_base_url` for more details.
 GURL RenderFrameImpl::GetLoadingUrl() const {
   WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
 
diff --git a/content/shell/browser/shell_download_manager_delegate.cc b/content/shell/browser/shell_download_manager_delegate.cc
index 2856779..25f8bb3 100644
--- a/content/shell/browser/shell_download_manager_delegate.cc
+++ b/content/shell/browser/shell_download_manager_delegate.cc
@@ -79,7 +79,8 @@
         download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
         download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
         download::DownloadItem::MixedContentStatus::UNKNOWN,
-        download->GetForcedFilePath(), absl::nullopt /*download_schedule*/,
+        download->GetForcedFilePath(), base::FilePath(),
+        absl::nullopt /*download_schedule*/,
         download::DOWNLOAD_INTERRUPT_REASON_NONE);
     return true;
   }
@@ -146,7 +147,7 @@
         download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
         download::DownloadItem::MixedContentStatus::UNKNOWN,
         suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")),
-        absl::nullopt /*download_schedule*/,
+        base::FilePath(), absl::nullopt /*download_schedule*/,
         download::DOWNLOAD_INTERRUPT_REASON_NONE);
     return;
   }
@@ -194,12 +195,12 @@
   NOTIMPLEMENTED();
 #endif
 
-  std::move(callback).Run(result,
-                          download::DownloadItem::TARGET_DISPOSITION_PROMPT,
-                          download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-                          download::DownloadItem::MixedContentStatus::UNKNOWN,
-                          result, absl::nullopt /*download_schedule*/,
-                          download::DOWNLOAD_INTERRUPT_REASON_NONE);
+  std::move(callback).Run(
+      result, download::DownloadItem::TARGET_DISPOSITION_PROMPT,
+      download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      download::DownloadItem::MixedContentStatus::UNKNOWN, result,
+      base::FilePath(), absl::nullopt /*download_schedule*/,
+      download::DOWNLOAD_INTERRUPT_REASON_NONE);
 }
 
 void ShellDownloadManagerDelegate::SetDownloadBehaviorForTesting(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 0fba6292..cc5047c 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -130,6 +130,8 @@
     "../public/test/browser_test_utils.h",
     "../public/test/browsing_data_remover_test_util.cc",
     "../public/test/browsing_data_remover_test_util.h",
+    "../public/test/browsing_topics_test_util.cc",
+    "../public/test/browsing_topics_test_util.h",
     "../public/test/commit_message_delayer.cc",
     "../public/test/commit_message_delayer.h",
     "../public/test/content_mock_cert_verifier.cc",
@@ -1288,6 +1290,7 @@
     "../browser/service_worker/service_worker_clients_api_browsertest.cc",
     "../browser/service_worker/service_worker_fetch_dispatcher_browsertest.cc",
     "../browser/service_worker/service_worker_file_upload_browsertest.cc",
+    "../browser/service_worker/service_worker_internals_ui_browsertest.cc",
     "../browser/service_worker/service_worker_no_best_effort_tasks_browsertest.cc",
     "../browser/service_worker/service_worker_offline_capability_check_browsertest.cc",
     "../browser/service_worker/service_worker_process_browsertest.cc",
diff --git a/content/test/data/accessibility/html/math-expected-android-external.txt b/content/test/data/accessibility/html/math-expected-android-external.txt
index f71b758..d9d4e0f 100644
--- a/content/test/data/accessibility/html/math-expected-android-external.txt
+++ b/content/test/data/accessibility/html/math-expected-android-external.txt
@@ -1,3 +1,13 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
-++++View text:"𝐴2 + 𝐵2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="math", roleDescription="math"]
\ No newline at end of file
+++++View actions:[AX_FOCUS] bundle:[chromeRole="math", roleDescription="math"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLRow"]
+++++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLSup"]
+++++++++++View text:"𝐴" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLIdentifier"]
+++++++++++View text:"2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLNumber"]
+++++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++++View text:"+" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLOperator"]
+++++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++++View actions:[AX_FOCUS] bundle:[chromeRole="mathMLSup"]
+++++++++++View text:"𝐵" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLIdentifier"]
+++++++++++View text:"2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mathMLNumber"]
\ No newline at end of file
diff --git a/content/test/data/back_forward_cache/page_with_dedicated_worker_and_importscripts.html b/content/test/data/back_forward_cache/page_with_dedicated_worker_and_importscripts.html
new file mode 100644
index 0000000..8fa30a4
--- /dev/null
+++ b/content/test/data/back_forward_cache/page_with_dedicated_worker_and_importscripts.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<script>
+const worker = new Worker('/back_forward_cache/worker_with_importscripts.js');
+window.receivedMessagePromise = new Promise(resolve => {
+  worker.addEventListener('message', (msg) => {
+    resolve(msg.data);
+  });
+});
+
+</script>
+</html>
diff --git a/content/test/data/back_forward_cache/worker_with_importscripts.js b/content/test/data/back_forward_cache/worker_with_importscripts.js
new file mode 100644
index 0000000..58bc6ba
--- /dev/null
+++ b/content/test/data/back_forward_cache/worker_with_importscripts.js
@@ -0,0 +1,4 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+self.importScripts('worker.js');
diff --git a/content/test/data/browsing_topics/v0.init_too_old.sql b/content/test/data/browsing_topics/v0.init_too_old.sql
index 6229824..1393ca290 100644
--- a/content/test/data/browsing_topics/v0.init_too_old.sql
+++ b/content/test/data/browsing_topics/v0.init_too_old.sql
@@ -4,9 +4,9 @@
 
 CREATE TABLE browsing_topics_api_usages (
 hashed_context_domain INTEGER NOT NULL,
-hashed_top_host INTEGER NOT NULL,
+hashed_main_frame_host INTEGER NOT NULL,
 last_usage_time INTEGER NOT NULL,
-PRIMARY KEY (hashed_context_domain, hashed_top_host));
+PRIMARY KEY (hashed_context_domain, hashed_main_frame_host));
 
 CREATE INDEX last_usage_time_idx ON browsing_topics_api_usages(last_usage_time);
 
diff --git a/content/test/data/browsing_topics/v1.init_too_new.sql b/content/test/data/browsing_topics/v1.init_too_new.sql
index 825043b..2284c94 100644
--- a/content/test/data/browsing_topics/v1.init_too_new.sql
+++ b/content/test/data/browsing_topics/v1.init_too_new.sql
@@ -4,9 +4,9 @@
 
 CREATE TABLE browsing_topics_api_usages (
 hashed_context_domain INTEGER NOT NULL,
-hashed_top_host INTEGER NOT NULL,
+hashed_main_frame_host INTEGER NOT NULL,
 last_usage_time INTEGER NOT NULL,
-PRIMARY KEY (hashed_context_domain, hashed_top_host));
+PRIMARY KEY (hashed_context_domain, hashed_main_frame_host));
 
 CREATE INDEX last_usage_time_idx ON browsing_topics_api_usages(last_usage_time);
 
diff --git a/content/test/data/browsing_topics/v1.sql b/content/test/data/browsing_topics/v1.sql
index 10d6373..9d5ce992c 100644
--- a/content/test/data/browsing_topics/v1.sql
+++ b/content/test/data/browsing_topics/v1.sql
@@ -4,9 +4,9 @@
 
 CREATE TABLE browsing_topics_api_usages (
 hashed_context_domain INTEGER NOT NULL,
-hashed_top_host INTEGER NOT NULL,
+hashed_main_frame_host INTEGER NOT NULL,
 last_usage_time INTEGER NOT NULL,
-PRIMARY KEY (hashed_context_domain, hashed_top_host));
+PRIMARY KEY (hashed_context_domain, hashed_main_frame_host));
 
 CREATE INDEX last_usage_time_idx ON browsing_topics_api_usages(last_usage_time);
 
diff --git a/content/test/data/gpu/vc/webgpu_video.js b/content/test/data/gpu/vc/webgpu_video.js
index 06d2ecb..1199d3cb 100644
--- a/content/test/data/gpu/vc/webgpu_video.js
+++ b/content/test/data/gpu/vc/webgpu_video.js
@@ -569,7 +569,11 @@
         passEncoder.endPass();
         device.queue.submit([commandEncoder.finish()]);
 
-        window.requestAnimationFrame(oneFrame);
+        // TODO(crbug.com/1289482): Workaround for backpressure mechanism
+        // not working properly.
+        device.queue.onSubmittedWorkDone().then(() => {
+          window.requestAnimationFrame(oneFrame);
+        });
       });
   };
 
@@ -634,7 +638,11 @@
           functionDuration, 'ms,  longer than 33.3 ms (1sec/30fps)');
     }
 
-    window.requestAnimationFrame(oneFrameWithImportTextureApi);
+    // TODO(crbug.com/1289482): Workaround for backpressure mechanism
+    // not working properly.
+    device.queue.onSubmittedWorkDone().then(() => {
+      window.requestAnimationFrame(oneFrameWithImportTextureApi);
+    });
   };
 
   if (useImportTextureApi) {
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn
index 7ceed2a..ef935a2 100644
--- a/content/test/fuzzer/BUILD.gn
+++ b/content/test/fuzzer/BUILD.gn
@@ -134,6 +134,28 @@
   environment_variables = [ "AFL_DRIVER_DONT_DEFER=1" ]
 }
 
+if (enable_mojom_fuzzer) {
+  source_set("mojolpm_fuzzer_support") {
+    testonly = true
+
+    sources = [
+      "mojolpm_fuzzer_support.cc",
+      "mojolpm_fuzzer_support.h",
+    ]
+
+    deps = [ "//mojo/core/embedder:embedder" ]
+
+    public_deps = [
+      "//base/test:test_support",
+      "//content/browser:for_content_tests",
+      "//content/test:test_support",
+      "//services/network:test_support",
+      "//storage/browser:test_support",
+      "//third_party/icu:icudata",
+    ]
+  }
+}
+
 mojolpm_fuzzer_test("code_cache_host_mojolpm_fuzzer") {
   sources = [ "code_cache_host_mojolpm_fuzzer.cc" ]
 
@@ -141,13 +163,9 @@
   testcase_proto_kind = "content.fuzzing.code_cache_host.proto.Testcase"
 
   deps = [
-    "//base/test:test_support",
+    ":mojolpm_fuzzer_support",
     "//content/browser:for_content_tests",
     "//content/public/browser:browser_sources",
-    "//content/test:test_support",
-    "//services/network:test_support",
-    "//storage/browser:test_support",
-    "//third_party/icu:icudata",
   ]
 
   proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ]
@@ -164,15 +182,9 @@
   proto_source = "file_system_manager_mojolpm_fuzzer.proto"
 
   deps = [
-    "//base/test:test_support",
-    "//content/browser:for_content_tests",
-    "//content/public/browser:browser_sources",
-    "//content/test:test_support",
-    "//services/network:test_support",
-    "//storage/browser:test_support",
+    ":mojolpm_fuzzer_support",
     "//testing/libfuzzer/proto:url_proto_converter",
     "//third_party/blink/public/common:storage_key_proto_converter",
-    "//third_party/icu:icudata",
   ]
 
   proto_deps = [
@@ -187,11 +199,9 @@
   proto_source = "audio_context_manager_mojolpm_fuzzer.proto"
 
   deps = [
-    "//base/test:test_support",
+    ":mojolpm_fuzzer_support",
     "//content/browser:for_content_tests",
-    "//content/test:test_support",
-    "//services/network:test_support",
-    "//storage/browser:test_support",
+    "//content/public/browser:browser_sources",
   ]
 
   proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ]
@@ -272,13 +282,10 @@
   proto_source = "media_stream_dispatcher_host_mojolpm_fuzzer.proto"
 
   deps = [
-    "//base/test:test_support",
+    ":mojolpm_fuzzer_support",
     "//content/browser:browser",
     "//content/browser:for_content_tests",
     "//content/public/browser:browser_sources",
-    "//content/test:test_support",
-    "//services/network:test_support",
-    "//storage/browser:test_support",
   ]
 
   proto_deps = [ "//third_party/blink/public/mojom:mojom_platform_mojolpm" ]
@@ -293,18 +300,14 @@
     proto_source = "video_capture_host_mojolpm_fuzzer.proto"
 
     deps = [
-      "//base/test:test_support",
+      ":mojolpm_fuzzer_support",
       "//content/browser:for_content_tests",
-      "//content/public/browser:browser",
+      "//content/public/browser:browser_sources",
       "//content/test:content_unittests",
-      "//content/test:test_support",
       "//media:test_support",
       "//media/capture:capture_lib",
       "//media/capture:test_support",
-      "//services/network:test_support",
-      "//storage/browser:test_support",
       "//third_party/blink/public/common:common",
-      "//third_party/icu:icudata",
     ]
 
     proto_deps = [ "//media/capture/mojom:video_capture_mojolpm" ]
@@ -318,13 +321,8 @@
 
   deps = [
     ":controller_presentation_service_delegate_for_fuzzing",
-    "//base/test:test_support",
+    ":mojolpm_fuzzer_support",
     "//content/browser:for_content_tests",
-    "//content/public/browser:browser",
-    "//content/test:content_unittests",
-    "//content/test:test_support",
-    "//services/network:test_support",
-    "//storage/browser:test_support",
     "//ui/events/devices:devices",
   ]
 
diff --git a/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc b/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc
index ba1597d..5322cfd6 100644
--- a/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.cc
@@ -6,38 +6,23 @@
 #include <cstddef>
 #include <utility>
 
-#include "base/at_exit.h"
-#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
-#include "base/i18n/icu_util.h"
 #include "base/no_destructor.h"
-#include "base/run_loop.h"
 #include "base/test/simple_test_tick_clock.h"
-#include "base/test/test_switches.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread.h"
 #include "content/browser/media/webaudio/audio_context_manager_impl.h"  // [nogncheck]
-#include "content/browser/network_service_instance_impl.h"  // nogncheck
 #include "content/browser/site_instance_impl.h"             // nogncheck
 #include "content/public/browser/audio_service.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/test/browser_task_environment.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_content_client_initializer.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/test/fuzzer/audio_context_manager_mojolpm_fuzzer.pb.h"
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_web_contents.h"
-#include "mojo/core/embedder/embedder.h"
-#include "mojo/public/cpp/bindings/lib/validation_errors.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -48,32 +33,11 @@
 #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
 #include "url/origin.h"
 
-const char* const cmdline[] = {"audio_context_manager_mojolpm_fuzzer", nullptr};
-class ContentFuzzerEnvironment {
- public:
-  ContentFuzzerEnvironment()
-      : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) {
-    TestTimeouts::Initialize();
-    logging::SetMinLogLevel(logging::LOG_FATAL);
-    mojo::core::Init();
-    base::i18n::InitializeICU();
-    fuzzer_thread_.StartAndWaitForTesting();
+const char* kCmdline[] = {"audio_context_manager_mojolpm_fuzzer", nullptr};
 
-    content::ForceCreateNetworkServiceDirectlyForTesting();
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
-    return fuzzer_thread_.task_runner();
-  }
-
- private:
-  base::AtExitManager at_exit_manager_;
-  base::Thread fuzzer_thread_;
-  content::TestContentClientInitializer content_client_initializer_;
-};
-
-ContentFuzzerEnvironment& GetEnvironment() {
-  static base::NoDestructor<ContentFuzzerEnvironment> environment;
+content::mojolpm::FuzzerEnvironment& GetEnvironment() {
+  static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment(
+      1, kCmdline);
   return *environment;
 }
 
diff --git a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
index 7a07f737..0e3f21d 100644
--- a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
@@ -6,32 +6,22 @@
 
 #include <utility>
 
-#include "base/at_exit.h"
-#include "base/base_switches.h"
-#include "base/command_line.h"
-#include "base/files/scoped_temp_dir.h"
 #include "base/i18n/icu_util.h"
 #include "base/no_destructor.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/test/test_switches.h"
-#include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"  // [nogncheck]
 #include "content/browser/cache_storage/cache_storage_control_wrapper.h"  // [nogncheck]
 #include "content/browser/code_cache/generated_code_cache_context.h"  // [nogncheck]
-#include "content/browser/network_service_instance_impl.h"       // [nogncheck]
 #include "content/browser/renderer_host/code_cache_host_impl.h"  // [nogncheck]
 #include "content/browser/storage_partition_impl.h"              // [nogncheck]
 #include "content/browser/storage_partition_impl_map.h"          // [nogncheck]
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_content_client_initializer.h"
 #include "content/test/fuzzer/code_cache_host_mojolpm_fuzzer.pb.h"
-#include "mojo/core/embedder/embedder.h"
-#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
 #include "storage/browser/quota/quota_manager.h"
 #include "storage/browser/quota/special_storage_policy.h"
 #include "storage/browser/test/mock_special_storage_policy.h"
@@ -41,59 +31,12 @@
 
 using url::Origin;
 
-const char* const kCmdline[] = {"code_cache_host_mojolpm_fuzzer", nullptr};
+const char* kCmdline[] = {"code_cache_host_mojolpm_fuzzer", nullptr};
 
-// Global environment needed to run the interface being tested.
-//
-// This will be created once, before fuzzing starts, and will be shared between
-// all testcases. It is created on the main thread.
-//
-// At a minimum, we should always be able to set up the command line, i18n and
-// mojo, and create the thread on which the fuzzer will be run. We want to avoid
-// (as much as is reasonable) any state being preserved between testcases.
-//
-// For CodeCacheHost, we can also safely re-use a single BrowserTaskEnvironment
-// and the TestContentClientInitializer between testcases. We try to create an
-// environment that matches the real browser process as much as possible, so we
-// use real platform threads in the task environment.
-class ContentFuzzerEnvironment {
- public:
-  ContentFuzzerEnvironment()
-      : fuzzer_thread_("fuzzer_thread"),
-        task_environment_(
-            (base::CommandLine::Init(1, kCmdline),
-             TestTimeouts::Initialize(),
-             base::test::TaskEnvironment::MainThreadType::DEFAULT),
-            base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC,
-            base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS,
-            content::BrowserTaskEnvironment::REAL_IO_THREAD) {
-    logging::SetMinLogLevel(logging::LOG_FATAL);
-    mojo::core::Init();
-    base::i18n::InitializeICU();
-
-    content::ForceCreateNetworkServiceDirectlyForTesting();
-    content::StoragePartitionImpl::ForceInProcessStorageServiceForTesting();
-
-    fuzzer_thread_.StartAndWaitForTesting();
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
-    return fuzzer_thread_.task_runner();
-  }
-
- private:
-  base::AtExitManager at_exit_manager_;
-  base::Thread fuzzer_thread_;
-  content::BrowserTaskEnvironment task_environment_;
-  content::TestContentClientInitializer content_client_initializer_;
-  mojo::internal::ScopedSuppressValidationErrorLoggingForTests
-      validation_error_suppressor_;
-  mojo::internal::SerializationWarningObserverForTesting
-      serialization_error_suppressor_;
-};
-
-ContentFuzzerEnvironment& GetEnvironment() {
-  static base::NoDestructor<ContentFuzzerEnvironment> environment;
+content::mojolpm::FuzzerEnvironment& GetEnvironment() {
+  static base::NoDestructor<
+      content::mojolpm::FuzzerEnvironmentWithTaskEnvironment>
+      environment(1, kCmdline);
   return *environment;
 }
 
diff --git a/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc b/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc
index deccede..3338e86 100644
--- a/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/file_system_manager_mojolpm_fuzzer.cc
@@ -24,6 +24,7 @@
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_content_client_initializer.h"
 #include "content/test/fuzzer/file_system_manager_mojolpm_fuzzer.pb.h"
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
 #include "mojo/core/embedder/embedder.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/quota/special_storage_policy.h"
@@ -43,52 +44,11 @@
 namespace content {
 
 const size_t kNumRenderers = 2;
-const char* cmdline[] = {"file_system_manager_mojolpm_fuzzer", nullptr};
+const char* kCmdline[] = {"file_system_manager_mojolpm_fuzzer", nullptr};
 
-// Global environment needed to run the interface being tested.
-//
-// This will be created once, before fuzzing starts, and will be shared between
-// all testcases. It is created on the main thread.
-//
-// At a minimum, we should always be able to set up the command line, i18n and
-// mojo, and create the thread on which the fuzzer will be run. We want to avoid
-// (as much as is reasonable) any state being preserved between testcases.
-//
-// For FileSystemManager, we can also safely re-use a single
-// BrowserTaskEnvironment and the TestContentClientInitializer between
-// testcases. We try to create an environment that matches the real browser
-// process as much as possible, so we use real platform threads in the task
-// environment.
-class ContentFuzzerEnvironment {
- public:
-  ContentFuzzerEnvironment()
-      : fuzzer_thread_("fuzzer_thread"),
-        task_environment_(
-            (base::CommandLine::Init(1, cmdline),
-             TestTimeouts::Initialize(),
-             base::test::TaskEnvironment::MainThreadType::DEFAULT),
-            base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC,
-            base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS,
-            content::BrowserTaskEnvironment::REAL_IO_THREAD) {
-    logging::SetMinLogLevel(logging::LOG_FATAL);
-    mojo::core::Init();
-    base::i18n::InitializeICU();
-    fuzzer_thread_.StartAndWaitForTesting();
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
-    return fuzzer_thread_.task_runner();
-  }
-
- private:
-  base::AtExitManager at_exit_manager_;
-  base::Thread fuzzer_thread_;
-  content::BrowserTaskEnvironment task_environment_;
-  content::TestContentClientInitializer content_client_initializer_;
-};
-
-ContentFuzzerEnvironment& GetEnvironment() {
-  static base::NoDestructor<ContentFuzzerEnvironment> environment;
+mojolpm::FuzzerEnvironment& GetEnvironment() {
+  static base::NoDestructor<mojolpm::FuzzerEnvironmentWithTaskEnvironment>
+      environment(1, kCmdline);
   return *environment;
 }
 
@@ -314,12 +274,12 @@
           break;
 
         case Action::kFileSystemManagerRemoteAction:
-          mojolpm::HandleRemoteAction(
+          ::mojolpm::HandleRemoteAction(
               action.file_system_manager_remote_action());
           break;
 
         case Action::kFileSystemCancellableOperationRemoteAction:
-          mojolpm::HandleRemoteAction(
+          ::mojolpm::HandleRemoteAction(
               action.file_system_cancellable_operation_remote_action());
           break;
 
@@ -363,7 +323,7 @@
       run_loop.QuitClosure());
   run_loop.Run();
 
-  mojolpm::GetContext()->AddInstance(id, std::move(remote));
+  ::mojolpm::GetContext()->AddInstance(id, std::move(remote));
 }
 }  // namespace content
 
@@ -391,7 +351,7 @@
 
   testcase->SetUp();
 
-  mojolpm::GetContext()->StartTestcase();
+  ::mojolpm::GetContext()->StartTestcase();
 
   base::RunLoop fuzzer_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
   content::GetFuzzerTaskRunner()->PostTask(
@@ -399,7 +359,7 @@
                                 fuzzer_run_loop.QuitClosure()));
   fuzzer_run_loop.Run();
 
-  mojolpm::GetContext()->EndTestcase();
+  ::mojolpm::GetContext()->EndTestcase();
 
   testcase->TearDown();
 }
diff --git a/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc b/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc
index 055f1d04..9339854 100644
--- a/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.cc
@@ -5,35 +5,22 @@
 #include <stdint.h>
 #include <utility>
 
-#include "base/at_exit.h"
-#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
-#include "base/i18n/icu_util.h"
 #include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/test/simple_test_tick_clock.h"
-#include "base/test/test_switches.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread.h"
-#include "content/browser/network_service_instance_impl.h"
 #include "content/public/browser/audio_service.h"
-#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/test/browser_task_environment.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_content_client_initializer.h"
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_web_contents.h"
 #include "media/audio/audio_manager.h"
 #include "media/audio/audio_system.h"
-#include "mojo/core/embedder/embedder.h"
-#include "mojo/public/cpp/bindings/lib/validation_errors.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -46,39 +33,17 @@
 
 #include "content/test/fuzzer/media_stream_dispatcher_host_mojolpm_fuzzer.pb.h"
 
-#include "mojo/core/embedder/embedder.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-mojolpm.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 
 #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
 
-const char* const cmdline[] = {"media_stream_dispatcher_host_mojolpm_fuzzer",
-                               nullptr};
-class ContentFuzzerEnvironment {
- public:
-  ContentFuzzerEnvironment()
-      : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) {
-    TestTimeouts::Initialize();
-    logging::SetMinLogLevel(logging::LOG_FATAL);
-    mojo::core::Init();
-    base::i18n::InitializeICU();
-    fuzzer_thread_.StartAndWaitForTesting();
+const char* kCmdline[] = {"media_stream_dispatcher_host_mojolpm_fuzzer",
+                          nullptr};
 
-    content::ForceCreateNetworkServiceDirectlyForTesting();
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
-    return fuzzer_thread_.task_runner();
-  }
-
- private:
-  base::AtExitManager at_exit_manager_;
-  base::Thread fuzzer_thread_;
-  content::TestContentClientInitializer content_client_initializer_;
-};
-
-ContentFuzzerEnvironment& GetEnvironment() {
-  static base::NoDestructor<ContentFuzzerEnvironment> environment;
+content::mojolpm::FuzzerEnvironment& GetEnvironment() {
+  static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment(
+      1, kCmdline);
   return *environment;
 }
 
diff --git a/content/test/fuzzer/mojolpm_fuzzer_support.cc b/content/test/fuzzer/mojolpm_fuzzer_support.cc
new file mode 100644
index 0000000..fcdc4ea
--- /dev/null
+++ b/content/test/fuzzer/mojolpm_fuzzer_support.cc
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
+
+#include "base/command_line.h"
+#include "base/i18n/icu_util.h"
+#include "base/test/test_timeouts.h"
+#include "content/browser/network_service_instance_impl.h"  // [nogncheck]
+#include "content/browser/storage_partition_impl.h"         // [nogncheck]
+#include "content/browser/storage_partition_impl_map.h"     // [nogncheck]
+
+namespace content {
+namespace mojolpm {
+FuzzerEnvironment::FuzzerEnvironment(int argc, const char** argv)
+    : command_line_initialized_(base::CommandLine::Init(argc, argv)),
+      fuzzer_thread_("fuzzer_thread") {
+  TestTimeouts::Initialize();
+
+  logging::SetMinLogLevel(logging::LOG_FATAL);
+  mojo::core::Init();
+  base::i18n::InitializeICU();
+
+  ForceCreateNetworkServiceDirectlyForTesting();
+  StoragePartitionImpl::ForceInProcessStorageServiceForTesting();
+
+  fuzzer_thread_.StartAndWaitForTesting();
+}
+
+FuzzerEnvironment::~FuzzerEnvironment() {}
+
+FuzzerEnvironmentWithTaskEnvironment::FuzzerEnvironmentWithTaskEnvironment(
+    int argc,
+    const char** argv)
+    : FuzzerEnvironment(argc, argv),
+      task_environment_(
+          base::test::TaskEnvironment::MainThreadType::DEFAULT,
+          base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC,
+          base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS,
+          BrowserTaskEnvironment::REAL_IO_THREAD) {}
+
+FuzzerEnvironmentWithTaskEnvironment::~FuzzerEnvironmentWithTaskEnvironment() {}
+}  // namespace mojolpm
+}  // namespace content
\ No newline at end of file
diff --git a/content/test/fuzzer/mojolpm_fuzzer_support.h b/content/test/fuzzer/mojolpm_fuzzer_support.h
new file mode 100644
index 0000000..9e55897
--- /dev/null
+++ b/content/test/fuzzer/mojolpm_fuzzer_support.h
@@ -0,0 +1,67 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_TEST_FUZZER_MOJOLPM_FUZZER_SUPPORT_H_
+#define CONTENT_TEST_FUZZER_MOJOLPM_FUZZER_SUPPORT_H_
+
+#include "base/at_exit.h"
+#include "base/threading/thread.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_content_client_initializer.h"
+#include "mojo/core/embedder/embedder.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+namespace content {
+namespace mojolpm {
+// Global environment needed to run the interface being tested.
+//
+// This will be created once, before fuzzing starts, and will be shared between
+// all testcases. It is created on the main thread.
+//
+// This file contains several base types that divide MojoLPM fuzzer
+// implementations within //content into two kinds - those that can reuse the
+// BrowserTaskEnvironment between multiple testcases, and those that require a
+// new BrowserTaskEnvironment for every testcase (usually this happens when
+// reusing existing test harness code).
+
+// At a minimum, we should always be able to set up the command line, i18n and
+// mojo, and create the thread on which the fuzzer will be run. We want to avoid
+// (as much as is reasonable) any state being preserved between testcases.
+class FuzzerEnvironment {
+ public:
+  FuzzerEnvironment(int argc, const char** argv);
+  virtual ~FuzzerEnvironment();
+
+  inline scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
+    return fuzzer_thread_.task_runner();
+  }
+
+ private:
+  bool command_line_initialized_;
+  base::AtExitManager at_exit_manager_;
+  base::Thread fuzzer_thread_;
+
+  mojo::internal::ScopedSuppressValidationErrorLoggingForTests
+      validation_error_suppressor_;
+  mojo::internal::SerializationWarningObserverForTesting
+      serialization_error_suppressor_;
+
+  TestContentClientInitializer content_client_initializer_;
+};
+
+// If we can also safely re-use a single BrowserTaskEnvironment and the
+// TestContentClientInitializer between testcases, then prefer to use this case
+// class instead.
+class FuzzerEnvironmentWithTaskEnvironment : public FuzzerEnvironment {
+ public:
+  FuzzerEnvironmentWithTaskEnvironment(int argc, const char** argv);
+  ~FuzzerEnvironmentWithTaskEnvironment() override;
+
+ private:
+  BrowserTaskEnvironment task_environment_;
+};
+}  // namespace mojolpm
+}  // namespace content
+
+#endif  // CONTENT_TEST_FUZZER_MOJOLPM_FUZZER_SUPPORT_H_
\ No newline at end of file
diff --git a/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc b/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc
index fb2befb..5f991bb 100644
--- a/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/presentation_service_mojolpm_fuzzer.cc
@@ -7,40 +7,28 @@
 #include <string>
 #include <utility>
 
-#include "base/at_exit.h"
-#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
-#include "base/i18n/icu_util.h"
 #include "base/no_destructor.h"
 #include "base/run_loop.h"
-#include "base/test/test_switches.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
 #include "base/threading/thread.h"
-#include "content/browser/gpu/gpu_data_manager_impl.h"      // nogncheck
-#include "content/browser/network_service_instance_impl.h"  // nogncheck
+#include "content/browser/gpu/gpu_data_manager_impl.h"  // nogncheck
 #include "content/browser/presentation/presentation_service_impl.h"  // nogncheck
 #include "content/browser/presentation/presentation_test_utils.h"
 #include "content/browser/site_instance_impl.h"  // nogncheck
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/presentation_request.h"
 #include "content/public/browser/presentation_service_delegate.h"
 #include "content/public/browser/site_instance.h"
-#include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_content_client_initializer.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/test/fuzzer/controller_presentation_service_delegate_for_fuzzing.h"
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
 #include "content/test/fuzzer/presentation_service_mojolpm_fuzzer.pb.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_web_contents.h"
-#include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -50,44 +38,11 @@
 #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
 #include "ui/events/devices/device_data_manager.h"
 
-const char* cmdline[] = {"presentation_service_mojolpm_fuzzer", nullptr};
+const char* kCmdline[] = {"presentation_service_mojolpm_fuzzer", nullptr};
 
-// Global environment needed to run the interface being tested.
-//
-// This will be created once, before fuzzing starts, and will be shared between
-// all testcases. It is created on the main thread.
-//
-// At a minimum, we should always be able to set up the command line, i18n and
-// mojo, and create the thread on which the fuzzer will be run. We want to avoid
-// (as much as is reasonable) any state being preserved between testcases.
-//
-// We try to create an environment that matches the real browser process as
-// much as possible, so we use real platform threads in the task environment.
-class ContentFuzzerEnvironment {
- public:
-  ContentFuzzerEnvironment()
-      : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) {
-    TestTimeouts::Initialize();
-    logging::SetMinLogLevel(logging::LOG_FATAL);
-    mojo::core::Init();
-    base::i18n::InitializeICU();
-    fuzzer_thread_.StartAndWaitForTesting();
-
-    content::ForceCreateNetworkServiceDirectlyForTesting();
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
-    return fuzzer_thread_.task_runner();
-  }
-
- private:
-  base::AtExitManager at_exit_manager_;
-  base::Thread fuzzer_thread_;
-  content::TestContentClientInitializer content_client_initializer_;
-};
-
-ContentFuzzerEnvironment& GetEnvironment() {
-  static base::NoDestructor<ContentFuzzerEnvironment> environment;
+content::mojolpm::FuzzerEnvironment& GetEnvironment() {
+  static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment(
+      1, kCmdline);
   return *environment;
 }
 
diff --git a/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc b/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc
index 61d1bea..3d02c82 100644
--- a/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/video_capture_host_mojolpm_fuzzer.cc
@@ -7,15 +7,11 @@
 #include <string>
 #include <utility>
 
-#include "base/at_exit.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
-#include "base/i18n/icu_util.h"
 #include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/task/task_traits.h"
-#include "base/test/test_timeouts.h"
 #include "base/threading/thread.h"
 #include "content/browser/renderer_host/media/fake_video_capture_provider.h"
 #include "content/browser/renderer_host/media/in_process_video_capture_provider.h"  // nogncheck
@@ -27,7 +23,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_content_client_initializer.h"
+#include "content/test/fuzzer/mojolpm_fuzzer_support.h"
 #include "content/test/fuzzer/video_capture_host_mojolpm_fuzzer.pb.h"
 #include "content/test/test_content_browser_client.h"
 #include "media/audio/audio_system_impl.h"
@@ -40,11 +36,10 @@
 #include "media/capture/video/linux/video_capture_device_factory_linux.h"
 #include "media/capture/video/video_capture_system_impl.h"
 #include "media/capture/video_capture_types.h"
-#include "mojo/core/embedder/embedder.h"
 #include "third_party/blink/public/common/mediastream/media_devices.h"
 #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
 
-const char* cmdline[] = {"video_capture_host_mojolpm_fuzzer", nullptr};
+const char* kCmdline[] = {"video_capture_host_mojolpm_fuzzer", nullptr};
 
 // Describe all the devices (as descriptors).
 const uint32_t kNumDeviceDescriptors = 4;
@@ -59,40 +54,9 @@
 
 using blink::mojom::MediaDeviceType;
 
-// Global environment needed to run the interface being tested.
-//
-// This will be created once, before fuzzing starts, and will be shared between
-// all testcases. It is created on the main thread.
-//
-// At a minimum, we should always be able to set up the command line, i18n and
-// mojo, and create the thread on which the fuzzer will be run. We want to avoid
-// (as much as is reasonable) any state being preserved between testcases.
-//
-// We try to create an environment that matches the real browser process as much
-// as possible, so we use real platform threads in the task environment.
-class ContentFuzzerEnvironment {
- public:
-  ContentFuzzerEnvironment()
-      : fuzzer_thread_((base::CommandLine::Init(1, cmdline), "fuzzer_thread")) {
-    TestTimeouts::Initialize();
-    logging::SetMinLogLevel(logging::LOG_FATAL);
-    mojo::core::Init();
-    base::i18n::InitializeICU();
-
-    fuzzer_thread_.StartAndWaitForTesting();
-  }
-
-  scoped_refptr<base::SequencedTaskRunner> fuzzer_task_runner() {
-    return fuzzer_thread_.task_runner();
-  }
-
-  base::AtExitManager at_exit_manager_;
-  base::Thread fuzzer_thread_;
-  content::TestContentClientInitializer content_client_initializer_;
-};
-
-ContentFuzzerEnvironment& GetEnvironment() {
-  static base::NoDestructor<ContentFuzzerEnvironment> environment;
+content::mojolpm::FuzzerEnvironment& GetEnvironment() {
+  static base::NoDestructor<content::mojolpm::FuzzerEnvironment> environment(
+      1, kCmdline);
   return *environment;
 }
 
diff --git a/content/test/gpu/flake_suppressor/expectations.py b/content/test/gpu/flake_suppressor/expectations.py
index d50e21f8..2eb2115 100644
--- a/content/test/gpu/flake_suppressor/expectations.py
+++ b/content/test/gpu/flake_suppressor/expectations.py
@@ -448,7 +448,7 @@
     files.append(line.split()[-1])
 
   origin_file_contents = {}
-  for f in files:
+  for f in (f for f in files if f.endswith('.txt')):
     origin_filepath = posixpath.join(origin_dir, f)
     origin_filepath_url = posixpath.join(GITILES_URL,
                                          origin_filepath) + TEXT_FORMAT_ARG
@@ -467,7 +467,8 @@
     that are available from the local checkout.
   """
   local_file_contents = {}
-  for f in os.listdir(ABSOLUTE_EXPECTATION_FILE_DIRECTORY):
+  for f in (f for f in os.listdir(ABSOLUTE_EXPECTATION_FILE_DIRECTORY)
+            if f.endswith('.txt')):
     with open(os.path.join(ABSOLUTE_EXPECTATION_FILE_DIRECTORY, f)) as infile:
       local_file_contents[f] = infile.read()
   return local_file_contents
diff --git a/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py b/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py
index db34772..429bc5e 100644
--- a/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py
+++ b/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py
@@ -127,6 +127,7 @@
   def TearDownProcess(cls):
     super(SkiaGoldIntegrationTestBase, cls).TearDownProcess()
     shutil.rmtree(cls._skia_gold_temp_dir)
+    cls._skia_gold_session_manager = None
 
   @classmethod
   def AddCommandlineArgs(cls, parser):
diff --git a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
index 26334c2..d1d193e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
@@ -79,6 +79,7 @@
 crbug.com/1206463 [ android android-nexus-5x ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
 crbug.com/1206463 [ android android-pixel-4 ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
 crbug.com/1206463 [ chromeos chromeos-board-kevin ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
+crbug.com/1206463 [ fuchsia fuchsia-board-qemu-x64 ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
 crbug.com/1206463 [ linux display-server-wayland intel ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
 crbug.com/1206463 [ linux display-server-x intel ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
 crbug.com/1206463 [ linux display-server-x nvidia ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index c4e3e4e..d7aaf8c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -231,6 +231,9 @@
 # Fails on Nexus 5
 crbug.com/883500 [ android android-nexus-5 ] Pixel_BackgroundImage [ Failure ]
 
+# Fails on Nexus 5X.
+crbug.com/1307317 [ android android-chromium android-nexus-5x ] Pixel_PrecisionRoundedCorner [ Failure ]
+
 # Flakes on Nexus 5X.
 crbug.com/883500 [ android android-chromium android-nexus-5x ] Pixel_BackgroundImage [ RetryOnFailure ]
 crbug.com/1065514 [ android android-chromium android-nexus-5x ] Pixel_WebGLReadPixelsTabSwitch [ RetryOnFailure ]
@@ -280,12 +283,14 @@
 
 # Flaky hang.
 crbug.com/1218288 [ fuchsia-board-astro ] Pixel_PrecisionRoundedCorner [ Failure ]
-crbug.com/1218288 [ fuchsia-board-sherlock ] Pixel_PrecisionRoundedCorner [ Failure ]
 crbug.com/1136875 [ fuchsia-board-qemu-x64 ] Pixel_PrecisionRoundedCorner [ RetryOnFailure ]
 
 # Still fails on Nexus 5 after all other Pixel_CanvasLowLatency* pass.
 crbug.com/1097752 [ android android-nexus-5 ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
 
+# Fails on Fuchsia emulators
+crbug.com/1302427 [ fuchsia-board-qemu-x64 ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
+
 # Produces blank images on gpu-fyi-try-chromeos-kevin, gpu-fyi-try-chromeos-amd64-generic.
 crbug.com/1086690 [ chromeos ] Pixel_WebGLSadCanvas [ Failure ]
 
@@ -321,15 +326,6 @@
 crbug.com/1268144 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_BackgroundImage [ Skip ]
 crbug.com/1268144 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_SolidColorBackground [ Skip ]
 
-crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_AA_NoAlpha [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGL2_ClearBufferfv_Result_Displayed [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGL2_ClearBufferfv_Result_Displayed [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] Pixel_WebGLGreenTriangle_NoAA_NoAlpha [ Failure ]
-
 # Failures in fuchsia-chrome browser
 crbug.com/1295076 [ fuchsia fuchsia-chrome ] Pixel_CanvasDisplaySRGBAccelerated2D [ Failure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 9fa7453..906b491 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -345,13 +345,8 @@
 ####################
 
 # Astro specific issues
-crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/context/context-no-alpha-fbo-with-alpha.html [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/context/context-attribute-preserve-drawing-buffer.html [ Failure ]
-crbug.com/1145858 [ fuchsia fuchsia-board-astro ] conformance/extensions/ext-disjoint-timer-query.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/extensions/oes-texture-half-float-with-video.html [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/rendering/color-mask-preserved-during-implicit-clears.html [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/rendering/scissor-rect-repeated-rendering.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
@@ -359,8 +354,6 @@
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/copy-tex-image-2d-formats.html [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/tex-input-validation.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
@@ -369,13 +362,10 @@
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
 # OOM on hardware devices
-crbug.com/1145951 [ fuchsia fuchsia-board-astro ] conformance/rendering/preservedrawingbuffer-leak.html [ Failure ]
 crbug.com/1146483 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-video-transparent.html [ Failure ]
 # Flaky tests
 [ fuchsia fuchsia-board-astro ] WebglExtension_EXT_float_blend [ Failure ]
 [ fuchsia fuchsia-board-sherlock ] WebglExtension_EXT_float_blend [ Failure ]
-[ fuchsia fuchsia-board-astro ] conformance/rendering/rendering-stencil-large-viewport.html [ Failure ]
-[ fuchsia fuchsia-board-astro ] conformance/canvas/drawingbuffer-static-canvas-test.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-astro ] conformance/textures/misc/texture-npot-video.html [ Failure ]
 crbug.com/1210953 [ fuchsia fuchsia-board-astro ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ RetryOnFailure ]
@@ -396,13 +386,8 @@
 # [ fuchsia ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
 
 # Sherlock failures
-crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/context/context-attribute-preserve-drawing-buffer.html [ Failure ]
-crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/context/context-no-alpha-fbo-with-alpha.html [ Failure ]
-crbug.com/1145858 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/extensions/ext-disjoint-timer-query.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/extensions/oes-texture-half-float-with-video.html [ Failure ]
-crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/rendering/color-mask-preserved-during-implicit-clears.html [ Failure ]
-crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/rendering/scissor-rect-repeated-rendering.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
@@ -410,19 +395,19 @@
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/copy-tex-image-2d-formats.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/texture-npot-video.html [ Failure ]
 crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/texture-video-transparent.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
 crbug.com/1268138 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ]
-crbug.com/1261867 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/tex-input-validation.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ]
 crbug.com/1145861 [ fuchsia fuchsia-board-sherlock skia-renderer-vulkan ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ]
 
+crbug.com/1145858 [ fuchsia ] conformance/extensions/ext-disjoint-timer-query.html [ Failure ]
+
 ####################
 # Win failures     #
 ####################
diff --git a/docs/security/document-domain.md b/docs/security/document-domain.md
index 4f9d340..2046870 100644
--- a/docs/security/document-domain.md
+++ b/docs/security/document-domain.md
@@ -58,6 +58,19 @@
 current default or have not implemented the `Origin-Agent-Cluster` header at
 all.
 
+## Where are the deprecation warnings found?
+
+The deprecation warnings are found in the [issues tab](https://developer.chrome.com/docs/devtools/issues/).
+
+## What does the deprecation warning tell me?
+
+There are two deprecation warnings: One for setting the `document.domain`
+accessors, which modifies the security behaviour. And from M101 on,
+a second warning when a cross-domain access is made that is facilitated by
+the modified `document.domain` property. The first warning tells you where
+the setup happens, and the second one tells you where it is being used (and
+thus likely why this is being done in the first place).
+
 ## How Can I Test This?
 
 In the DevTools console, for a page `www.example.test`:
@@ -78,7 +91,7 @@
 
 How to enable/disable the deprecation:
 
-### Enable the Warning (Scheduled for M100)
+### Enable the Warning (Before M100)
 
 * Start Chrome with `--enable-features=OriginAgentClusterDefaultWarning`
 
diff --git a/docs/security/web-mitigation-metrics.md b/docs/security/web-mitigation-metrics.md
index cabfde43..00d30d68 100644
--- a/docs/security/web-mitigation-metrics.md
+++ b/docs/security/web-mitigation-metrics.md
@@ -181,11 +181,12 @@
 * Sanitizer creation: `kSanitizerAPICreated` and
   `kSanitizerAPIDefaultConfiguration` tell us how many Sanitizers are
   created and how many Sanitizers are created without custom configurations.
-* Sanitizing process: `kSanitizerAPIToString` and
-  `kSanitizerAPIToFragment` counts the usage of two methods,
-  `Sanitizer::sanitizeToString` and `Sanitizer::sanitize`.
-* `kSanitizerAPIActionTaken` shows how many times do the
-  actual sanitize action has been performed while calling the Sanitizer APIs.
+* Sanitizer method: `kSanitizerAPIToFragment`, `kSanitizerAPISanitizeFor`,
+  and `kSanitizerAPIElementSetSanitized` measure which API entry point has been
+  called.
+* `kSanitizerAPIActionTaken` shows how many times a sanitize action has been
+  performed while calling the Sanitizer APIs. (That is, on how many sanitizer
+  calls did the sanitizer remove nodes from the input sets.)
 * Input type: `kSanitizerAPIFromString`, `kSanitizerAPIFromDocument` and
   `kSanitizerAPIFromFragment` tell us what kind of input people are using.
 
diff --git a/docs/updater/design_doc.md b/docs/updater/design_doc.md
index 93b4e40..908396c 100644
--- a/docs/updater/design_doc.md
+++ b/docs/updater/design_doc.md
@@ -8,3 +8,157 @@
 The objective is to create an updater for desktop client software using Chromium
 code and tools.
 
+## Dynamic Install Parameters
+
+### `installdataindex`
+
+`installdataindex` is one of the install parameters that can be specified for
+first installs on the command line or via the
+[metainstaller tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tools/tag.py).
+
+For example, here is a typical command line for the Updater on Windows:
+```
+UpdaterSetup.exe /install "appguid=YourAppID&appname=YourAppName&needsadmin=False&lang=en&installdataindex =verboselog"
+```
+
+In this case, the updater client sends the `installdataindex` of `verboselog` to
+the update server.
+
+This involves:
+* [Parsing the tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tag.h)
+into individual tag components, including install_data_index.
+* Creating a CrxComponent with the install_data_index and providing it to the
+[update_client](https://source.chromium.org/chromium/chromium/src/+/main:components/update_client/update_client.h).
+* update_client sends the install_data_index to the update server.
+
+This is how a [JSON](https://www.json.org/) request from update_client may look
+like:
+
+```
+{
+   "request":{
+      "@os":"win",
+      "@updater":"updater",
+      "acceptformat":"crx3",
+      "app":[
+         {
+            "appid":"YourAppID",
+            "data":[
+               {
+                  "index":"verboselog",
+                  "name":"install"
+               }
+            ],
+            "enabled":true,
+            "installsource":"ondemand",
+            "ping":{
+               "r":-2
+            },
+            "updatecheck":{
+               "sameversionupdate":true
+            },
+            "version":"0.1"
+         }
+      ],
+      "arch":"x86",
+      "dedup":"cr",
+      "domainjoined":true,
+      "hw":{
+         "avx":true,
+         "physmemory":32,
+         "sse":true,
+         "sse2":true,
+         "sse3":true,
+         "sse41":true,
+         "sse42":true,
+         "ssse3":true
+      },
+      "ismachine":false,
+      "lang":"en-US",
+      "nacl_arch":"x86-64",
+      "os":{
+         "arch":"x86_64",
+         "platform":"Windows",
+         "version":"10.0.19042.1586"
+      },
+      "prodversion":"101.0.4949.0",
+      "protocol":"3.1",
+      "requestid":"{6b417770-1f68-4d52-8843-356760c84d33}",
+      "sessionid":"{37775211-4487-48d5-845d-35a1d71b03bc}",
+      "updaterversion":"101.0.4949.0",
+      "wow64":true
+   }
+}
+```
+
+The server retrieves the data corresponding to `installdataindex=verboselog`
+and returns it back to update_client.
+
+This is how a JSON response from the update server may look like:
+
+```
+  "response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "data":[{
+      "status":"ok",
+      "name":"install",
+      "index":"verboselog",
+      "#text":"{\"logging\":{\"verbose\":true}}"
+     }],
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"},
+                    {"codebasediff":"http://diff.example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "run":"UpdaterSetup.exe",
+      "arguments":"--arg1 --arg2",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }
+```
+
+update_client provides this response data back to the Updater.
+
+The updater client writes this data to a temporary file in the same directory as
+the application installer. This is for security reasons, since writing the data
+to the temp directory could potentially allow a man-in-the-middle attack.
+
+The updater client provides the temporary file as a parameter to the application
+installer.
+
+Let's say, as shown above, that the update server responds with these example
+file contents:
+```
+{"logging":{"verbose":true}}
+```
+
+The updater client will now create a temporary file, say `c:\my
+path\temporaryfile.dat` (assuming the application installer is running from
+`c:\my path\YesExe.exe`), with the following file contents:
+```
+\xEF\xBB\xBF{"logging":{"verbose":true}}
+```
+
+and then provide the file as a parameter to the application installer:
+```
+"c:\my path\YesExe.exe" --installerdata="c:\my path\temporaryfile.dat"
+```
+
+* Notice above that the temp file contents are prefixed with an UTF-8 Byte Order
+Mark of `EF BB BF`.
+* For MSI installers, a property will passed to the installer:
+`INSTALLERDATA="pathtofile"`.
+* For exe-based installers, as shown above, a command line parameter will be
+passed to the installer: `--installerdata="pathtofile"`.
+* For Mac installers, an environment variable will be set:
+`INSTALLERDATA="pathtofile"`.
+* Ownership of the temp file is the responsibility of the application installer.
+The updater will not delete this file.
+* This installerdata is not persisted anywhere else, and it is not sent as a
+part of pings to the update server.
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md
index 0e75a3a..022b470 100644
--- a/docs/updater/functional_spec.md
+++ b/docs/updater/functional_spec.md
@@ -5,4 +5,148 @@
 
 [TOC]
 
-##
+## Dynamic Install Parameters
+
+### `installdataindex`
+
+`installdataindex` is one of the install parameters that can be specified for
+first installs on the command line or via the
+[metainstaller tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tools/tag.py).
+
+For example, here is a typical command line for the Updater on Windows:
+```
+UpdaterSetup.exe /install "appguid=YourAppID&appname=YourAppName&needsadmin=False&lang=en&installdataindex =verboselog"
+```
+
+In this case, the updater client sends the `installdataindex` of `verboselog` to
+the update server.
+
+This is how a [JSON](https://www.json.org/) request from the updater client may
+look like:
+
+```
+{
+   "request":{
+      "@os":"win",
+      "@updater":"updater",
+      "acceptformat":"crx3",
+      "app":[
+         {
+            "appid":"YourAppID",
+            "data":[
+               {
+                  "index":"verboselog",
+                  "name":"install"
+               }
+            ],
+            "enabled":true,
+            "installsource":"ondemand",
+            "ping":{
+               "r":-2
+            },
+            "updatecheck":{
+               "sameversionupdate":true
+            },
+            "version":"0.1"
+         }
+      ],
+      "arch":"x86",
+      "dedup":"cr",
+      "domainjoined":true,
+      "hw":{
+         "avx":true,
+         "physmemory":32,
+         "sse":true,
+         "sse2":true,
+         "sse3":true,
+         "sse41":true,
+         "sse42":true,
+         "ssse3":true
+      },
+      "ismachine":false,
+      "lang":"en-US",
+      "nacl_arch":"x86-64",
+      "os":{
+         "arch":"x86_64",
+         "platform":"Windows",
+         "version":"10.0.19042.1586"
+      },
+      "prodversion":"101.0.4949.0",
+      "protocol":"3.1",
+      "requestid":"{6b417770-1f68-4d52-8843-356760c84d33}",
+      "sessionid":"{37775211-4487-48d5-845d-35a1d71b03bc}",
+      "updaterversion":"101.0.4949.0",
+      "wow64":true
+   }
+}
+```
+
+The server retrieves the data corresponding to `installdataindex=verboselog`
+and returns it back to the updater client.
+
+This is how a JSON response from the update server may look like:
+
+```
+  "response":{
+   "protocol":"3.1",
+   "app":[
+    {"appid":"12345",
+     "data":[{
+      "status":"ok",
+      "name":"install",
+      "index":"verboselog",
+      "#text":"{\"logging\":{\"verbose\":true}}"
+     }],
+     "updatecheck":{
+     "status":"ok",
+     "urls":{"url":[{"codebase":"http://example.com/"},
+                    {"codebasediff":"http://diff.example.com/"}]},
+     "manifest":{
+      "version":"1.2.3.4",
+      "prodversionmin":"2.0.143.0",
+      "run":"UpdaterSetup.exe",
+      "arguments":"--arg1 --arg2",
+      "packages":{"package":[{"name":"extension_1_2_3_4.crx"}]}}
+     }
+    }
+   ]
+  }
+```
+
+The updater client writes this data to a temporary file in the same directory as
+the application installer. This is for security reasons, since writing the data
+to the temp directory could potentially allow a man-in-the-middle attack.
+
+The updater client provides the temporary file as a parameter to the application
+installer.
+
+Let's say, as shown above, that the update server responds with these example
+file contents:
+```
+{"logging":{"verbose":true}}
+```
+
+The updater client will now create a temporary file, say `c:\my
+path\temporaryfile.dat` (assuming the application installer is running from
+`c:\my path\YesExe.exe`), with the following file contents:
+```
+\xEF\xBB\xBF{"logging":{"verbose":true}}
+```
+
+and then provide the file as a parameter to the application installer:
+```
+"c:\my path\YesExe.exe" --installerdata="c:\my path\temporaryfile.dat"
+```
+
+* Notice above that the temp file contents are prefixed with an UTF-8 Byte Order
+Mark of `EF BB BF`.
+* For MSI installers, a property will passed to the installer:
+`INSTALLERDATA="pathtofile"`.
+* For exe-based installers, as shown above, a command line parameter will be
+passed to the installer: `--installerdata="pathtofile"`.
+* For Mac installers, an environment variable will be set:
+`INSTALLERDATA="pathtofile"`.
+* Ownership of the temp file is the responsibility of the application installer.
+The updater will not delete this file.
+* This installerdata is not persisted anywhere else, and it is not sent as a
+part of pings to the update server.
diff --git a/docs/updater/user_manual.md b/docs/updater/user_manual.md
index 5f06126..5e9f43a 100644
--- a/docs/updater/user_manual.md
+++ b/docs/updater/user_manual.md
@@ -1,7 +1,62 @@
 # Chromium Updater User Manual
 
-This is the user manual for [Chromium Updater](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/).
+This is the user manual for
+[Chromium Updater](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/).
 
 [TOC]
 
-##
+## Dynamic Install Parameters
+
+### `installdataindex`
+
+`installdataindex` is one of the install parameters that can be specified for
+first installs on the command line or via the
+[metainstaller tag](https://source.chromium.org/chromium/chromium/src/+/main:chrome/updater/tools/tag.py).
+
+For example, here is a typical command line for the Updater on Windows:
+```
+UpdaterSetup.exe /install "appguid=YourAppID&appname=YourAppName&needsadmin=False&lang=en&installdataindex =verboselog"
+```
+
+In this case, the updater client sends the `installdataindex` of `verboselog` to
+the update server.
+
+The server retrieves the data corresponding to `installdataindex=verboselog` and
+returns it back to the updater client.
+
+The updater client writes this data to a temporary file in the same directory as
+the application installer.
+
+The updater client provides the temporary file as a parameter to the application
+installer.
+
+Let's say, as shown above, that the update server responds with these example
+file contents:
+```
+{"logging":{"verbose":true}}
+```
+
+The updater client will now create a temporary file, say `c:\my
+path\temporaryfile.dat` (assuming the application installer is running from
+`c:\my path\YesExe.exe`), with the following file contents:
+```
+\xEF\xBB\xBF{"logging":{"verbose":true}}
+```
+
+and then provide the file as a parameter to the application installer:
+```
+"c:\my path\YesExe.exe" --installerdata="c:\my path\temporaryfile.dat"
+```
+
+* Notice above that the temp file contents are prefixed with an UTF-8 Byte Order
+Mark of `EF BB BF`.
+* For MSI installers, a property will passed to the installer:
+`INSTALLERDATA="pathtofile"`.
+* For exe-based installers, as shown above, a command line parameter will be
+passed to the installer: `--installerdata="pathtofile"`.
+* For Mac installers, an environment variable will be set:
+`INSTALLERDATA="pathtofile"`.
+* Ownership of the temp file is the responsibility of the application installer.
+The updater will not delete this file.
+* This installerdata is not persisted anywhere else, and it is not sent as a
+part of pings to the update server.
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 44b1d66..96de962 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -164,6 +164,7 @@
     "api/scripting/scripting_utils.h",
     "api/storage/settings_namespace.cc",
     "api/storage/settings_namespace.h",
+    "api/storage/settings_observer.h",
     "api/system_display/display_info_provider.cc",
     "api/system_display/display_info_provider.h",
     "api/system_display/system_display_api.cc",
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index 042c82b..61e9cc8 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -23,7 +23,6 @@
     "storage/local_value_store_cache.h",
     "storage/session_storage_manager.cc",
     "storage/session_storage_manager.h",
-    "storage/settings_observer.h",
     "storage/settings_storage_quota_enforcer.cc",
     "storage/settings_storage_quota_enforcer.h",
     "storage/storage_api.cc",
diff --git a/extensions/browser/api/extensions_api_client.cc b/extensions/browser/api/extensions_api_client.cc
index cf7b1ac..500006b 100644
--- a/extensions/browser/api/extensions_api_client.cc
+++ b/extensions/browser/api/extensions_api_client.cc
@@ -30,8 +30,7 @@
 void ExtensionsAPIClient::AddAdditionalValueStoreCaches(
     content::BrowserContext* context,
     const scoped_refptr<value_store::ValueStoreFactory>& factory,
-    const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>&
-        observers,
+    SettingsChangedCallback observer,
     std::map<settings_namespace::Namespace, ValueStoreCache*>* caches) {}
 
 void ExtensionsAPIClient::AttachWebContentsHelpers(
diff --git a/extensions/browser/api/extensions_api_client.h b/extensions/browser/api/extensions_api_client.h
index 20ddb020..d5f0ceb 100644
--- a/extensions/browser/api/extensions_api_client.h
+++ b/extensions/browser/api/extensions_api_client.h
@@ -16,17 +16,13 @@
 #include "extensions/browser/api/clipboard/clipboard_api.h"
 #include "extensions/browser/api/declarative_content/content_rules_registry.h"
 #include "extensions/browser/api/storage/settings_namespace.h"
+#include "extensions/browser/api/storage/settings_observer.h"
 #include "extensions/common/api/clipboard.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_id.h"
 
 class GURL;
 
-namespace base {
-template <class T>
-class ObserverListThreadSafe;
-}
-
 namespace content {
 class BrowserContext;
 class WebContents;
@@ -59,7 +55,6 @@
 class MimeHandlerViewGuestDelegate;
 class NonNativeFileSystemDelegate;
 class RulesCacheDelegate;
-class SettingsObserver;
 class SupervisedUserExtensionsDelegate;
 class ValueStoreCache;
 class VirtualKeyboardDelegate;
@@ -90,8 +85,7 @@
   virtual void AddAdditionalValueStoreCaches(
       content::BrowserContext* context,
       const scoped_refptr<value_store::ValueStoreFactory>& factory,
-      const scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>>&
-          observers,
+      SettingsChangedCallback observer,
       std::map<settings_namespace::Namespace, ValueStoreCache*>* caches);
 
   // Attaches any extra web contents helpers (like ExtensionWebContentsObserver)
diff --git a/extensions/browser/api/storage/settings_observer.h b/extensions/browser/api/storage/settings_observer.h
index 968ef0b..a4abf1fd 100644
--- a/extensions/browser/api/storage/settings_observer.h
+++ b/extensions/browser/api/storage/settings_observer.h
@@ -5,25 +5,33 @@
 #ifndef EXTENSIONS_BROWSER_API_STORAGE_SETTINGS_OBSERVER_H_
 #define EXTENSIONS_BROWSER_API_STORAGE_SETTINGS_OBSERVER_H_
 
-#include "base/observer_list_threadsafe.h"
+#include "base/callback.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/types/strong_alias.h"
 #include "base/values.h"
 
 namespace extensions {
 
 enum class StorageAreaNamespace;
 
-// Interface for classes that listen to changes to extension settings.
-class SettingsObserver {
- public:
-  // Called when a list of settings have changed for an extension.
-  virtual void OnSettingsChanged(const std::string& extension_id,
-                                 StorageAreaNamespace storage_area,
-                                 const base::Value& changes) = 0;
+using SettingsChangedCallback = base::RepeatingCallback<
+    void(const std::string&, StorageAreaNamespace, base::Value)>;
 
-  virtual ~SettingsObserver() {}
-};
+using SequenceBoundSettingsChangedCallback =
+    base::StrongAlias<class SequenceBoundSettingsChangedCallbackTag,
+                      SettingsChangedCallback>;
 
-typedef base::ObserverListThreadSafe<SettingsObserver> SettingsObserverList;
+// Returns a callback that is guaranteed to run on |task_runner|. This should be
+// used when the callback is invoked from other sequences.
+inline SequenceBoundSettingsChangedCallback
+GetSequenceBoundSettingsChangedCallback(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    SettingsChangedCallback callback,
+    const base::Location& location = FROM_HERE) {
+  return SequenceBoundSettingsChangedCallback(base::BindPostTask(
+      std::move(task_runner), std::move(callback), location));
+}
 
 }  // namespace extensions
 
diff --git a/extensions/browser/api/storage/storage_api.cc b/extensions/browser/api/storage/storage_api.cc
index 00f0f15..765fd338 100644
--- a/extensions/browser/api/storage/storage_api.cc
+++ b/extensions/browser/api/storage/storage_api.cc
@@ -167,7 +167,9 @@
                            storage_area_string.c_str())));
   }
 
-  observers_ = frontend->GetObservers();
+  observer_ = GetSequenceBoundSettingsChangedCallback(
+      base::SequencedTaskRunnerHandle::Get(), frontend->GetObserver());
+
   frontend->RunWithStorage(
       extension(), settings_namespace_,
       base::BindOnce(&SettingsFunction::AsyncRunWithStorage, this));
@@ -201,9 +203,8 @@
     return Error(result.status().message);
 
   if (!result.changes().empty()) {
-    observers_->Notify(
-        FROM_HERE, &SettingsObserver::OnSettingsChanged, extension_id(),
-        storage_area_,
+    observer_->Run(
+        extension_id(), storage_area_,
         value_store::ValueStoreChange::ToValue(result.PassChanges()));
   }
 
@@ -213,11 +214,14 @@
 void SettingsFunction::OnSessionSettingsChanged(
     std::vector<SessionStorageManager::ValueChange> changes) {
   if (!changes.empty()) {
-    scoped_refptr<SettingsObserverList> observers =
-        StorageFrontend::Get(browser_context())->GetObservers();
-    observers->Notify(FROM_HERE, &SettingsObserver::OnSettingsChanged,
-                      extension_id(), storage_area_,
-                      ValueChangeToValue(std::move(changes)));
+    SettingsChangedCallback observer =
+        StorageFrontend::Get(browser_context())->GetObserver();
+    // This used to dispatch asynchronously as a result of a
+    // ObserverListThreadSafe. Ideally, we'd just run this synchronously, but it
+    // appears at least some tests rely on the asynchronous behavior.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(observer, extension_id(), storage_area_,
+                                  ValueChangeToValue(std::move(changes))));
   }
 }
 
diff --git a/extensions/browser/api/storage/storage_api.h b/extensions/browser/api/storage/storage_api.h
index fe30fad..5e1c525 100644
--- a/extensions/browser/api/storage/storage_api.h
+++ b/extensions/browser/api/storage/storage_api.h
@@ -67,7 +67,7 @@
       settings_namespace::INVALID;
 
   // Observers, cached so that it's only grabbed from the UI thread.
-  scoped_refptr<SettingsObserverList> observers_;
+  SequenceBoundSettingsChangedCallback observer_;
 };
 
 class StorageStorageAreaGetFunction : public SettingsFunction {
diff --git a/extensions/browser/api/storage/storage_frontend.cc b/extensions/browser/api/storage/storage_frontend.cc
index 1ebd2137..a250b18 100644
--- a/extensions/browser/api/storage/storage_frontend.cc
+++ b/extensions/browser/api/storage/storage_frontend.cc
@@ -57,64 +57,6 @@
   }
 }
 
-// Settings change Observer which forwards changes on to the extension
-// processes for |context| and its incognito partner if it exists.
-class DefaultObserver : public SettingsObserver {
- public:
-  explicit DefaultObserver(BrowserContext* context)
-      : browser_context_(context) {}
-
-  // SettingsObserver implementation.
-  void OnSettingsChanged(const std::string& extension_id,
-                         StorageAreaNamespace storage_area,
-                         const base::Value& changes) override {
-    TRACE_EVENT1("browser", "SettingsObserver:OnSettingsChanged",
-                 "extension_id", extension_id);
-
-    // Alias extension_id for investigation of shutdown hangs. crbug.com/1154997
-    // Extension IDs are exactly 32 characters in length.
-    constexpr size_t kExtensionsIdLength = 32;
-    char extension_id_str[kExtensionsIdLength + 1];
-    base::strlcpy(extension_id_str, extension_id.c_str(),
-                  std::size(extension_id_str));
-    base::debug::Alias(extension_id_str);
-
-    const std::string namespace_string = StorageAreaToString(storage_area);
-    EventRouter* event_router = EventRouter::Get(browser_context_);
-
-    // We only dispatch the events if there's a valid listener (even though
-    // EventRouter would handle the no-listener case) since copying `changes`
-    // can be expensive.
-    // Event for each storage(sync, local, managed).
-    if (event_router->ExtensionHasEventListener(
-            extension_id, api::storage::OnChanged::kEventName)) {
-      std::vector<base::Value> args;
-      args.push_back(changes.Clone());
-      args.push_back(base::Value(namespace_string));
-      std::unique_ptr<Event> event(
-          new Event(events::STORAGE_ON_CHANGED,
-                    api::storage::OnChanged::kEventName, std::move(args)));
-      event_router->DispatchEventToExtension(extension_id, std::move(event));
-    }
-
-    // Event for StorageArea.
-    auto area_event_name =
-        base::StringPrintf("storage.%s.onChanged", namespace_string.c_str());
-    if (event_router->ExtensionHasEventListener(extension_id,
-                                                area_event_name)) {
-      std::vector<base::Value> args;
-      args.push_back(changes.Clone());
-      auto event =
-          std::make_unique<Event>(StorageAreaToEventHistogram(storage_area),
-                                  area_event_name, std::move(args));
-      event_router->DispatchEventToExtension(extension_id, std::move(event));
-    }
-  }
-
- private:
-  const raw_ptr<BrowserContext> browser_context_;
-};
-
 }  // namespace
 
 // static
@@ -145,25 +87,19 @@
     scoped_refptr<value_store::ValueStoreFactory> factory) {
   TRACE_EVENT0("browser,startup", "StorageFrontend::Init");
 
-  observers_ = new SettingsObserverList();
-  browser_context_observer_ =
-      std::make_unique<DefaultObserver>(browser_context_);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!browser_context_->IsOffTheRecord());
 
-  observers_->AddObserver(browser_context_observer_.get());
-
   caches_[settings_namespace::LOCAL] = new LocalValueStoreCache(factory);
 
   // Add any additional caches the embedder supports (for example, caches
   // for chrome.storage.managed and chrome.storage.sync).
   ExtensionsAPIClient::Get()->AddAdditionalValueStoreCaches(
-      browser_context_, factory, observers_, &caches_);
+      browser_context_, factory, GetObserver(), &caches_);
 }
 
 StorageFrontend::~StorageFrontend() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  observers_->RemoveObserver(browser_context_observer_.get());
   for (auto it = caches_.begin(); it != caches_.end(); ++it) {
     ValueStoreCache* cache = it->second;
     cache->ShutdownOnUI();
@@ -216,9 +152,10 @@
   }
 }
 
-scoped_refptr<SettingsObserverList> StorageFrontend::GetObservers() {
+SettingsChangedCallback StorageFrontend::GetObserver() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return observers_;
+  return base::BindRepeating(&StorageFrontend::OnSettingsChanged,
+                             weak_factory_.GetWeakPtr());
 }
 
 void StorageFrontend::DisableStorageForTesting(
@@ -232,6 +169,67 @@
   }
 }
 
+// Forwards changes on to the extension processes for |browser_context_| and its
+// incognito partner if it exists.
+void StorageFrontend::OnSettingsChanged(const std::string& extension_id,
+                                        StorageAreaNamespace storage_area,
+                                        base::Value changes) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  TRACE_EVENT1("browser", "SettingsObserver:OnSettingsChanged", "extension_id",
+               extension_id);
+
+  // Alias extension_id for investigation of shutdown hangs. crbug.com/1154997
+  // Extension IDs are exactly 32 characters in length.
+  constexpr size_t kExtensionsIdLength = 32;
+  char extension_id_str[kExtensionsIdLength + 1];
+  base::strlcpy(extension_id_str, extension_id.c_str(),
+                std::size(extension_id_str));
+  base::debug::Alias(extension_id_str);
+
+  const std::string namespace_string = StorageAreaToString(storage_area);
+  EventRouter* event_router = EventRouter::Get(browser_context_);
+
+  bool has_event_changed_listener = event_router->ExtensionHasEventListener(
+      extension_id, api::storage::OnChanged::kEventName);
+
+  // Event for StorageArea.
+  auto area_event_name =
+      base::StringPrintf("storage.%s.onChanged", namespace_string.c_str());
+  bool has_area_changed_event_listener =
+      event_router->ExtensionHasEventListener(extension_id, area_event_name);
+
+  auto make_changed_event = [&namespace_string](base::Value changes) {
+    std::vector<base::Value> args;
+    args.emplace_back(std::move(changes));
+    args.emplace_back(base::Value(namespace_string));
+    return std::make_unique<Event>(events::STORAGE_ON_CHANGED,
+                                   api::storage::OnChanged::kEventName,
+                                   std::move(args));
+  };
+  auto make_area_changed_event = [&storage_area,
+                                  &area_event_name](base::Value changes) {
+    std::vector<base::Value> args;
+    args.push_back(std::move(changes));
+    return std::make_unique<Event>(StorageAreaToEventHistogram(storage_area),
+                                   area_event_name, std::move(args));
+  };
+  // We only dispatch the events if there's a valid listener (even though
+  // EventRouter would handle the no-listener case) since copying `changes`
+  // can be expensive.
+  // Event for each storage(sync, local, managed).
+  if (has_event_changed_listener && has_area_changed_event_listener) {
+    event_router->DispatchEventToExtension(
+        extension_id, make_area_changed_event(changes.Clone()));
+  }
+  if (has_event_changed_listener) {
+    event_router->DispatchEventToExtension(
+        extension_id, make_changed_event(std::move(changes)));
+  } else if (has_area_changed_event_listener) {
+    event_router->DispatchEventToExtension(
+        extension_id, make_area_changed_event(std::move(changes)));
+  }
+}
+
 // BrowserContextKeyedAPI implementation.
 
 // static
diff --git a/extensions/browser/api/storage/storage_frontend.h b/extensions/browser/api/storage/storage_frontend.h
index 9bdfa37..474d137 100644
--- a/extensions/browser/api/storage/storage_frontend.h
+++ b/extensions/browser/api/storage/storage_frontend.h
@@ -11,6 +11,8 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
 #include "extensions/browser/api/storage/settings_namespace.h"
 #include "extensions/browser/api/storage/settings_observer.h"
 #include "extensions/browser/api/storage/value_store_cache.h"
@@ -61,8 +63,8 @@
   void DeleteStorageSoon(const std::string& extension_id,
                          base::OnceClosure done_callback);
 
-  // Gets the thread-safe observer list.
-  scoped_refptr<SettingsObserverList> GetObservers();
+  // Gets the Settings change callback.
+  SettingsChangedCallback GetObserver();
 
   void DisableStorageForTesting(
       settings_namespace::Namespace settings_namespace);
@@ -87,18 +89,18 @@
 
   void Init(scoped_refptr<value_store::ValueStoreFactory> storage_factory);
 
+  void OnSettingsChanged(const std::string& extension_id,
+                         StorageAreaNamespace storage_area,
+                         base::Value changes);
+
   // The (non-incognito) browser context this Frontend belongs to.
   const raw_ptr<content::BrowserContext> browser_context_;
 
-  // List of observers to settings changes.
-  scoped_refptr<SettingsObserverList> observers_;
-
-  // Observer for |browser_context_|.
-  std::unique_ptr<SettingsObserver> browser_context_observer_;
-
   // Maps a known namespace to its corresponding ValueStoreCache. The caches
   // are owned by this object.
   CacheMap caches_;
+
+  base::WeakPtrFactory<StorageFrontend> weak_factory_{this};
 };
 
 }  // namespace extensions
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index b20f5fc..73beeb8 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -5,18 +5,27 @@
 #include "extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h"
 
 #include <memory>
+#include <ostream>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/check.h"
+#include "base/check_op.h"
 #include "base/feature_list.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
+#include "base/notreached.h"
 #include "base/strings/stringprintf.h"
-#include "base/task/post_task.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
-#include "components/ukm/content/source_url_recorder.h"
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -24,16 +33,21 @@
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/url_utils.h"
 #include "extensions/browser/api/web_request/permission_helper.h"
+#include "extensions/browser/api/web_request/web_request_api.h"
 #include "extensions/browser/extension_navigation_ui_data.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/extensions_client.h"
 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
-#include "net/base/completion_repeating_callback.h"
+#include "net/base/auth.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/isolation_info.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
@@ -42,12 +56,16 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
-#include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/url_loader_completion_status.h"
 #include "services/network/public/mojom/early_hints.mojom.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/network/public/mojom/parsed_headers.mojom-forward.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/loader/throttling_url_loader.h"
+#include "url/gurl.h"
 #include "url/origin.h"
+#include "url/url_constants.h"
 
 namespace extensions {
 namespace {
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index afb814d..10a7ca6 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -7,24 +7,27 @@
 
 #include <cstdint>
 #include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
 
-#include "base/callback.h"
+#include "base/callback_list.h"
 #include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_frame_host.h"
 #include "extensions/browser/api/web_request/web_request_api.h"
 #include "extensions/browser/api/web_request/web_request_info.h"
-#include "extensions/common/extension_id.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/auth.h"
 #include "net/base/completion_once_callback.h"
+#include "net/base/request_priority.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -34,6 +37,22 @@
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
+#include "url/origin.h"
+
+namespace {
+class BrowserContext;
+}
+
+namespace net {
+class HttpRequestHeaders;
+class HttpResponseHeaders;
+class IPEndPoint;
+struct RedirectInfo;
+}  // namespace net
+
+namespace network {
+struct URLLoaderCompletionStatus;
+}
 
 namespace extensions {
 
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc
index fed3a765..a843c1f 100644
--- a/extensions/browser/event_router.cc
+++ b/extensions/browser/event_router.cc
@@ -34,7 +34,6 @@
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_map.h"
 #include "extensions/common/constants.h"
-#include "extensions/common/event_filtering_info_type_converters.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_api.h"
 #include "extensions/common/extension_messages.h"
@@ -163,26 +162,22 @@
     UserGestureState user_gesture,
     mojom::EventFilteringInfoPtr info) {
   NotifyEventDispatched(browser_context, extension_id, event_name, *event_args);
-  mojom::DispatchEventParams params;
-  params.worker_thread_id = worker_thread_id;
-  params.extension_id = extension_id;
-  params.event_name = event_name;
-  params.event_id = event_id;
-  params.is_user_gesture = user_gesture == USER_GESTURE_ENABLED;
-  params.filtering_info = info->To<EventFilteringInfo>();
+  auto params = mojom::DispatchEventParams::New();
+  params->worker_thread_id = worker_thread_id;
+  params->extension_id = extension_id;
+  params->event_name = event_name;
+  params->event_id = event_id;
+  params->is_user_gesture = user_gesture == USER_GESTURE_ENABLED;
+  params->filtering_info = std::move(info);
 
-  // TODO(crbug/1222550): Remove IPC->Send call after worker_thread_dispatcher
-  // is also mojofied.
-  if (worker_thread_id == kMainThreadId) {
-    Get(browser_context)->RouteDispatchEvent(rph, params.Clone(), *event_args);
-  } else {
-    rph->Send(new ExtensionMsg_DispatchEvent(params, *event_args));
-  }
+  Get(browser_context)->RouteDispatchEvent(rph, std::move(params), *event_args);
 }
 
 void EventRouter::RouteDispatchEvent(content::RenderProcessHost* rph,
-                                     const mojom::DispatchEventParamsPtr params,
-                                     const ListValue& event_args) {
+                                     mojom::DispatchEventParamsPtr params,
+                                     ListValue& event_args) {
+  // TODO(crbug.com/1302000) Add bindings for worker threads to be directly
+  // channel-associated.
   mojo::AssociatedRemote<mojom::EventDispatcher>& dispatcher =
       rph_dispatcher_map_[rph];
   if (!dispatcher.is_bound()) {
@@ -193,7 +188,7 @@
     channel->GetRemoteAssociatedInterface(
         dispatcher.BindNewEndpointAndPassReceiver());
   }
-  dispatcher->DispatchEvent(params.Clone(), event_args.Clone());
+  dispatcher->DispatchEvent(std::move(params), event_args.Clone());
 }
 
 // static
diff --git a/extensions/browser/event_router.h b/extensions/browser/event_router.h
index 62def67..82adadd 100644
--- a/extensions/browser/event_router.h
+++ b/extensions/browser/event_router.h
@@ -449,8 +449,8 @@
                                int64_t service_worker_version_id);
 
   void RouteDispatchEvent(content::RenderProcessHost* rph,
-                          const mojom::DispatchEventParamsPtr params,
-                          const base::ListValue& event_args);
+                          mojom::DispatchEventParamsPtr params,
+                          base::ListValue& event_args);
 
   // static
   static void DoDispatchEventToSenderBookkeeping(
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
index 3c60b32..56b6d5d 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/check_op.h"
 #include "components/guest_view/common/guest_view_constants.h"
 #include "content/public/browser/host_zoom_map.h"
 #include "content/public/browser/navigation_handle.h"
@@ -14,6 +15,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/site_instance.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/browser/api/extensions_api_client.h"
@@ -188,11 +190,28 @@
     return;
   }
 
-  // Use the mime handler extension's SiteInstance to create the guest so it
-  // goes under the same process as the extension.
-  ProcessManager* process_manager = ProcessManager::Get(browser_context());
-  scoped_refptr<content::SiteInstance> guest_site_instance =
-      process_manager->GetSiteInstanceForURL(stream_->handler_url());
+  // Compute the mime handler extension's `SiteInstance`. This must match the
+  // `SiteInstance` for the navigation in `DidAttachToEmbedder()`, otherwise the
+  // wrong `HostZoomMap` will be used, and the `RenderFrameHost` for the guest
+  // `WebContents` will need to be swapped.
+  scoped_refptr<content::SiteInstance> guest_site_instance;
+#if BUILDFLAG(ENABLE_PDF)
+  // TODO(crbug.com/1300730): Using `SiteInstance::CreateForURL()` creates a new
+  // `BrowsingInstance`, which causes problems for features like background
+  // pages. Remove one of these branches either when `ProcessManager` correctly
+  // handles the multiple `StoragePartitionConfig` case, or when no
+  // `MimeHandlerView` extension depends on background pages.
+  if (mime_handler_extension->id() == extension_misc::kPdfExtensionId) {
+    guest_site_instance = content::SiteInstance::CreateForURL(
+        browser_context(), stream_->handler_url());
+  } else {
+#endif  // BUILDFLAG(ENABLE_PDF)
+    ProcessManager* process_manager = ProcessManager::Get(browser_context());
+    guest_site_instance =
+        process_manager->GetSiteInstanceForURL(stream_->handler_url());
+#if BUILDFLAG(ENABLE_PDF)
+  }
+#endif  // BUILDFLAG_ENABLE_PDF)
 
   // Clear the zoom level for the mime handler extension. The extension is
   // responsible for managing its own zoom. This is necessary for OOP PDF, as
@@ -466,6 +485,13 @@
     const GURL& new_url = navigation_handle->GetURL();
     CHECK(url::IsSameOriginWith(new_url, stream_->handler_url()) ||
           new_url.IsAboutBlank());
+
+#if BUILDFLAG(ENABLE_PDF)
+    if (stream_->extension_id() == extension_misc::kPdfExtensionId) {
+      // Host zoom level should match the override set in `CreateWebContents()`.
+      DCHECK_EQ(0, content::HostZoomMap::GetZoomLevel(web_contents()));
+    }
+#endif  // BUILDFLAG(ENABLE_PDF)
   }
 }
 
diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc
index c0c456fe..7361208 100644
--- a/extensions/browser/script_executor.cc
+++ b/extensions/browser/script_executor.cc
@@ -66,22 +66,28 @@
                                                          frame_id);
       if (!frame) {
         AddWillNotInjectResult(
-            frame_id, base::StringPrintf("No frame with ID: %d", frame_id));
+            frame_id, ExtensionApiFrameIdMap::DocumentId(),
+            base::StringPrintf("No frame with ID: %d", frame_id));
         continue;
       }
 
       DCHECK(!base::Contains(pending_render_frames_, frame));
       if (!frame->IsRenderFrameLive()) {
+        ExtensionApiFrameIdMap::DocumentId document_id =
+            ExtensionApiFrameIdMap::GetDocumentId(frame);
         AddWillNotInjectResult(
-            frame_id,
+            frame_id, document_id,
             base::StringPrintf("Frame with ID %d is not ready", frame_id));
         continue;
       }
 
       if (frame->IsErrorDocument()) {
+        ExtensionApiFrameIdMap::DocumentId document_id =
+            ExtensionApiFrameIdMap::GetDocumentId(frame);
         AddWillNotInjectResult(
-            frame_id, base::StringPrintf(
-                          "Frame with ID %d is showing error page", frame_id));
+            frame_id, document_id,
+            base::StringPrintf("Frame with ID %d is showing error page",
+                               frame_id));
         continue;
       }
 
@@ -146,8 +152,10 @@
   void WebContentsDestroyed() override {
     for (content::RenderFrameHost* frame : pending_render_frames_) {
       int frame_id = ExtensionApiFrameIdMap::GetFrameId(frame);
+      ExtensionApiFrameIdMap::DocumentId document_id =
+          ExtensionApiFrameIdMap::GetDocumentId(frame);
       AddWillNotInjectResult(
-          frame_id,
+          frame_id, document_id,
           base::StringPrintf("Tab containing frame with ID %d was removed.",
                              frame_id));
     }
@@ -163,16 +171,22 @@
       return;
 
     int frame_id = ExtensionApiFrameIdMap::GetFrameId(render_frame_host);
+    ExtensionApiFrameIdMap::DocumentId document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(render_frame_host);
     AddWillNotInjectResult(
-        frame_id,
+        frame_id, document_id,
         base::StringPrintf("Frame with ID %d was removed.", frame_id));
     if (pending_render_frames_.empty())
       Finish();
   }
 
-  void AddWillNotInjectResult(int frame_id, std::string error) {
+  void AddWillNotInjectResult(
+      int frame_id,
+      const ExtensionApiFrameIdMap::DocumentId& document_id,
+      std::string error) {
     ScriptExecutor::FrameResult result;
     result.frame_id = frame_id;
+    result.document_id = document_id;
     result.error = std::move(error);
     results_.push_back(std::move(result));
   }
@@ -213,6 +227,8 @@
     frame_result.frame_responded = true;
     frame_result.frame_id =
         ExtensionApiFrameIdMap::GetFrameId(render_frame_host);
+    frame_result.document_id =
+        ExtensionApiFrameIdMap::GetDocumentId(render_frame_host);
     frame_result.error = error;
     // TODO(devlin): Do we need to trust the renderer for the URL here? Is there
     // a risk of the frame having navigated since the injection happened?
diff --git a/extensions/browser/script_executor.h b/extensions/browser/script_executor.h
index 12fe8fc..d762422 100644
--- a/extensions/browser/script_executor.h
+++ b/extensions/browser/script_executor.h
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/values.h"
+#include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/mojom/code_injection.mojom.h"
 #include "extensions/common/mojom/css_origin.mojom-shared.h"
@@ -77,6 +78,8 @@
 
     // The ID of the frame of the injection.
     int frame_id = -1;
+    // The document ID of the frame of the injection.
+    ExtensionApiFrameIdMap::DocumentId document_id;
     // The error associated with the injection, if any. Empty if the injection
     // succeeded.
     std::string error;
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index a2a26ee..21575366 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -173,18 +173,6 @@
       traits_sources =
           [ "//extensions/common/mojom/activation_sequence_mojom_traits.cc" ]
     },
-    {
-      types = [
-        {
-          mojom = "extensions.mojom.EventFilteringInfo"
-          cpp = "::extensions::EventFilteringInfo"
-        },
-      ]
-      traits_headers =
-          [ "//extensions/common/mojom/event_dispatcher_mojom_traits.h" ]
-      traits_sources =
-          [ "//extensions/common/mojom/event_dispatcher_mojom_traits.cc" ]
-    },
   ]
   overridden_deps = [ "//content/public/common:interfaces" ]
 
@@ -251,10 +239,6 @@
     "error_utils.h",
     "event_filter.cc",
     "event_filter.h",
-    "event_filtering_info.cc",
-    "event_filtering_info.h",
-    "event_filtering_info_type_converters.cc",
-    "event_filtering_info_type_converters.h",
     "event_matcher.cc",
     "event_matcher.h",
     "extension.cc",
diff --git a/extensions/common/event_filtering_info.cc b/extensions/common/event_filtering_info.cc
deleted file mode 100644
index 865b2b85..0000000
--- a/extensions/common/event_filtering_info.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/common/event_filtering_info.h"
-
-namespace extensions {
-
-EventFilteringInfo::EventFilteringInfo() {}
-EventFilteringInfo::EventFilteringInfo(const EventFilteringInfo& other) =
-    default;
-EventFilteringInfo::~EventFilteringInfo() {}
-
-}  // namespace extensions
diff --git a/extensions/common/event_filtering_info.h b/extensions/common/event_filtering_info.h
deleted file mode 100644
index 04af133..0000000
--- a/extensions/common/event_filtering_info.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
-#define EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
-
-#include <memory>
-#include <string>
-
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/gurl.h"
-
-namespace extensions {
-
-// Extra information about an event that is used in event filtering.
-//
-// This is the information that is matched against criteria specified in JS
-// extension event listeners. Eg:
-//
-// chrome.someApi.onSomeEvent.addListener(cb,
-//                                        {url: [{hostSuffix: 'google.com'}],
-//                                         tabId: 1});
-struct EventFilteringInfo {
- public:
-  EventFilteringInfo();
-  EventFilteringInfo(const EventFilteringInfo& other);
-  ~EventFilteringInfo();
-
-  absl::optional<GURL> url;
-  absl::optional<std::string> service_type;
-  absl::optional<int> instance_id;
-
-  // Note: window type & visible are Chrome concepts, so arguably
-  // doesn't belong in the extensions module. If the number of Chrome
-  // concept grows, consider a delegation model with a
-  // ChromeEventFilteringInfo class.
-  absl::optional<std::string> window_type;
-
-  // By default events related to windows are filtered based on the
-  // listener's extension. This parameter will be set if the listener
-  // didn't set any filter on window types.
-  absl::optional<bool> window_exposed_by_default;
-
-  bool is_empty() const {
-    return !url && !service_type && !instance_id && !window_type &&
-           !window_exposed_by_default;
-  }
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
diff --git a/extensions/common/event_filtering_info_type_converters.cc b/extensions/common/event_filtering_info_type_converters.cc
deleted file mode 100644
index c6df0a0..0000000
--- a/extensions/common/event_filtering_info_type_converters.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/common/event_filtering_info_type_converters.h"
-
-namespace mojo {
-
-// static
-extensions::mojom::EventFilteringInfoPtr
-TypeConverter<extensions::mojom::EventFilteringInfoPtr,
-              extensions::EventFilteringInfo>::
-    Convert(const extensions::EventFilteringInfo& input) {
-  extensions::mojom::EventFilteringInfoPtr output =
-      extensions::mojom::EventFilteringInfo::New();
-  output->url = input.url;
-  output->service_type = input.service_type;
-  output->has_instance_id = input.instance_id.has_value();
-  if (output->has_instance_id)
-    output->instance_id = input.instance_id.value();
-  output->window_type = input.window_type;
-  output->has_window_exposed_by_default =
-      input.window_exposed_by_default.has_value();
-  if (output->has_window_exposed_by_default)
-    output->window_exposed_by_default = input.window_exposed_by_default.value();
-  return output;
-}
-
-// static
-extensions::EventFilteringInfo
-TypeConverter<extensions::EventFilteringInfo,
-              extensions::mojom::EventFilteringInfo>::
-    Convert(const extensions::mojom::EventFilteringInfo& input) {
-  extensions::EventFilteringInfo output;
-  output.url = input.url;
-  output.service_type = input.service_type;
-  if (input.has_instance_id)
-    output.instance_id = input.instance_id;
-  output.window_type = input.window_type;
-  if (input.has_window_exposed_by_default)
-    output.window_exposed_by_default = input.window_exposed_by_default;
-  return output;
-}
-
-}  // namespace mojo
\ No newline at end of file
diff --git a/extensions/common/event_filtering_info_type_converters.h b/extensions/common/event_filtering_info_type_converters.h
deleted file mode 100644
index 5617457..0000000
--- a/extensions/common/event_filtering_info_type_converters.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef EXTENSIONS_COMMON_EVENT_FILTERING_INFO_TYPE_CONVERTERS_H_
-#define EXTENSIONS_COMMON_EVENT_FILTERING_INFO_TYPE_CONVERTERS_H_
-
-#include "extensions/common/event_filtering_info.h"
-#include "extensions/common/mojom/event_dispatcher.mojom.h"
-#include "mojo/public/cpp/bindings/type_converter.h"
-
-namespace mojo {
-// TODO(crbug.com/1222550): Remove these converters once
-// extensions::EventFilteringInfo is removed.
-template <>
-struct TypeConverter<extensions::mojom::EventFilteringInfoPtr,
-                     extensions::EventFilteringInfo> {
-  static extensions::mojom::EventFilteringInfoPtr Convert(
-      const extensions::EventFilteringInfo& input);
-};
-
-template <>
-struct TypeConverter<extensions::EventFilteringInfo,
-                     extensions::mojom::EventFilteringInfo> {
-  static extensions::EventFilteringInfo Convert(
-      const extensions::mojom::EventFilteringInfo& input);
-};
-
-}  // namespace mojo
-
-#endif  // EXTENSIONS_COMMON_EVENT_FILTERING_INFO_TYPE_CONVERTERS_H_
\ No newline at end of file
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 339ce4b..73683a9b 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -28,7 +28,6 @@
 #include "extensions/common/common_param_traits.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/draggable_region.h"
-#include "extensions/common/event_filtering_info.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_guid.h"
 #include "extensions/common/extensions_client.h"
@@ -138,27 +137,6 @@
   IPC_STRUCT_TRAITS_MEMBER(service_worker_version_id)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(extensions::mojom::DispatchEventParams)
-  // If this event is for a service worker, then this is the worker thread
-  // id. Otherwise, this is 0.
-  IPC_STRUCT_TRAITS_MEMBER(worker_thread_id)
-
-  // The id of the extension to dispatch the event to.
-  IPC_STRUCT_TRAITS_MEMBER(extension_id)
-
-  // The name of the event to dispatch.
-  IPC_STRUCT_TRAITS_MEMBER(event_name)
-
-  // The id of the event for use in the EventAck response message.
-  IPC_STRUCT_TRAITS_MEMBER(event_id)
-
-  // Whether or not the event is part of a user gesture.
-  IPC_STRUCT_TRAITS_MEMBER(is_user_gesture)
-
-  // Additional filtering info for the event.
-  IPC_STRUCT_TRAITS_MEMBER(filtering_info)
-IPC_STRUCT_TRAITS_END()
-
 // Struct containing information about the sender of connect() calls that
 // originate from a tab.
 IPC_STRUCT_BEGIN(ExtensionMsg_TabConnectionInfo)
@@ -275,14 +253,6 @@
   IPC_STRUCT_TRAITS_MEMBER(serialization_format)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(extensions::EventFilteringInfo)
-  IPC_STRUCT_TRAITS_MEMBER(url)
-  IPC_STRUCT_TRAITS_MEMBER(service_type)
-  IPC_STRUCT_TRAITS_MEMBER(instance_id)
-  IPC_STRUCT_TRAITS_MEMBER(window_type)
-  IPC_STRUCT_TRAITS_MEMBER(window_exposed_by_default)
-IPC_STRUCT_TRAITS_END()
-
 // Singly-included section for custom IPC traits.
 #ifndef INTERNAL_EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_
 #define INTERNAL_EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_
@@ -310,13 +280,6 @@
 
 // Messages sent from the browser to the renderer:
 
-// Sent to the renderer to dispatch an event to an extension.
-// Note: |event_args| is separate from the params to avoid having the message
-// take ownership.
-IPC_MESSAGE_CONTROL2(ExtensionMsg_DispatchEvent,
-                     extensions::mojom::DispatchEventParams /* params */,
-                     base::ListValue /* event_args */)
-
 // The browser's response to the ExtensionMsg_WakeEventPage IPC.
 IPC_MESSAGE_CONTROL2(ExtensionMsg_WakeEventPageResponse,
                      int /* request_id */,
diff --git a/extensions/common/mojom/event_dispatcher.mojom b/extensions/common/mojom/event_dispatcher.mojom
index 90f9677e..611d762 100644
--- a/extensions/common/mojom/event_dispatcher.mojom
+++ b/extensions/common/mojom/event_dispatcher.mojom
@@ -18,9 +18,6 @@
                   mojo_base.mojom.DeprecatedListValue event_args);
 };
 
-// Typemapped to extensions::EventFilteringInfo.
-// TODO(yochio): Convert extensions::EventFilteringInfo usage to
-// extensions::mojom::EventFilteringInfo (https://crbug.com/1222550)
 struct EventFilteringInfo {
   url.mojom.Url? url;
 
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 22bf85b..0ae7b02 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -32,7 +32,6 @@
 #include "extensions/common/api/messaging/message.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/cors_util.h"
-#include "extensions/common/event_filtering_info_type_converters.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_api.h"
 #include "extensions/common/extension_features.h"
@@ -1302,6 +1301,11 @@
 
 void Dispatcher::DispatchEvent(mojom::DispatchEventParamsPtr params,
                                base::Value event_args) {
+  if (params->worker_thread_id != kMainThreadId) {
+    WorkerThreadDispatcher::Get()->DispatchEvent(std::move(params),
+                                                 std::move(event_args));
+    return;
+  }
   content::RenderFrame* background_frame =
       ExtensionFrameHelper::GetBackgroundPageFrame(params->extension_id);
 
@@ -1326,7 +1330,7 @@
 
   DispatchEventHelper(params->extension_id, params->event_name,
                       base::Value::AsListValue(event_args),
-                      mojom::EventFilteringInfo::From(params->filtering_info));
+                      std::move(params->filtering_info));
 
   if (background_frame) {
     // Tell the browser process when an event has been dispatched with a lazy
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index e2cb976..6b03612 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -284,6 +284,8 @@
   void OnDispatchOnDisconnect(int worker_thread_id,
                               const PortId& port_id,
                               const std::string& error_message);
+
+  // mojom::EventDispatcher implementation.
   void DispatchEvent(mojom::DispatchEventParamsPtr params,
                      base::Value event_args) override;
 
@@ -381,8 +383,9 @@
   // it is dependent on other messages sent on other associated channels.
   mojo::AssociatedReceiver<mojom::Renderer> receiver_;
 
-  // Extensions Dipsatch receiver. This is an associated receiver because
-  // it is dependent on other messages sent on other associated channels.
+  // Extensions mojom::EventDispatcher receiver. This is an associated receiver
+  // because it is dependent on other messages sent on other associated
+  // channels.
   mojo::AssociatedReceiver<mojom::EventDispatcher> dispatcher_;
 
   // Used to hold a service worker information which is ready to execute but the
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc
index 3486329..b55177d1 100644
--- a/extensions/renderer/worker_thread_dispatcher.cc
+++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -16,7 +16,6 @@
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/worker_thread.h"
 #include "extensions/common/constants.h"
-#include "extensions/common/event_filtering_info_type_converters.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/mojom/event_dispatcher.mojom.h"
@@ -33,8 +32,6 @@
 
 namespace {
 
-base::LazyInstance<WorkerThreadDispatcher>::DestructorAtExit
-    g_worker_thread_dispatcher_instance = LAZY_INSTANCE_INITIALIZER;
 base::LazyInstance<base::ThreadLocalPointer<extensions::ServiceWorkerData>>::
     DestructorAtExit g_data_tls = LAZY_INSTANCE_INITIALIZER;
 
@@ -122,11 +119,12 @@
 
 }  // namespace
 
-WorkerThreadDispatcher::WorkerThreadDispatcher() {}
-WorkerThreadDispatcher::~WorkerThreadDispatcher() {}
+WorkerThreadDispatcher::WorkerThreadDispatcher() = default;
+WorkerThreadDispatcher::~WorkerThreadDispatcher() = default;
 
 WorkerThreadDispatcher* WorkerThreadDispatcher::Get() {
-  return g_worker_thread_dispatcher_instance.Pointer();
+  static base::NoDestructor<WorkerThreadDispatcher> dispatcher;
+  return dispatcher.get();
 }
 
 void WorkerThreadDispatcher::Init(content::RenderThread* render_thread) {
@@ -162,7 +160,6 @@
 bool WorkerThreadDispatcher::HandlesMessageOnWorkerThread(
     const IPC::Message& message) {
   return message.type() == ExtensionMsg_ResponseWorker::ID ||
-         message.type() == ExtensionMsg_DispatchEvent::ID ||
          message.type() == ExtensionMsg_DispatchOnConnect::ID ||
          message.type() == ExtensionMsg_DeliverMessage::ID ||
          message.type() == ExtensionMsg_DispatchOnDisconnect::ID ||
@@ -186,6 +183,14 @@
                                       Dispatcher::GetWorkerScriptContextSet());
 }
 
+// static
+void WorkerThreadDispatcher::DispatchEventOnWorkerThread(
+    mojom::DispatchEventParamsPtr params,
+    base::Value event_args) {
+  auto* dispatcher = WorkerThreadDispatcher::Get();
+  dispatcher->DispatchEventHelper(std::move(params), std::move(event_args));
+}
+
 bool WorkerThreadDispatcher::OnControlMessageReceived(
     const IPC::Message& message) {
   if (HandlesMessageOnWorkerThread(message)) {
@@ -305,7 +310,6 @@
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(WorkerThreadDispatcher, message)
     IPC_MESSAGE_HANDLER(ExtensionMsg_ResponseWorker, OnResponseWorker)
-    IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchEvent, OnDispatchEvent)
     IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect, OnDispatchOnConnect)
     IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnDeliverMessage)
     IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect,
@@ -353,32 +357,48 @@
                                           error);
 }
 
-void WorkerThreadDispatcher::OnDispatchEvent(
-    const mojom::DispatchEventParams& params,
-    const base::ListValue& event_args) {
+void WorkerThreadDispatcher::DispatchEventHelper(
+    mojom::DispatchEventParamsPtr params,
+    base::Value event_args) {
+  DCHECK_EQ(params->worker_thread_id, content::WorkerThread::GetCurrentId());
+
   ServiceWorkerData* data = g_data_tls.Pointer()->Get();
-  DCHECK(data);
+
+  // If the worker state was already destroyed via
+  // Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread, then
+  // drop this mojo event. See https://crbug.com/1008143 for details.
+  if (!data)
+    return;
 
   ScriptContext* script_context = data->context();
   // Note |scoped_extension_interaction| requires a HandleScope.
   v8::Isolate* isolate = script_context->isolate();
   v8::HandleScope handle_scope(isolate);
   std::unique_ptr<InteractionProvider::Scope> scoped_extension_interaction;
-  if (params.is_user_gesture) {
+  if (params->is_user_gesture) {
     scoped_extension_interaction =
         ExtensionInteractionProvider::Scope::ForWorker(
             script_context->v8_context());
   }
-  mojom::EventFilteringInfoPtr filtering_info =
-      mojom::EventFilteringInfo::From(params.filtering_info);
+
   data->bindings_system()->DispatchEventInContext(
-      params.event_name, &event_args, filtering_info, data->context());
+      params->event_name, &base::Value::AsListValue(event_args),
+      std::move(params->filtering_info), data->context());
   const int worker_thread_id = content::WorkerThread::GetCurrentId();
   Send(new ExtensionHostMsg_EventAckWorker(data->context()->GetExtensionID(),
                                            data->service_worker_version_id(),
-                                           worker_thread_id, params.event_id));
+                                           worker_thread_id, params->event_id));
 }
 
+void WorkerThreadDispatcher::DispatchEvent(mojom::DispatchEventParamsPtr params,
+                                           base::Value event_args) {
+  DCHECK(!worker_thread_util::IsWorkerThread());
+  const int worker_thread_id = params->worker_thread_id;
+  PostTaskToWorkerThread(
+      worker_thread_id,
+      base::BindOnce(&WorkerThreadDispatcher::DispatchEventOnWorkerThread,
+                     std::move(params), std::move(event_args)));
+}
 void WorkerThreadDispatcher::OnDispatchOnConnect(
     int worker_thread_id,
     const PortId& target_port_id,
diff --git a/extensions/renderer/worker_thread_dispatcher.h b/extensions/renderer/worker_thread_dispatcher.h
index 6e42ee9b..9110c7b7 100644
--- a/extensions/renderer/worker_thread_dispatcher.h
+++ b/extensions/renderer/worker_thread_dispatcher.h
@@ -50,7 +50,8 @@
 // worker thread (this TODO formerly referred to content::ThreadSafeSender
 // which no longer exists).
 class WorkerThreadDispatcher : public content::RenderThreadObserver,
-                               public IPC::Sender {
+                               public IPC::Sender,
+                               public mojom::EventDispatcher {
  public:
   WorkerThreadDispatcher();
 
@@ -150,10 +151,16 @@
   // the IO thread.
   mojom::EventRouter* GetEventRouterOnIO();
 
+  // Mojo interface implementation, called from the main thread.
+  void DispatchEvent(mojom::DispatchEventParamsPtr params,
+                     base::Value event_args) override;
+
  private:
   static bool HandlesMessageOnWorkerThread(const IPC::Message& message);
   static void ForwardIPC(int worker_thread_id, const IPC::Message& message);
   static void UpdateBindingsOnWorkerThread(const ExtensionId& extension_id);
+  static void DispatchEventOnWorkerThread(mojom::DispatchEventParamsPtr params,
+                                          base::Value event_args);
 
   void OnMessageReceivedOnWorkerThread(int worker_thread_id,
                                        const IPC::Message& message);
@@ -166,8 +173,6 @@
                         bool succeeded,
                         const base::ListValue& response,
                         const std::string& error);
-  void OnDispatchEvent(const mojom::DispatchEventParams& params,
-                       const base::ListValue& event_args);
   void OnValidateMessagePort(int worker_thread_id, const PortId& id);
   void OnDispatchOnConnect(int worker_thread_id,
                            const PortId& target_port_id,
@@ -181,6 +186,9 @@
                               const PortId& port_id,
                               const std::string& error_message);
 
+  void DispatchEventHelper(mojom::DispatchEventParamsPtr params,
+                           base::Value event_args);
+
   // IPC sender. Belongs to the render thread, but thread safe.
   scoped_refptr<IPC::SyncMessageFilter> message_filter_;
 
diff --git a/gpu/command_buffer/service/common_decoder.cc b/gpu/command_buffer/service/common_decoder.cc
index 1dc657ca..6edc324 100644
--- a/gpu/command_buffer/service/common_decoder.cc
+++ b/gpu/command_buffer/service/common_decoder.cc
@@ -128,6 +128,13 @@
   return true;
 }
 
+bool CommonDecoder::Bucket::OffsetSizeValid(size_t offset, size_t size) const {
+  size_t end = 0;
+  if (!base::CheckAdd<size_t>(offset, size).AssignIfValid(&end))
+    return false;
+  return end <= size_;
+}
+
 CommonDecoder::CommonDecoder(DecoderClient* client,
                              CommandBufferServiceBase* command_buffer_service)
     : command_buffer_service_(command_buffer_service),
diff --git a/gpu/command_buffer/service/common_decoder.h b/gpu/command_buffer/service/common_decoder.h
index 8096224..3f96002 100644
--- a/gpu/command_buffer/service/common_decoder.h
+++ b/gpu/command_buffer/service/common_decoder.h
@@ -11,16 +11,17 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/memory/raw_ptr.h"
 #include "gpu/command_buffer/common/buffer.h"
 #include "gpu/command_buffer/common/cmd_buffer_common.h"
-#include "gpu/command_buffer/service/async_api_interface.h"
+#include "gpu/command_buffer/common/constants.h"
 #include "gpu/gpu_export.h"
 
 // Forwardly declare a few GL types to avoid including GL header files.
-typedef int GLsizei;
-typedef int GLint;
+using GLsizei = int;
+using GLint = int;
 
 namespace gfx {
 class ColorSpace;
@@ -35,9 +36,9 @@
 // o3d/gl2 command buffer decoder.
 class GPU_EXPORT CommonDecoder {
  public:
-  typedef error::Error Error;
+  using Error = error::Error;
 
-  static const unsigned int kMaxStackDepth = 32;
+  static constexpr unsigned int kMaxStackDepth = 32;
 
   // A bucket is a buffer to help collect memory across a command buffer. When
   // creating a command buffer implementation of an existing API, sometimes that
@@ -107,12 +108,7 @@
                       std::vector<GLint>* _length);
 
    private:
-    bool OffsetSizeValid(size_t offset, size_t size) const {
-      size_t end = 0;
-      if (!base::CheckAdd<size_t>(offset, size).AssignIfValid(&end))
-        return false;
-      return end <= size_;
-    }
+    bool OffsetSizeValid(size_t offset, size_t size) const;
 
     size_t size_;
     ::std::unique_ptr<int8_t[]> data_;
@@ -225,11 +221,11 @@
   raw_ptr<DecoderClient> client_;
   size_t max_bucket_size_;
 
-  typedef std::map<uint32_t, std::unique_ptr<Bucket>> BucketMap;
+  using BucketMap = std::map<uint32_t, std::unique_ptr<Bucket>>;
   BucketMap buckets_;
 
-  typedef Error (CommonDecoder::*CmdHandler)(uint32_t immediate_data_size,
-                                             const volatile void* data);
+  using CmdHandler = Error (CommonDecoder::*)(uint32_t immediate_data_size,
+                                              const volatile void* data);
 
   // A struct to hold info about each command.
   struct CommandInfo {
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.cc b/gpu/command_buffer/service/shared_image_backing_d3d.cc
index 2e2c33e..5050ba75 100644
--- a/gpu/command_buffer/service/shared_image_backing_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_d3d.cc
@@ -594,6 +594,13 @@
                                    WGPUDevice device,
                                    WGPUBackendType backend_type) {
 #if BUILDFLAG(USE_DAWN)
+#if BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
+  if (backend_type == WGPUBackendType_OpenGLES) {
+    return std::make_unique<SharedImageRepresentationDawnEGLImage>(
+        ProduceGLTexturePassthrough(manager, tracker), manager, this, tracker,
+        device);
+  }
+#endif
   const viz::ResourceFormat viz_resource_format = format();
   const WGPUTextureFormat wgpu_format = viz::ToWGPUFormat(viz_resource_format);
   if (wgpu_format == WGPUTextureFormat_Undefined) {
@@ -610,16 +617,6 @@
   texture_descriptor.mipLevelCount = 1;
   texture_descriptor.sampleCount = 1;
 
-#if BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
-  if (backend_type == WGPUBackendType_OpenGLES) {
-    // EGLImage textures do not support sampling, at the moment.
-    texture_descriptor.usage &= ~WGPUTextureUsage_TextureBinding;
-    return std::make_unique<SharedImageRepresentationDawnEGLImage>(
-        ProduceGLTexturePassthrough(manager, tracker), manager, this, tracker,
-        device, texture_descriptor);
-  }
-#endif
-
   // We need to have internal usages of CopySrc for copies and
   // RenderAttachment for clears.
   WGPUDawnTextureInternalUsageDescriptor internalDesc = {};
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc
index e6915d9..b57aa8b 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.cc
@@ -29,6 +29,7 @@
     : use_passthrough_(gpu_preferences.use_passthrough_cmd_decoder &&
                        gles2::PassthroughCommandDecoderSupported()),
       workarounds_(workarounds),
+      use_webgpu_adapter_(gpu_preferences.use_webgpu_adapter),
       progress_reporter_(progress_reporter) {
   gl::GLApi* api = gl::g_current_gl_context;
   api->glGetIntegervFn(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h
index f7546a7..de9f28f 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_common.h
@@ -77,6 +77,7 @@
   bool texture_usage_angle_ = false;
   SharedImageBackingGLCommon::UnpackStateAttribs attribs_;
   GpuDriverBugWorkarounds workarounds_;
+  WebGPUAdapterName use_webgpu_adapter_ = WebGPUAdapterName::kDefault;
 
   // Used to notify the watchdog before a buffer allocation in case it takes
   // long.
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index 89f22cf..b5cb4fe3 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -142,7 +142,8 @@
     return false;
   }
   // Needs interop factory
-  if ((usage & SHARED_IMAGE_USAGE_WEBGPU) ||
+  if (((usage & SHARED_IMAGE_USAGE_WEBGPU) &&
+       use_webgpu_adapter_ != WebGPUAdapterName::kCompat) ||
       (usage & SHARED_IMAGE_USAGE_VIDEO_DECODE) ||
       (usage & SHARED_IMAGE_USAGE_SCANOUT)) {
     return false;
diff --git a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
index a58dd2af..58f69f0 100644
--- a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
@@ -13,7 +13,6 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "components/viz/common/resources/resource_format_utils.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/common/shared_image_trace_utils.h"
@@ -46,6 +45,7 @@
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_version_info.h"
 #include "ui/gl/scoped_binders.h"
+#include "ui/gl/scoped_make_current.h"
 #include "ui/gl/shared_gl_fence_egl.h"
 #include "ui/gl/trace_util.h"
 
@@ -58,6 +58,10 @@
 #include "gpu/command_buffer/service/shared_image_backing_factory_iosurface.h"
 #endif
 
+#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
+#include "gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h"
+#endif
+
 namespace gpu {
 
 namespace {
@@ -186,6 +190,22 @@
                                          MemoryTypeTracker* tracker,
                                          WGPUDevice device,
                                          WGPUBackendType backend_type) {
+#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
+  if (backend_type == WGPUBackendType_OpenGLES) {
+    if (!image_egl_) {
+      CreateEGLImage();
+    }
+    std::unique_ptr<SharedImageRepresentationGLTextureBase> texture;
+    if (IsPassthrough()) {
+      texture = ProduceGLTexturePassthrough(manager, tracker);
+    } else {
+      texture = ProduceGLTexture(manager, tracker);
+    }
+    return std::make_unique<SharedImageRepresentationDawnEGLImage>(
+        std::move(texture), manager, this, tracker, device);
+  }
+#endif
+
   if (!factory()) {
     DLOG(ERROR) << "No SharedImageFactory to create a dawn representation.";
     return nullptr;
@@ -236,6 +256,25 @@
   }
 }
 
+void SharedImageBackingGLTexture::CreateEGLImage() {
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(USE_OZONE)
+  SharedContextState* shared_context_state = factory()->GetSharedContextState();
+  ui::ScopedMakeCurrent smc(shared_context_state->context(),
+                            shared_context_state->surface());
+  auto image_np = base::MakeRefCounted<gl::GLImageNativePixmap>(
+      size(), viz::BufferFormat(format()));
+  image_np->InitializeFromTexture(GetGLServiceId());
+  image_egl_ = image_np;
+  if (passthrough_texture_) {
+    passthrough_texture_->SetLevelImage(passthrough_texture_->target(), 0,
+                                        image_egl_.get());
+  } else if (texture_) {
+    texture_->SetLevelImage(texture_->target(), 0, image_egl_.get(),
+                            gles2::Texture::ImageState::BOUND);
+  }
+#endif
+}
+
 void SharedImageBackingGLTexture::SetCompatibilitySwizzle(
     const gles2::Texture::CompatibilitySwizzle* swizzle) {
   if (!IsPassthrough())
diff --git a/gpu/command_buffer/service/shared_image_backing_gl_texture.h b/gpu/command_buffer/service/shared_image_backing_gl_texture.h
index 0475952f..70a0371 100644
--- a/gpu/command_buffer/service/shared_image_backing_gl_texture.h
+++ b/gpu/command_buffer/service/shared_image_backing_gl_texture.h
@@ -7,6 +7,10 @@
 
 #include "gpu/command_buffer/service/shared_image_backing_gl_common.h"
 
+namespace gl {
+class GLImageEGL;
+}
+
 namespace gpu {
 
 // Implementation of SharedImageBacking that creates a GL Texture that is not
@@ -34,6 +38,7 @@
 
   GLenum GetGLTarget() const;
   GLuint GetGLServiceId() const;
+  void CreateEGLImage();
 
  private:
   // SharedImageBacking:
@@ -68,6 +73,7 @@
   scoped_refptr<gles2::TexturePassthrough> passthrough_texture_;
 
   sk_sp<SkPromiseImageTexture> cached_promise_texture_;
+  scoped_refptr<gl::GLImageEGL> image_egl_;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_representation.h b/gpu/command_buffer/service/shared_image_representation.h
index e1f2e81..b497284 100644
--- a/gpu/command_buffer/service/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image_representation.h
@@ -201,6 +201,7 @@
 
  protected:
   friend class SharedImageRepresentationSkiaGL;
+  friend class SharedImageRepresentationDawnEGLImage;
   friend class SharedImageRepresentationGLTextureImpl;
 
   // Can be overridden to handle clear state tracking when GL access begins or
@@ -247,9 +248,6 @@
   GetTexturePassthrough() = 0;
 
   gpu::TextureBase* GetTextureBase() override;
-
- protected:
-  friend class SharedImageRepresentationDawnEGLImage;
 };
 
 class GPU_GLES2_EXPORT SharedImageRepresentationSkia
diff --git a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc
index 3c9bd0b..698543ee 100644
--- a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc
+++ b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h"
 
 #include "build/build_config.h"
+#include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 
 #include <dawn/native/OpenGLBackend.h>
@@ -23,17 +24,14 @@
 namespace gpu {
 
 SharedImageRepresentationDawnEGLImage::SharedImageRepresentationDawnEGLImage(
-    std::unique_ptr<SharedImageRepresentationGLTexturePassthrough>
-        gl_representation,
+    std::unique_ptr<SharedImageRepresentationGLTextureBase> gl_representation,
     SharedImageManager* manager,
     SharedImageBacking* backing,
     MemoryTypeTracker* tracker,
-    WGPUDevice device,
-    const WGPUTextureDescriptor& texture_descriptor)
+    WGPUDevice device)
     : SharedImageRepresentationDawn(manager, backing, tracker),
       gl_representation_(std::move(gl_representation)),
       device_(device),
-      texture_descriptor_(texture_descriptor),
       dawn_procs_(dawn::native::GetProcs()) {
   DCHECK(device_);
 
@@ -51,11 +49,29 @@
 WGPUTexture SharedImageRepresentationDawnEGLImage::BeginAccess(
     WGPUTextureUsage usage) {
   gl_representation_->BeginAccess(ToSharedImageAccessGLMode(usage));
+  WGPUTextureDescriptor texture_descriptor = {};
+  texture_descriptor.nextInChain = nullptr;
+  texture_descriptor.format = viz::ToWGPUFormat(format());
+  texture_descriptor.usage = WGPUTextureUsage_CopySrc |
+                             WGPUTextureUsage_CopyDst |
+                             WGPUTextureUsage_RenderAttachment;
+  texture_descriptor.dimension = WGPUTextureDimension_2D;
+  texture_descriptor.size = {static_cast<uint32_t>(size().width()),
+                             static_cast<uint32_t>(size().height()), 1};
+  texture_descriptor.mipLevelCount = 1;
+  texture_descriptor.sampleCount = 1;
   dawn::native::opengl::ExternalImageDescriptorEGLImage externalImageDesc;
-  externalImageDesc.cTextureDescriptor = &texture_descriptor_;
-  const auto& texture = gl_representation_->GetTexturePassthrough();
-  externalImageDesc.image =
-      texture->GetLevelImage(texture->target(), 0u)->GetEGLImage();
+  externalImageDesc.cTextureDescriptor = &texture_descriptor;
+  const gl::GLImage* image;
+  gpu::TextureBase* texture = gl_representation_->GetTextureBase();
+  if (texture->GetType() == gpu::TextureBase::Type::kPassthrough) {
+    image = static_cast<gles2::TexturePassthrough*>(texture)->GetLevelImage(
+        texture->target(), 0u);
+  } else {
+    image = static_cast<gles2::Texture*>(texture)->GetLevelImage(
+        texture->target(), 0u);
+  }
+  externalImageDesc.image = image->GetEGLImage();
   DCHECK(externalImageDesc.image);
   externalImageDesc.isInitialized = true;
   texture_ =
diff --git a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h
index e256f71..4fda76a5 100644
--- a/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h
+++ b/gpu/command_buffer/service/shared_image_representation_dawn_egl_image.h
@@ -15,13 +15,11 @@
     : public SharedImageRepresentationDawn {
  public:
   SharedImageRepresentationDawnEGLImage(
-      std::unique_ptr<SharedImageRepresentationGLTexturePassthrough>
-          gl_representation,
+      std::unique_ptr<SharedImageRepresentationGLTextureBase> gl_representation,
       SharedImageManager* manager,
       SharedImageBacking* backing,
       MemoryTypeTracker* tracker,
-      WGPUDevice device,
-      const WGPUTextureDescriptor& texture_descriptor);
+      WGPUDevice device);
   ~SharedImageRepresentationDawnEGLImage() override;
 
  private:
@@ -29,10 +27,8 @@
   void EndAccess() override;
 
  private:
-  std::unique_ptr<SharedImageRepresentationGLTexturePassthrough>
-      gl_representation_;
+  std::unique_ptr<SharedImageRepresentationGLTextureBase> gl_representation_;
   WGPUDevice device_;
-  WGPUTextureDescriptor texture_descriptor_;
   DawnProcTable dawn_procs_;
   WGPUTexture texture_ = nullptr;
 };
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index bd85889f..772ce5f 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1504,7 +1504,8 @@
     force_fallback_adapter = true;
   }
 
-  if (gr_context_type_ != GrContextType::kVulkan) {
+  if (gr_context_type_ != GrContextType::kVulkan &&
+      use_webgpu_adapter_ != WebGPUAdapterName::kCompat) {
 #if BUILDFLAG(IS_LINUX)
     SendAdapterProperties(request_adapter_serial, -1, nullptr,
                           "WebGPU on Linux requires command-line flag "
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc
index 12b263e..b15351f 100644
--- a/gpu/config/gpu_control_list.cc
+++ b/gpu/config/gpu_control_list.cc
@@ -146,21 +146,6 @@
       if (op == kBetween)
         ref_version2.erase(ref_version2.begin());
     }
-
-    // No comparison should be run if two being-compared versions do not match
-    // Intel driver version schema.
-    if (Version::Compare({version[0]}, {"100"}, style) >= 0) {
-      if ((Version::Compare({ref_version1[0]}, {"100"}, style) < 0) ||
-          (op == kBetween
-               ? Version::Compare({ref_version2[0]}, {"100"}, style) < 0
-               : false))
-        return false;
-    } else if ((Version::Compare({ref_version1[0]}, {"100"}, style) >= 0) ||
-               (op == kBetween
-                    ? Version::Compare({ref_version2[0]}, {"100"}, style) >= 0
-                    : false)) {
-      return false;
-    }
   } else if (schema == kVersionSchemaNvidiaDriver) {
     // The driver version we get from the os is "XX.XX.XXXA.BBCC", while the
     // workaround is of the form "ABB.CC".  Drop the first two stanzas from the
diff --git a/gpu/config/gpu_control_list_entry_unittest.cc b/gpu/config/gpu_control_list_entry_unittest.cc
index cdd3006..f68656f 100644
--- a/gpu/config/gpu_control_list_entry_unittest.cc
+++ b/gpu/config/gpu_control_list_entry_unittest.cc
@@ -1129,7 +1129,7 @@
   gpu_info.gpu.driver_version = "25.20.100.6000";
   EXPECT_TRUE(entry.Contains(kOsWin, "", gpu_info));
   gpu_info.gpu.driver_version = "24.20.99.6000";
-  EXPECT_FALSE(entry.Contains(kOsWin, "", gpu_info));
+  EXPECT_TRUE(entry.Contains(kOsWin, "", gpu_info));
   gpu_info.gpu.driver_version = "24.20.101.6000";
   EXPECT_FALSE(entry.Contains(kOsWin, "", gpu_info));
   gpu_info.gpu.driver_version = "25.20.100.7000";
diff --git a/gpu/config/gpu_control_list_format.txt b/gpu/config/gpu_control_list_format.txt
index 45b3854..5d8b1195 100644
--- a/gpu/config/gpu_control_list_format.txt
+++ b/gpu/config/gpu_control_list_format.txt
@@ -108,11 +108,10 @@
 // in that case, major is still numerical, but minor is lexical.
 // Only "driver_version" supports "(intel|nvidia)_driver" schema.
 //
-// intel_driver schema versions have two forms: AA.BB.CC.DDDD (legacy) and
-// AA.BB.CCC.DDDD (new). Of these, the last two fields are most relevant, and
-// the first two can be ignored by setting them to 0. The two forms must not be
-// mixed e.g. < 0.0.100.0 does not include legacy driver versions - the correct
-// constraint is <= 0.0.99.9999.
+// intel_driver schema versions have the form like "AA.BB.(CC|CCC).DDDD". The
+// last two fields are most relevant, and the first two will be ignored by
+// setting them to 0. Thus "0.0.(CC|CCC).DDDD" will be used in comparison
+// following the general rule.
 //
 // FLOAT includes "op" "value", and "value2".  "op" can be any of the
 // following values: "=", "<", "<=", ">", ">=", "any", "between".  "value2" is
diff --git a/gpu/config/gpu_control_list_version_unittest.cc b/gpu/config/gpu_control_list_version_unittest.cc
index e3d8675..0c633c4 100644
--- a/gpu/config/gpu_control_list_version_unittest.cc
+++ b/gpu/config/gpu_control_list_version_unittest.cc
@@ -194,33 +194,22 @@
 
 TEST_F(VersionTest, IntelDriverSchema) {
   {
-    // New drivers, AA.BB.CCC.DDDD
     Version info = {kLT, kNumerical, kIntelDriver, "25.20.100.6952", nullptr};
     EXPECT_TRUE(info.Contains("0.0.100.6000"));
     EXPECT_FALSE(info.Contains("0.0.100.7000"));
     EXPECT_FALSE(info.Contains("0.0.200.6000"));
     EXPECT_TRUE(info.Contains("26.20.100.6000"));
     EXPECT_FALSE(info.Contains("24.20.100.7000"));
+    EXPECT_TRUE(info.Contains("23.20.16.5037"));
   }
   {
-    // Old drivers, AA.BB.CC.DDDD
     Version info = {kGT, kNumerical, kIntelDriver, "10.18.15.4256", nullptr};
     EXPECT_TRUE(info.Contains("0.0.15.6000"));
     EXPECT_FALSE(info.Contains("0.0.15.4000"));
     EXPECT_TRUE(info.Contains("10.18.15.4279"));
     EXPECT_FALSE(info.Contains("15.40.15.4058"));
-  }
-  {
-    // Old driver versions cannot be compared against new driver versions.
-    Version info = {kLT, kNumerical, kIntelDriver, "0.0.100.0", nullptr};
-    EXPECT_FALSE(info.Contains("23.20.16.4973"));
-    EXPECT_FALSE(info.Contains("20.19.15.4364"));
-  }
-  {
-    // Old driver versions can only be compared against old driver versions.
-    Version info = {kLE, kNumerical, kIntelDriver, "0.0.99.9999", nullptr};
-    EXPECT_TRUE(info.Contains("23.20.16.4973"));
-    EXPECT_TRUE(info.Contains("20.19.15.4364"));
+    EXPECT_TRUE(info.Contains("26.20.100.6000"));
+    EXPECT_TRUE(info.Contains("26.20.100.4000"));
   }
 }
 
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
index 234b920..904f1556 100644
--- "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
+++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "builder_group": "chromium.fyi",
+              "builder_group": "chromium.gpu.fyi",
               "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
@@ -53,6 +53,7 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
+  "builder_group": "chromium.gpu.fyi",
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
   "recipe": "chromium"
 }
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json"
index 93d6f64..2848ac3 100644
--- "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json"
+++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "builder_group": "chromium.fyi",
+              "builder_group": "chromium.gpu.fyi",
               "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
@@ -53,6 +53,7 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
+  "builder_group": "chromium.gpu.fyi",
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
   "recipe": "chromium"
 }
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json"
index a7db12b..35595f32 100644
--- "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json"
+++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050dbg\051 \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "builder_group": "chromium.fyi",
+              "builder_group": "chromium.gpu.fyi",
               "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
@@ -53,6 +53,7 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
+  "builder_group": "chromium.gpu.fyi",
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
   "recipe": "chromium"
 }
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json"
index bbddbdce..9e97238 100644
--- "a/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json"
+++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 DX12 Vulkan Builder \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "builder_group": "chromium.fyi",
+              "builder_group": "chromium.gpu.fyi",
               "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
@@ -53,6 +53,7 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
+  "builder_group": "chromium.gpu.fyi",
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
   "recipe": "chromium"
 }
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json"
index 5735468180..1394828 100644
--- "a/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json"
+++ "b/infra/config/generated/builders/ci/GPU FYI XR Win x64 Builder \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "builder_group": "chromium.fyi",
+              "builder_group": "chromium.gpu.fyi",
               "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
@@ -54,6 +54,7 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
+  "builder_group": "chromium.gpu.fyi",
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
   "recipe": "chromium"
 }
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json" "b/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
index 39ae36fb..c9fda0f 100644
--- "a/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
+++ "b/infra/config/generated/builders/ci/GPU Win x64 Builder \050dbg\051 \050reclient shadow\051/properties.json"
@@ -10,7 +10,7 @@
               "project": "chromium"
             },
             "builder_spec": {
-              "builder_group": "chromium.fyi",
+              "builder_group": "chromium.gpu.fyi",
               "execution_mode": "COMPILE_AND_TEST",
               "legacy_chromium_config": {
                 "apply_configs": [
@@ -51,6 +51,7 @@
       "v.test_suite"
     ]
   },
-  "builder_group": "chromium.fyi",
+  "builder_group": "chromium.gpu.fyi",
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
   "recipe": "chromium"
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json b/infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json
deleted file mode 100644
index e4f5494..0000000
--- a/infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "$build/goma": {
-    "enable_ats": true,
-    "jobs": 300,
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org",
-    "use_luci_auth": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.android",
-  "orchestrator": {
-    "builder_group": "tryserver.chromium.android",
-    "builder_name": "android-11-x86-rel"
-  },
-  "recipe": "chromium/compilator"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-11-x86-rel/properties.json b/infra/config/generated/builders/try/android-11-x86-rel/properties.json
index f46f338d..d90599c 100644
--- a/infra/config/generated/builders/try/android-11-x86-rel/properties.json
+++ b/infra/config/generated/builders/try/android-11-x86-rel/properties.json
@@ -1,7 +1,9 @@
 {
-  "$build/chromium_orchestrator": {
-    "compilator": "android-11-x86-rel-compilator",
-    "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
+  "$build/goma": {
+    "enable_ats": true,
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
   },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
@@ -11,5 +13,5 @@
     ]
   },
   "builder_group": "tryserver.chromium.android",
-  "recipe": "chromium/orchestrator"
+  "recipe": "chromium_trybot"
 }
\ No newline at end of file
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index 140386f..f11c6ce 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -453,9 +453,6 @@
 by CQ. These are often used to test new configurations before they are added
 as required builders.
 
-* [android-11-x86-rel](https://ci.chromium.org/p/chromium/builders/try/android-11-x86-rel) ([definition](https://cs.chromium.org/search?q=+file:/try.star$+""android-11-x86-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""android-11-x86-rel""))
-  * Experiment percentage: 10.0
-
 * [android-12-x64-rel](https://ci.chromium.org/p/chromium/builders/try/android-12-x64-rel) ([definition](https://cs.chromium.org/search?q=+file:/try.star$+""android-12-x64-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""android-12-x64-rel""))
   * Experiment percentage: 20.0
 
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index d079cdd..2f32d0f 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -271,13 +271,6 @@
       }
       builders {
         name: "chromium/try/android-11-x86-rel"
-        experiment_percentage: 10
-        location_regexp: ".*"
-        location_regexp_exclude: ".+/[+]/docs/.+"
-        location_regexp_exclude: ".+/[+]/infra/config/.+"
-      }
-      builders {
-        name: "chromium/try/android-11-x86-rel-compilator"
         includable_only: true
       }
       builders {
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 7d16238a..4190f75 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -7168,11 +7168,11 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.gpu.fyi",'
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium"'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 18000
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -7247,11 +7247,11 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.gpu.fyi",'
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium"'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 18000
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -7490,11 +7490,11 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.gpu.fyi",'
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium"'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 18000
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -7569,11 +7569,11 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.gpu.fyi",'
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium"'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 18000
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -7730,11 +7730,11 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.gpu.fyi",'
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium"'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 18000
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -8288,11 +8288,11 @@
         '      }'
         '    }'
         '  },'
-        '  "builder_group": "chromium.fyi",'
+        '  "builder_group": "chromium.gpu.fyi",'
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium"'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 18000
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -24006,6 +24006,8 @@
         '    "tools/android/avd/proto/creation/generic_android19.textpb",'
         '    "tools/android/avd/proto/creation/generic_android22.textpb",'
         '    "tools/android/avd/proto/creation/generic_android23.textpb",'
+        '    "tools/android/avd/proto/creation/generic_android24.textpb",'
+        '    "tools/android/avd/proto/creation/generic_playstore_android24.textpb",'
         '    "tools/android/avd/proto/creation/generic_android25.textpb",'
         '    "tools/android/avd/proto/creation/generic_playstore_android25.textpb",'
         '    "tools/android/avd/proto/creation/generic_android27.textpb",'
@@ -26268,7 +26270,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium"'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 14400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -27119,6 +27121,14 @@
         '      "sdk_package_name": "system-images;android-23;google_apis;x86"'
         '    },'
         '    {'
+        '      "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml",'
+        '      "sdk_package_name": "system-images;android-24;google_apis;x86"'
+        '    },'
+        '    {'
+        '      "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml",'
+        '      "sdk_package_name": "system-images;android-24;google_apis_playstore;x86"'
+        '    },'
+        '    {'
         '      "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-25/google_apis/x86.yaml",'
         '      "sdk_package_name": "system-images;android-25;google_apis;x86"'
         '    },'
@@ -48053,11 +48063,12 @@
     builders {
       name: "android-11-x86-rel"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:android-11-x86-rel"
-      dimensions: "cores:4"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -48086,98 +48097,7 @@
         '  },'
         '  "builder_group": "tryserver.chromium.android",'
         '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium/orchestrator"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      build_numbers: YES
-      service_account: "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/android-11-x86-rel-compilator\">android-11-x86-rel-compilator</a>."
-    }
-    builders {
-      name: "android-11-x86-rel-compilator"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:android-11-x86-rel-compilator"
-      dimensions: "cores:32"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/android-11-x86-rel-compilator/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.android",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium/compilator"'
+        '  "recipe": "chromium_trybot"'
         '}'
       execution_timeout_secs: 14400
       expiration_secs: 7200
@@ -48229,7 +48149,6 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/android-11-x86-rel\">android-11-x86-rel</a>."
     }
     builders {
       name: "android-12-x64-dbg"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 2538cbfb..c023520 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -2448,12 +2448,6 @@
   id: "try"
   name: "Chromium CQ Console"
   builders {
-    name: "buildbucket/luci.chromium.try/android-11-x86-rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-12-x64-rel"
   }
   builders {
@@ -6720,35 +6714,6 @@
     short_name: "64rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/GPU Win x64 Builder (dbg) (reclient shadow)"
-    category: "Windows"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (dbg) (reclient shadow)"
-    category: "Windows|Builder|Debug"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (reclient shadow)"
-    category: "Windows|Builder|Release"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI XR Win x64 Builder (reclient shadow)"
-    category: "Windows|Builder|XR"
-    short_name: "x64"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)"
-    category: "Windows|Builder|dx12vk"
-    short_name: "dbg"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)"
-    category: "Windows|Builder|dx12vk"
-    short_name: "rel"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Comparison Android (reclient)"
     category: "android"
     short_name: "cmp"
@@ -8437,6 +8402,36 @@
     short_name: "x64"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (dbg) (reclient shadow)"
+    category: "Windows|Builder|reclient|Debug"
+    short_name: "x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 Builder (reclient shadow)"
+    category: "Windows|Builder|reclient|Release"
+    short_name: "x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/GPU FYI XR Win x64 Builder (reclient shadow)"
+    category: "Windows|Builder|reclient|XR"
+    short_name: "x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)"
+    category: "Windows|Builder|reclient|dx12vk"
+    short_name: "dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)"
+    category: "Windows|Builder|reclient|dx12vk"
+    short_name: "rel"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/GPU Win x64 Builder (dbg) (reclient shadow)"
+    category: "Windows|Builder|reclient|nonFYI|Debug"
+    short_name: "x64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win10 FYI x64 Release (AMD RX 5500 XT)"
     category: "Windows|10|x64|AMD"
     short_name: "rel"
@@ -14535,9 +14530,6 @@
     name: "buildbucket/luci.chromium.try/android-11-x86-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-12-x64-dbg"
   }
   builders {
@@ -15664,9 +15656,6 @@
     name: "buildbucket/luci.chromium.try/android-11-x86-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-11-x86-rel-compilator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-12-x64-dbg"
   }
   builders {
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index 2465c36b..17c5672b 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -206,15 +206,6 @@
 # setting ssd:0 dimension
 _EXCLUDE_BUILDERLESS_SSD_OS_CATEGORIES = [os_category.MAC]
 
-def _chromium_tests_property(*, project_trigger_overrides):
-    chromium_tests = {}
-
-    project_trigger_overrides = defaults.get_value("project_trigger_overrides", project_trigger_overrides)
-    if project_trigger_overrides:
-        chromium_tests["project_trigger_overrides"] = project_trigger_overrides
-
-    return chromium_tests or None
-
 def _goma_property(*, goma_backend, goma_debug, goma_enable_ats, goma_jobs):
     goma_properties = {}
 
@@ -339,7 +330,6 @@
     goma_jobs = None,
     list_view = args.COMPUTE,
     os = None,
-    project_trigger_overrides = None,
     pool = None,
     sheriff_rotations = None,
     xcode = None,
@@ -396,7 +386,6 @@
         xcode = args.DEFAULT,
         console_view_entry = None,
         list_view = args.DEFAULT,
-        project_trigger_overrides = args.DEFAULT,
         goma_backend = args.DEFAULT,
         goma_debug = args.DEFAULT,
         goma_enable_ats = args.DEFAULT,
@@ -513,11 +502,6 @@
         list_view: A string or a list of strings identifying the ID(s) of the
             list view(s) to add an entry to. Supports a module-level default
             that defaults to no list views.
-        project_trigger_overrides: a dict mapping the LUCI projects declared in
-            recipe BotSpecs to the LUCI project to use when triggering builders.
-            When this builder triggers another builder, if the BotSpec for that
-            builder has a LUCI project that is a key in this mapping, the
-            corresponding value will be used instead.
         goma_backend: a member of the `goma.backend` enum indicating the goma
             backend the builder should use. Will be incorporated into the
             '$build/goma' property. By default, considered None.
@@ -684,12 +668,6 @@
     if ssd != None:
         dimensions["ssd"] = str(int(ssd))
 
-    chromium_tests = _chromium_tests_property(
-        project_trigger_overrides = project_trigger_overrides,
-    )
-    if chromium_tests != None:
-        properties["$build/chromium_tests"] = chromium_tests
-
     goma_enable_ats = defaults.get_value("goma_enable_ats", goma_enable_ats)
 
     # Enable ATS on linux by default.
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 40c02bb..24502a1 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -13,9 +13,6 @@
     bucket = "ci",
     build_numbers = True,
     cpu = cpu.X86_64,
-    project_trigger_overrides = branches.value({
-        branches.NOT_MAIN: {"chromium": settings.project},
-    }),
     triggered_by = ["chromium-gitiles-trigger"],
 )
 
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
index 891eb52..1f57629 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -138,6 +138,7 @@
         category = "emulator|x86|rel",
         short_name = "N",
     ),
+    execution_timeout = 4 * time.hour,
     goma_backend = None,
     reclient_instance = rbe_instance.DEFAULT,
     reclient_jobs = rbe_jobs.DEFAULT,
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index fd404edd..dcdb2ed 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -82,18 +82,6 @@
     kwargs.setdefault("os", os.MAC_DEFAULT)
     return ci.builder(name = name, **kwargs)
 
-def _fyi_gpu_windows_builder(*, name, **kwargs):
-    kwargs.setdefault("execution_timeout", ci.DEFAULT_EXECUTION_TIMEOUT)
-    kwargs.setdefault("builderless", True)
-    kwargs.setdefault("cores", 8)
-    kwargs.setdefault("os", os.WINDOWS_ANY)
-    kwargs.setdefault("service_account", ci.gpu.SERVICE_ACCOUNT)
-    kwargs.setdefault("pool", ci.gpu.POOL)
-    kwargs.setdefault("goma_backend", None)
-    kwargs.setdefault("reclient_jobs", rbe_jobs.LOW_JOBS_FOR_CI)
-    kwargs.setdefault("reclient_instance", rbe_instance.DEFAULT)
-    return ci.builder(name = name, **kwargs)
-
 ci.builder(
     name = "Linux Viz",
     console_view_entry = consoles.console_view_entry(
@@ -814,154 +802,6 @@
 )
 # End - Reclient migration, phase 2, block 1 shadow builders
 
-_fyi_gpu_windows_builder(
-    name = "GPU FYI Win x64 Builder (reclient shadow)",
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "chrome_internal",
-                "no_kaleidoscope",
-                "enable_reclient",
-            ],
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|Release",
-        short_name = "x64",
-    ),
-)
-
-_fyi_gpu_windows_builder(
-    name = "GPU FYI Win x64 Builder (dbg) (reclient shadow)",
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.DEBUG,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "chrome_internal",
-                "no_kaleidoscope",
-                "enable_reclient",
-            ],
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|Debug",
-        short_name = "x64",
-    ),
-)
-
-_fyi_gpu_windows_builder(
-    name = "GPU FYI XR Win x64 Builder (reclient shadow)",
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "chrome_internal",
-                "no_kaleidoscope",
-                "enable_reclient",
-            ],
-        ),
-        # This causes the builder to upload isolates to a location where
-        # Pinpoint can access them in addition to the usual isolate
-        # server. This is necessary because "Win10 FYI x64 Release XR
-        # perf (NVIDIA)", which is a child of this builder, uploads perf
-        # results, and Pinpoint may trigger additional builds on this
-        # builder during a bisect.
-        perf_isolate_upload = True,
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|XR",
-        short_name = "x64",
-    ),
-)
-
-_fyi_gpu_windows_builder(
-    name = "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)",
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "chrome_internal",
-                "no_kaleidoscope",
-                "enable_reclient",
-            ],
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|dx12vk",
-        short_name = "rel",
-    ),
-)
-
-_fyi_gpu_windows_builder(
-    name = "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)",
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.DEBUG,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "chrome_internal",
-                "no_kaleidoscope",
-                "enable_reclient",
-            ],
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|dx12vk",
-        short_name = "dbg",
-    ),
-)
-
-_fyi_gpu_windows_builder(
-    name = "GPU Win x64 Builder (dbg) (reclient shadow)",
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.DEBUG,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "enable_reclient",
-            ],
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows",
-    ),
-)
-
 ci.builder(
     name = "Win ASan Release (reclient shadow)",
     builderless = True,
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index b0f06d5..1152256 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -3,6 +3,8 @@
 # found in the LICENSE file.
 """Definitions of builders in the chromium.gpu.fyi builder group."""
 
+load("//lib/args.star", "args")
+load("//lib/builder_config.star", "builder_config")
 load("//lib/builders.star", "goma", "sheriff_rotations")
 load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
 load("//lib/consoles.star", "consoles")
@@ -55,6 +57,14 @@
     kwargs.setdefault("execution_timeout", ci.DEFAULT_EXECUTION_TIMEOUT)
     return ci.gpu.windows_builder(name = name, **kwargs)
 
+def _gpu_fyi_windows_builder_shadow(*, name, **kwargs):
+    kwargs.setdefault("execution_timeout", 5 * time.hour)
+    kwargs.setdefault("goma_backend", None)
+    kwargs.setdefault("reclient_jobs", rbe_jobs.LOW_JOBS_FOR_CI)
+    kwargs.setdefault("reclient_instance", rbe_instance.DEFAULT)
+    kwargs.setdefault("sheriff_rotations", args.ignore_default(None))
+    return gpu_fyi_windows_builder(name = name, **kwargs)
+
 ci.thin_tester(
     name = "Android FYI Release (NVIDIA Shield TV)",
     console_view_entry = consoles.console_view_entry(
@@ -626,3 +636,152 @@
         short_name = "x64",
     ),
 )
+
+_gpu_fyi_windows_builder_shadow(
+    name = "GPU FYI Win x64 Builder (reclient shadow)",
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "chrome_internal",
+                "no_kaleidoscope",
+                "enable_reclient",
+            ],
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|reclient|Release",
+        short_name = "x64",
+    ),
+)
+
+_gpu_fyi_windows_builder_shadow(
+    name = "GPU FYI Win x64 Builder (dbg) (reclient shadow)",
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.DEBUG,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "chrome_internal",
+                "no_kaleidoscope",
+                "enable_reclient",
+            ],
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|reclient|Debug",
+        short_name = "x64",
+    ),
+)
+
+_gpu_fyi_windows_builder_shadow(
+    name = "GPU FYI XR Win x64 Builder (reclient shadow)",
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "chrome_internal",
+                "no_kaleidoscope",
+                "enable_reclient",
+            ],
+        ),
+        # This causes the builder to upload isolates to a location where
+        # Pinpoint can access them in addition to the usual isolate
+        # server. This is necessary because "Win10 FYI x64 Release XR
+        # perf (NVIDIA)", which is a child of this builder, uploads perf
+        # results, and Pinpoint may trigger additional builds on this
+        # builder during a bisect.
+        perf_isolate_upload = True,
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|reclient|XR",
+        short_name = "x64",
+    ),
+)
+
+_gpu_fyi_windows_builder_shadow(
+    name = "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)",
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "chrome_internal",
+                "no_kaleidoscope",
+                "enable_reclient",
+            ],
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|reclient|dx12vk",
+        short_name = "rel",
+    ),
+)
+
+_gpu_fyi_windows_builder_shadow(
+    name = "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)",
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.DEBUG,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "chrome_internal",
+                "no_kaleidoscope",
+                "enable_reclient",
+            ],
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|reclient|dx12vk",
+        short_name = "dbg",
+    ),
+)
+
+_gpu_fyi_windows_builder_shadow(
+    name = "GPU Win x64 Builder (dbg) (reclient shadow)",
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.DEBUG,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "enable_reclient",
+            ],
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|reclient|nonFYI|Debug",
+        short_name = "x64",
+    ),
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.packager.star b/infra/config/subprojects/chromium/ci/chromium.packager.star
index bc612b0..f8d622d 100644
--- a/infra/config/subprojects/chromium/ci/chromium.packager.star
+++ b/infra/config/subprojects/chromium/ci/chromium.packager.star
@@ -95,6 +95,8 @@
             "tools/android/avd/proto/creation/generic_android19.textpb",
             "tools/android/avd/proto/creation/generic_android22.textpb",
             "tools/android/avd/proto/creation/generic_android23.textpb",
+            "tools/android/avd/proto/creation/generic_android24.textpb",
+            "tools/android/avd/proto/creation/generic_playstore_android24.textpb",
             "tools/android/avd/proto/creation/generic_android25.textpb",
             "tools/android/avd/proto/creation/generic_playstore_android25.textpb",
             "tools/android/avd/proto/creation/generic_android27.textpb",
@@ -195,6 +197,14 @@
                 "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-23/google_apis/x86.yaml",
             },
             {
+                "sdk_package_name": "system-images;android-24;google_apis;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-24;google_apis_playstore;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml",
+            },
+            {
                 "sdk_package_name": "system-images;android-25;google_apis;x86",
                 "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-25/google_apis/x86.yaml",
             },
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 069ebaf..fc7607a 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -32,23 +32,8 @@
     name = "android-10-arm64-rel",
 )
 
-try_.orchestrator_builder(
+try_.builder(
     name = "android-11-x86-rel",
-    compilator = "android-11-x86-rel-compilator",
-    # TODO(crbug.com/1137474): Enable it on branch after running on CQ
-    # branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    # TODO(crbug.com/1137474): Fully enable once it works fine
-    tryjob = try_.job(
-        experiment_percentage = 10,
-    ),
-)
-
-try_.compilator_builder(
-    name = "android-11-x86-rel-compilator",
-    # TODO(crbug.com/1137474): Enable it on branch after running on CQ
-    # branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
 )
 
 try_.builder(
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt
index 411f0e5..ae20a2e 100644
--- a/infra/inclusive_language_presubmit_exempt_dirs.txt
+++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -604,8 +604,8 @@
 third_party/rust/aquamarine/v0_1/crate/doc/js 1 1
 third_party/rust/atty/v0_2/crate 3 2
 third_party/rust/autocxx_bindgen/v0_59/crate/src 10 2
-third_party/rust/autocxx/v0_16/crate/.github/workflows 1 1
-third_party/rust/autocxx/v0_16/crate/src 1 1
+third_party/rust/autocxx/v0_17/crate/.github/workflows 1 1
+third_party/rust/autocxx/v0_17/crate/src 1 1
 third_party/rust/bindgen/v0_59/crate/src 10 2
 third_party/rust/cexpr/v0_6/crate/.github/workflows 2 1
 third_party/rust/cfg_if/v1/crate/.github/workflows 4 1
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift
index 48049a5..82e5780 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_provider.swift
@@ -7,12 +7,14 @@
 
 /// A provider to provide the SwiftUI PopupView to Objective-C. This is
 /// necessary because Objective-C can't see SwiftUI types.
-///
-/// - Parameters:
-///   - model: The popup model to be used by the popup view.
-///   - popupShouldSelfSize: Whether the popup should resize itself to fit its content.
-/// - Returns: The hosting controller which embeds the popup view.
 @objcMembers public class OmniboxPopupViewProvider: NSObject {
+
+  /// Returns a hosting controller embedding the popup view using the given model and settings.
+  ///
+  /// - Parameters:
+  ///   - model: The popup model to be used by the popup view.
+  ///   - popupShouldSelfSize: Whether the popup should resize itself to fit its content.
+  /// - Returns: The hosting controller which embeds the popup view.
   public static func makeViewController(withModel model: PopupModel, popupShouldSelfSize: Bool)
     -> UIViewController & ContentProviding
   {
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift
index 556937b3e..0273f200 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_match_row_view.swift
@@ -120,13 +120,13 @@
     ZStack {
       if self.isPressed || self.isHighlighted { Color.cr_tableRowViewHighlight }
 
-      Button(action: selectionHandler) { Rectangle().fill(.clear).contentShape(Rectangle()) }
+      Button(action: selectionHandler) { Color.clear.contentShape(Rectangle()) }
         .buttonStyle(PressedPreferenceKeyButtonStyle())
         .onPreferenceChange(PressedPreferenceKey.self) { isPressed in
           self.isPressed = isPressed
         }
 
-      /// The content is in front of the button, for proper hit testing.
+      // The content is in front of the button, for proper hit testing.
       HStack(alignment: .center, spacing: 0) {
         HStack(alignment: .center, spacing: 0) {
           Spacer()
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift b/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift
index 14e8b18..252d52f 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/popup_view.swift
@@ -4,6 +4,34 @@
 
 import SwiftUI
 
+/// Utility which provides a way to treat the `simultaneousGesture` view modifier as a value.
+struct SimultaneousGestureModifier<T: Gesture>: ViewModifier {
+  let gesture: T
+  let mask: GestureMask
+
+  init(_ gesture: T, including mask: GestureMask = .all) {
+    self.gesture = gesture
+    self.mask = mask
+  }
+
+  func body(content: Content) -> some View {
+    content.simultaneousGesture(gesture, including: mask)
+  }
+}
+
+/// A view modifier which embeds the content in a `ScrollViewReader` and calls `action`
+/// when the provided `value` changes. It is similar to the `onChange` view modifier, but provides
+/// a `ScrollViewProxy` object in addition to the new state of `value` when calling `action`.
+struct ScrollOnChangeModifier<V: Equatable>: ViewModifier {
+  @Binding var value: V
+  let action: (V, ScrollViewProxy) -> Void
+  func body(content: Content) -> some View {
+    ScrollViewReader { scrollProxy in
+      content.onChange(of: value) { newState in action(newState, scrollProxy) }
+    }
+  }
+}
+
 struct PopupView: View {
   enum Dimensions {
     static let matchListRowInsets = EdgeInsets(.zero)
@@ -23,7 +51,7 @@
     self.appearanceContainerType = appearanceContainerType
   }
 
-  var content: some View {
+  var listContent: some View {
     ForEach(Array(zip(model.sections.indices, model.sections)), id: \.0) {
       sectionIndex, section in
 
@@ -34,7 +62,6 @@
             match: match,
             isHighlighted: IndexPath(row: matchIndex, section: sectionIndex)
               == self.model.highlightedMatchIndexPath,
-
             selectionHandler: {
               model.delegate?.autocompleteResultConsumer(
                 model, didSelectRow: UInt(matchIndex), inSection: UInt(sectionIndex))
@@ -69,18 +96,24 @@
   }
 
   var body: some View {
+    let listModifier = SimultaneousGestureModifier(DragGesture().onChanged { onDrag($0) })
+      .concat(ScrollOnChangeModifier(value: $model.sections, action: onNewMatches))
+
     if shouldSelfSize {
-      SelfSizingList(bottomMargin: Dimensions.selfSizingListBottomMargin) {
-        content
+      SelfSizingList(
+        bottomMargin: Dimensions.selfSizingListBottomMargin,
+        listModifier: listModifier
+      ) {
+        listContent
       } emptySpace: {
         PopupEmptySpaceView()
       }
       .onAppear(perform: onAppear)
     } else {
-      List {
-        content
-      }
-      .onAppear(perform: onAppear)
+      List { listContent }
+        .modifier(listModifier)
+        .onAppear(perform: onAppear)
+        .ignoresSafeArea(.keyboard)
     }
   }
 
@@ -90,9 +123,21 @@
         appearanceContainerType
       ])
 
-      listAppearance.bounces = false
+      if shouldSelfSize {
+        listAppearance.bounces = false
+      }
     }
   }
+
+  func onDrag(_ dragValue: DragGesture.Value) {
+    model.highlightedMatchIndexPath = nil
+    model.delegate?.autocompleteResultConsumerDidScroll(model)
+  }
+
+  func onNewMatches(matches: [PopupMatchSection], scrollProxy: ScrollViewProxy) {
+    // Scroll to the very top of the list.
+    scrollProxy.scrollTo(0, anchor: UnitPoint(x: 0, y: -.infinity))
+  }
 }
 
 struct PopupView_Previews: PreviewProvider {
diff --git a/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift b/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift
index 8b01425..0f482ec 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift
+++ b/ios/chrome/browser/ui/omnibox/popup/shared/self_sizing_list.swift
@@ -20,17 +20,25 @@
 
 /// View which acts like a `List` but which also clips whatever empty space
 /// is available below the actual content to replace it with an arbitrary view.
-struct SelfSizingList<Content: View, EmptySpace: View>: View {
+struct SelfSizingList<Content: View, EmptySpace: View, ListModifier: ViewModifier>: View {
   let bottomMargin: CGFloat
+  var listModifier: ListModifier
   var content: () -> Content
   var emptySpace: () -> EmptySpace
 
+  /// - Parameters:
+  ///   - bottomMargin: The bottom margin below the end of the list.
+  ///   - listModifier: A `ViewModifier` to apply to the internal list.
+  ///   - content: The content of the list.
+  ///   - emptySpace: The view used to replace the area below the list.
   init(
     bottomMargin: CGFloat = 0,
+    listModifier: ListModifier,
     @ViewBuilder content: @escaping () -> Content,
     @ViewBuilder emptySpace: @escaping () -> EmptySpace
   ) {
     self.bottomMargin = bottomMargin
+    self.listModifier = listModifier
     self.content = content
     self.emptySpace = emptySpace
   }
@@ -47,6 +55,7 @@
             content()
               .anchorPreference(key: MaxYPreferenceKey.self, value: .bounds) { geometry[$0].maxY }
           }
+          .modifier(listModifier)
           .onPreferenceChange(MaxYPreferenceKey.self) { newMaxY in
             let newContentHeightEstimate = newMaxY.map {
               min($0 + bottomMargin, geometry.size.height)
diff --git a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
index 6c3c137..999f182 100644
--- a/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/table_cell_catalog_view_controller.mm
@@ -67,9 +67,12 @@
   ItemTypeSearchHistorySuggestedItem,
   ItemTypeTextAccessoryNoImage,
   ItemTypeTextEditItem,
+  ItemTypeURLWithActivityIndicator,
+  ItemTypeURLWithActivityIndicatorStopped,
   ItemTypeURLWithTimestamp,
   ItemTypeURLWithSize,
   ItemTypeURLWithSupplementalText,
+  ItemTypeURLWithThirdRowText,
   ItemTypeURLWithBadgeImage,
   ItemTypeTextSettingsDetail,
   ItemTypeLinkFooter,
@@ -598,6 +601,33 @@
   item.URL = [[CrURL alloc] initWithGURL:GURL("https://photos.google.com/")];
   item.badgeImage = [UIImage imageNamed:@"table_view_cell_check_mark"];
   [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
+
+  item =
+      [[TableViewURLItem alloc] initWithType:ItemTypeURLWithActivityIndicator];
+  item.title = @"Sent Request to Server";
+  item.URL = [[CrURL alloc] initWithGURL:GURL("https://started.spinner.com/")];
+  [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
+
+  item = [[TableViewURLItem alloc]
+      initWithType:ItemTypeURLWithActivityIndicatorStopped];
+  item.title = @"Received Response from Server";
+  item.URL = [[CrURL alloc] initWithGURL:GURL("https://stopped.spinner.com/")];
+  [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
+
+  item = [[TableViewURLItem alloc] initWithType:ItemTypeURLWithThirdRowText];
+  item.title = @"Web Channel with 3rd Row Text";
+  item.URL =
+      [[CrURL alloc] initWithGURL:GURL("https://blog.google/products/chrome/")];
+  item.thirdRowText = @"Unavailable";
+  [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
+
+  item = [[TableViewURLItem alloc] initWithType:ItemTypeURLWithThirdRowText];
+  item.title = @"Web Channel with 3rd Row Red Text";
+  item.URL =
+      [[CrURL alloc] initWithGURL:GURL("https://blog.google/products/chrome/")];
+  item.thirdRowText = @"Unavailable";
+  item.thirdRowTextColor = UIColor.redColor;
+  [model addItem:item toSectionWithIdentifier:SectionIdentifierURL];
 }
 
 #pragma mark - Actions
@@ -667,6 +697,18 @@
         base::mac::ObjCCastStrict<TableViewTabsSearchSuggestedHistoryCell>(
             cell);
     [searchHistoryCell updateHistoryResultsCount:7];
+  } else if (itemType == ItemTypeURLWithActivityIndicator) {
+    TableViewURLCell* URLCell =
+        base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+    [URLCell startAnimatingActivityIndicator];
+  } else if (itemType == ItemTypeURLWithActivityIndicatorStopped) {
+    TableViewURLCell* URLCell =
+        base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+    [URLCell startAnimatingActivityIndicator];
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC),
+                   dispatch_get_main_queue(), ^{
+                     [URLCell stopAnimatingActivityIndicator];
+                   });
   }
   return cell;
 }
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
index 9039fb8..e9ead03 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.h
@@ -25,6 +25,12 @@
 @property(nonatomic, readwrite, copy) NSString* supplementalURLText;
 // Delimiter used to separate the URL hostname and the supplemental text.
 @property(nonatomic, readwrite, copy) NSString* supplementalURLTextDelimiter;
+// Custom third row text. This is not shown if it is empty or if the second row
+// is empty.
+@property(nonatomic, readwrite, copy) NSString* thirdRowText;
+// Third row text color, if it is shown. If nil, ChromeTableViewStyler's
+// |detailTextColor| is used, otherwise a default color is used.
+@property(nonatomic, strong) UIColor* thirdRowTextColor;
 // Detail text to be displayed instead of the URL.
 @property(nonatomic, strong) NSString* detailText;
 // Metadata text displayed at the trailing edge of the cell.
@@ -59,6 +65,10 @@
 // Optional metadata that is displayed at the trailing edge of the cell.
 @property(nonatomic, readonly, strong) UILabel* metadataLabel;
 
+// Optional third row label. This is never used in place of the second row of
+// text.
+@property(nonatomic, readonly, strong) UILabel* thirdRowLabel;
+
 // Unique identifier that matches with one URLItem.
 @property(nonatomic, strong) NSString* cellUniqueIdentifier;
 
@@ -67,6 +77,14 @@
 // that use TableViewURLCell.
 - (void)configureUILayout;
 
+// Starts the animation of the activity indicator replacing the favicon. NO-OP
+// if it is already running.
+- (void)startAnimatingActivityIndicator;
+
+// Stops the animation of the activity indicator and puts favicon back in place.
+// NO-OP if it is already stopped.
+- (void)stopAnimatingActivityIndicator;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_CELLS_TABLE_VIEW_URL_ITEM_H_
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
index eb35a03..d7eb985 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item.mm
@@ -56,6 +56,7 @@
       base::mac::ObjCCastStrict<TableViewURLCell>(tableCell);
   cell.titleLabel.text = [self titleLabelText];
   cell.URLLabel.text = [self URLLabelText];
+  cell.thirdRowLabel.text = self.thirdRowText;
   cell.faviconBadgeView.image = self.badgeImage;
   cell.metadataLabel.text = self.metadata;
   cell.cellUniqueIdentifier = self.uniqueIdentifier;
@@ -65,8 +66,11 @@
     cell.titleLabel.textColor = styler.cellTitleColor;
   if (styler.cellDetailColor) {
     cell.URLLabel.textColor = styler.cellDetailColor;
+    cell.thirdRowLabel.textColor = styler.cellDetailColor;
     cell.metadataLabel.textColor = styler.cellDetailColor;
   }
+  if (self.thirdRowTextColor)
+    cell.thirdRowLabel.textColor = self.thirdRowTextColor;
 
   [cell configureUILayout];
 }
@@ -132,6 +136,9 @@
 @property(nonatomic, strong) UIStackView* horizontalStack;
 // Container View for the faviconView.
 @property(nonatomic, strong) FaviconContainerView* faviconContainerView;
+// Activity indicator (spinner) used for indicating an in-flight request related
+// to the item represented by this cell.
+@property(nonatomic, strong) UIActivityIndicatorView* activityIndicatorView;
 @end
 
 @implementation TableViewURLCell
@@ -146,6 +153,7 @@
     _faviconBadgeView = [[TableViewURLCellFaviconBadgeView alloc] init];
     _titleLabel = [[UILabel alloc] init];
     _URLLabel = [[UILabel alloc] init];
+    _thirdRowLabel = [[UILabel alloc] init];
     _metadataLabel = [[UILabel alloc] init];
 
     // Set font sizes using dynamic type.
@@ -156,6 +164,11 @@
     _URLLabel.adjustsFontForContentSizeCategory = YES;
     _URLLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
     _URLLabel.hidden = YES;
+    _thirdRowLabel.font =
+        [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
+    _thirdRowLabel.adjustsFontForContentSizeCategory = YES;
+    _thirdRowLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
+    _thirdRowLabel.hidden = YES;
     _metadataLabel.font =
         [UIFont preferredFontForTextStyle:kTableViewSublabelFontStyle];
     _metadataLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
@@ -164,7 +177,7 @@
 
     // Use stack views to layout the subviews except for the favicon.
     UIStackView* verticalStack = [[UIStackView alloc]
-        initWithArrangedSubviews:@[ _titleLabel, _URLLabel ]];
+        initWithArrangedSubviews:@[ _titleLabel, _URLLabel, _thirdRowLabel ]];
     verticalStack.axis = UILayoutConstraintAxisVertical;
     [_metadataLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh
                                       forAxis:UILayoutConstraintAxisHorizontal];
@@ -256,6 +269,12 @@
   } else {
     self.URLLabel.hidden = YES;
   }
+  if ([self.thirdRowLabel.text length] && !self.URLLabel.hidden) {
+    self.thirdRowLabel.hidden = NO;
+  } else {
+    // There shouldn't be a third row if the second row isn't even shown.
+    self.thirdRowLabel.hidden = YES;
+  }
 }
 
 - (void)prepareForReuse {
@@ -265,6 +284,7 @@
   self.horizontalStack.alignment = UIStackViewAlignmentFill;
   self.metadataLabel.hidden = YES;
   self.URLLabel.hidden = YES;
+  self.thirdRowLabel.hidden = YES;
 }
 
 - (void)setAccessibilityLabel:(NSString*)accessibilityLabel {
@@ -279,6 +299,11 @@
       accessibilityLabel = [NSString
           stringWithFormat:@"%@, %@", accessibilityLabel, self.URLLabel.text];
     }
+    if (self.thirdRowLabel.text.length > 0) {
+      accessibilityLabel =
+          [NSString stringWithFormat:@"%@, %@", accessibilityLabel,
+                                     self.thirdRowLabel.text];
+    }
     if (self.metadataLabel.text.length > 0) {
       accessibilityLabel =
           [NSString stringWithFormat:@"%@, %@", accessibilityLabel,
@@ -307,4 +332,31 @@
   return YES;
 }
 
+- (void)startAnimatingActivityIndicator {
+  // It may be an edge case if the activity indicator is spinning when we don't
+  // expect it. But it's okay to leave indicator spinning instead of crashing.
+  if (self.activityIndicatorView != nil) {
+    return;
+  }
+
+  self.activityIndicatorView = [[UIActivityIndicatorView alloc] init];
+  UIActivityIndicatorView* activityView = self.activityIndicatorView;
+  activityView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.faviconContainerView addSubview:activityView];
+  [NSLayoutConstraint activateConstraints:@[
+    [activityView.centerXAnchor
+        constraintEqualToAnchor:self.faviconContainerView.centerXAnchor],
+    [activityView.centerYAnchor
+        constraintEqualToAnchor:self.faviconContainerView.centerYAnchor],
+  ]];
+  [activityView startAnimating];
+  activityView.backgroundColor = [UIColor whiteColor];
+}
+
+- (void)stopAnimatingActivityIndicator {
+  [self.activityIndicatorView stopAnimating];
+  [self.activityIndicatorView removeFromSuperview];
+  self.activityIndicatorView = nil;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
index fe9056e..100d3ec1 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_url_item_unittest.mm
@@ -122,3 +122,88 @@
   EXPECT_NSEQ(base::SysUTF8ToNSString(kURL.host()), url_cell.titleLabel.text);
   EXPECT_NSEQ(kSupplementalURLText, url_cell.URLLabel.text);
 }
+
+// Tests that the third row text is shown when the other two rows are shown.
+TEST_F(TableViewURLItemTest, ThirdRowText) {
+  NSString* const kTitle = @"Title";
+  const GURL kURL("https://www.google.com");
+  NSString* const kThirdRowText = @"third-row";
+
+  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
+  item.title = kTitle;
+  item.URL = [[CrURL alloc] initWithGURL:kURL];
+  item.thirdRowText = kThirdRowText;
+
+  id cell = [[[item cellClass] alloc] init];
+  ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
+  [item configureCell:cell withStyler:styler];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+  EXPECT_NSEQ(kThirdRowText,
+              base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.text);
+  EXPECT_FALSE(
+      base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.hidden);
+}
+
+// Tests that the third row text is not shown when the second row is not shown.
+TEST_F(TableViewURLItemTest, ThirdRowTextNotShown) {
+  NSString* const kTitle = @"Title";
+  NSString* const kThirdRowText = @"third-row";
+
+  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
+  item.title = kTitle;
+  item.thirdRowText = kThirdRowText;
+
+  id cell = [[[item cellClass] alloc] init];
+  ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
+  [item configureCell:cell withStyler:styler];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+  EXPECT_TRUE(base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.hidden);
+}
+
+// Tests that the third row text is shown in chosen color.
+TEST_F(TableViewURLItemTest, ThirdRowTextColor) {
+  NSString* const kTitle = @"Title";
+  const GURL kURL("https://www.google.com");
+  NSString* const kThirdRowText = @"third-row";
+  UIColor* const kExpectedColor = UIColor.greenColor;
+
+  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
+  item.title = kTitle;
+  item.URL = [[CrURL alloc] initWithGURL:kURL];
+  item.thirdRowText = kThirdRowText;
+  item.thirdRowTextColor = kExpectedColor;
+
+  id cell = [[[item cellClass] alloc] init];
+  ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
+  styler.cellDetailColor = UIColor.redColor;
+  [item configureCell:cell withStyler:styler];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+  EXPECT_NSEQ(
+      kExpectedColor,
+      base::mac::ObjCCast<TableViewURLCell>(cell).thirdRowLabel.textColor);
+}
+
+// Tests that the third row text is included in the accessibility label.
+TEST_F(TableViewURLItemTest, ThirdRowTextAccessibilityLabel) {
+  NSString* const kTitle = @"Title";
+  NSString* const kDetail = @"Detail";
+  NSString* const kThirdRowText = @"third-row";
+  NSString* const kMetadataText = @"Metadata";
+  NSString* const kExpectedAccessibilityText =
+      @"Title, Detail, third-row, Metadata";
+
+  TableViewURLItem* item = [[TableViewURLItem alloc] initWithType:0];
+  item.title = kTitle;
+  item.detailText = kDetail;
+  item.thirdRowText = kThirdRowText;
+  item.metadata = kMetadataText;
+
+  id cell = [[[item cellClass] alloc] init];
+  ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
+  [item configureCell:cell withStyler:styler];
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+
+  UITableViewCell* tableViewCell = base::mac::ObjCCast<UITableViewCell>(cell);
+  tableViewCell.accessibilityLabel = nil;
+  EXPECT_NSEQ(kExpectedAccessibilityText, tableViewCell.accessibilityLabel);
+}
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 2db5539c0..3ad24f0 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e25eb1a60148ccd084207d96e564273e706aa661
\ No newline at end of file
+7f608e9492078a1f22ed12561f1982e8113f960c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 40f2141..9262cee 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-0ac249240a4f8f20d63d790aa2531fab0243e9ea
\ No newline at end of file
+690aeab35e6013694c0dd4a6666ee2ea7ff1c04a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index f4cb630..bd3fa1c 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b721ea9d14be2f46ea4865fbcee2690728067611
\ No newline at end of file
+fc8cfc68cafa4c9522ec5e018588567c1b4d38ac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 1849f4ec..567c083 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f5d910ab439f5bf4f465e43a1593c76db4178117
\ No newline at end of file
+f2b0a90a2cb66e339a35669a7a026ce67d35e145
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index cc6dca3..9b4ef34 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b4466c779fbff53fbd6c7f998faa9626cc2f2f13
\ No newline at end of file
+eace1fc4900032677b24b3356ec525fef5c608c6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 95fe566..cfa4f2b1 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-c05399c014dfbfa186d200c0cfcbaa490e72bcaa
\ No newline at end of file
+6c21d5a51c8f31492e15766ad0e01a0f196263da
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index ce673d0..2cf8eaf 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-d161c21584624770e57fe17f7bdb6028b484c05c
\ No newline at end of file
+82028e6abe7880a544f84c0652dc8980d02f8821
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 3e88de2..40243f78 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-7640e5dd1d544b5d66db7b6cd7b458ab7e5c4ba7
\ No newline at end of file
+bbafa8e495b91bdc8a7766befd5895a8a222f0df
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 66eb0d12..36e1f5a 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-5bc5a9c070289fe262401290055b3203425fbc0f
\ No newline at end of file
+4bb09874b98ff7d953a469a5a04c5237cea048bf
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index f63974f..ba6c018 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-de0a803543bd8e50eb264e25ea4287cd58bdaf1d
\ No newline at end of file
+4585849047939207e3fee1a12be51a2ee0626728
\ No newline at end of file
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc
index c5e6d26..6490f6e 100644
--- a/media/audio/cras/audio_manager_cras.cc
+++ b/media/audio/cras/audio_manager_cras.cc
@@ -79,42 +79,178 @@
   }
 }
 
+// Checks if a system AEC with a specific group ID is flagged to be deactivated
+// by the field trial.
+bool IsSystemAecDeactivated(int aec_group_id) {
+  return base::GetFieldTrialParamByFeatureAsBool(
+      features::kCrOSSystemAECDeactivatedGroups,
+      base::NumberToString(aec_group_id), false);
+}
+
+// Checks if the board with `aec_group_id` is flagged by the field trial to not
+// allow using DSP-based AEC effect.
+bool IsDspBasedAecDeactivated(int aec_group_id) {
+  return base::GetFieldTrialParamByFeatureAsBool(
+             features::kCrOSDspBasedAecDeactivatedGroups,
+             base::NumberToString(aec_group_id), false) ||
+         !base::FeatureList::IsEnabled(features::kCrOSDspBasedAecAllowed);
+}
+
+// Checks if the board with `aec_group_id` is flagged by the field trial to not
+// allow using DSP-based NS effect.
+bool IsDspBasedNsDeactivated(int aec_group_id) {
+  return base::GetFieldTrialParamByFeatureAsBool(
+             features::kCrOSDspBasedNsDeactivatedGroups,
+             base::NumberToString(aec_group_id), false) ||
+         !base::FeatureList::IsEnabled(features::kCrOSDspBasedNsAllowed);
+}
+
+// Checks if the board with `aec_group_id` is flagged by the field trial to not
+// allow using DSP-based AGC effect.
+bool IsDspBasedAgcDeactivated(int aec_group_id) {
+  return base::GetFieldTrialParamByFeatureAsBool(
+             features::kCrOSDspBasedAgcDeactivatedGroups,
+             base::NumberToString(aec_group_id), false) ||
+         !base::FeatureList::IsEnabled(features::kCrOSDspBasedAgcAllowed);
+}
+
+// Specifies which DSP-based effects are allowed based on media constraints and
+// any finch field trials.
+void SetAllowedDspBasedEffects(int aec_group_id, AudioParameters& params) {
+  int effects = params.effects();
+
+  // Allow AEC to be applied by CRAS on DSP if the AEC is active in CRAS and if
+  // using the AEC on DSP has not been deactivated by any field trials.
+  if ((effects & AudioParameters::ECHO_CANCELLER) &&
+      !IsDspBasedAecDeactivated(aec_group_id)) {
+    effects = effects | AudioParameters::ALLOW_DSP_ECHO_CANCELLER;
+  } else {
+    effects = effects & ~AudioParameters::ALLOW_DSP_ECHO_CANCELLER;
+  }
+
+  // Allow NS to be applied by CRAS on DSP if the NS is active in CRAS and if
+  // using the NS on DSP has not been deactivated by any field trials.
+  if ((effects & AudioParameters::NOISE_SUPPRESSION) &&
+      !IsDspBasedNsDeactivated(aec_group_id)) {
+    effects = effects | AudioParameters::ALLOW_DSP_NOISE_SUPPRESSION;
+  } else {
+    effects = effects & ~AudioParameters::ALLOW_DSP_NOISE_SUPPRESSION;
+  }
+
+  // Allow AGC to be applied by CRAS on DSP if the AGC is active in CRAS and if
+  // using the AGC on DSP has not been deactivated by any field trials.
+  if ((effects & AudioParameters::AUTOMATIC_GAIN_CONTROL) &&
+      !IsDspBasedAgcDeactivated(aec_group_id)) {
+    effects = effects | AudioParameters::ALLOW_DSP_AUTOMATIC_GAIN_CONTROL;
+  } else {
+    effects = effects & ~AudioParameters::ALLOW_DSP_AUTOMATIC_GAIN_CONTROL;
+  }
+
+  params.set_effects(effects);
+}
+
+
+// Collects flags values for whether, and in what way, the AEC, NS or AGC
+// effects should be enforced in spite of them not being flagged as supported by
+// the board.
+void RetrieveSystemEffectFeatures(bool& enforce_system_aec,
+                                  bool& enforce_system_ns,
+                                  bool& enforce_system_agc,
+                                  bool& tuned_system_aec_allowed) {
+  const bool enforce_system_aec_ns_agc_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNsAgc);
+  const bool enforce_system_aec_ns_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNs);
+  const bool enforce_system_aec_agc_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecAgc);
+  const bool enforce_system_aec_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAec);
+
+  enforce_system_aec =
+      enforce_system_aec_feature || enforce_system_aec_ns_agc_feature ||
+      enforce_system_aec_ns_feature || enforce_system_aec_agc_feature;
+  enforce_system_ns =
+      enforce_system_aec_ns_agc_feature || enforce_system_aec_ns_feature;
+  enforce_system_agc =
+      enforce_system_aec_ns_agc_feature || enforce_system_aec_agc_feature;
+
+  tuned_system_aec_allowed =
+      base::FeatureList::IsEnabled(features::kCrOSSystemAEC);
+}
+
+AudioParameters AudioManagerCras::GetStreamParametersForSystem(
+    int user_buffer_size) {
+  AudioParameters params(
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
+      kDefaultSampleRate, user_buffer_size,
+      AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
+                                            limits::kMaxAudioBufferSize));
+
+  bool enforce_system_aec;
+  bool enforce_system_ns;
+  bool enforce_system_agc;
+  bool tuned_system_aec_allowed;
+  RetrieveSystemEffectFeatures(enforce_system_aec, enforce_system_ns,
+                               enforce_system_agc, tuned_system_aec_allowed);
+
+  // Activation of the system AEC. Allow experimentation with system AEC with
+  // all devices, but enable it by default on devices that actually support it.
+  params.set_effects(params.effects() |
+                     AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
+
+  // Rephrase the field aec_supported to properly reflect its meaning in this
+  // context (since it currently signals whether an CrAS APM with tuned settings
+  // is available).
+  // TODO(crbug.com/1307680): add unit tests and caching cras_util_ results.
+  const bool tuned_system_apm_available = cras_util_->CrasGetAecSupported();
+
+  // Don't use the system AEC if it is deactivated for this group ID. Also never
+  // activate NS nor AGC for this board if the AEC is not activated, since this
+  // will cause issues for the Browser AEC.
+  bool use_system_aec =
+      (tuned_system_apm_available && tuned_system_aec_allowed) ||
+      enforce_system_aec;
+
+  // TODO(hychao): query from CRAS
+  bool system_ns_supported = true;
+  bool system_agc_supported = true;
+
+  int aec_group_id = cras_util_->CrasGetAecGroupId();
+  if (!use_system_aec || IsSystemAecDeactivated(aec_group_id)) {
+    SetAllowedDspBasedEffects(aec_group_id, params);
+    return params;
+  }
+
+  // Activation of the system AEC.
+  params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER);
+
+  // Don't use system NS or AGC if the AEC has board-specific tunings.
+  if (!tuned_system_apm_available) {
+    // Activation of the system NS.
+    if (system_ns_supported || enforce_system_ns) {
+      params.set_effects(params.effects() | AudioParameters::NOISE_SUPPRESSION);
+    }
+
+    // Activation of the system AGC.
+    if (system_agc_supported || enforce_system_agc) {
+      params.set_effects(params.effects() |
+                         AudioParameters::AUTOMATIC_GAIN_CONTROL);
+    }
+  }
+
+  SetAllowedDspBasedEffects(aec_group_id, params);
+  return params;
+}
+
 AudioParameters AudioManagerCras::GetInputStreamParameters(
     const std::string& device_id) {
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
 
   int user_buffer_size = GetUserBufferSize();
-  int buffer_size =
+  user_buffer_size =
       user_buffer_size ? user_buffer_size : kDefaultInputBufferSize;
 
-  AudioParameters params(
-      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
-      kDefaultSampleRate, buffer_size,
-      AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
-                                            limits::kMaxAudioBufferSize));
-
-  // Allow experimentation with system echo cancellation with all devices,
-  // but enable it by default on devices that actually support it.
-  params.set_effects(params.effects() |
-                     AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
-  if (base::FeatureList::IsEnabled(features::kCrOSSystemAEC)) {
-    if (cras_util_->CrasGetAecSupported()) {
-      const int32_t aec_group_id = cras_util_->CrasGetAecGroupId();
-
-      // Check if the system AEC has a group ID which is flagged to be
-      // deactivated by the field trial.
-      const bool system_aec_deactivated =
-          base::GetFieldTrialParamByFeatureAsBool(
-              features::kCrOSSystemAECDeactivatedGroups,
-              base::NumberToString(aec_group_id), false);
-
-      if (!system_aec_deactivated) {
-        params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER);
-      }
-    }
-  }
-
-  return params;
+  return GetStreamParametersForSystem(user_buffer_size);
 }
 
 std::string AudioManagerCras::GetDefaultInputDeviceID() {
diff --git a/media/audio/cras/audio_manager_cras.h b/media/audio/cras/audio_manager_cras.h
index da2302f..db042649 100644
--- a/media/audio/cras/audio_manager_cras.h
+++ b/media/audio/cras/audio_manager_cras.h
@@ -46,6 +46,11 @@
   bool IsDefault(const std::string& device_id, bool is_input) override;
   enum CRAS_CLIENT_TYPE GetClientType() override;
 
+  // Produces AudioParameters for the system, including audio processing
+  // capabilities tailored for the system,
+  AudioParameters GetStreamParametersForSystem(
+      int user_buffer_size);
+
  protected:
   AudioParameters GetPreferredOutputStreamParameters(
       const std::string& output_device_id,
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index e74c63a..5bab781 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -73,6 +73,7 @@
     "audio_power_monitor.h",
     "audio_processing.cc",
     "audio_processing.h",
+    "audio_processor_controls.h",
     "audio_pull_fifo.cc",
     "audio_pull_fifo.h",
     "audio_push_fifo.cc",
diff --git a/media/base/audio_processing.h b/media/base/audio_processing.h
index 95fda4b1..ca80199 100644
--- a/media/base/audio_processing.h
+++ b/media/base/audio_processing.h
@@ -9,7 +9,6 @@
 
 #include "build/build_config.h"
 #include "media/base/media_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace media {
 
@@ -90,13 +89,6 @@
   std::string ToString() const;
 };
 
-// This struct contains audio processing metrics that are reported by the audio
-// service.
-struct MEDIA_EXPORT AudioProcessingStats {
-  absl::optional<double> echo_return_loss;
-  absl::optional<double> echo_return_loss_enhancement;
-};
-
 }  // namespace media
 
 #endif  // MEDIA_BASE_AUDIO_PROCESSING_H_
diff --git a/media/base/audio_processor_controls.h b/media/base/audio_processor_controls.h
new file mode 100644
index 0000000..b429f803
--- /dev/null
+++ b/media/base/audio_processor_controls.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_AUDIO_PROCESSOR_CONTROLS_H_
+#define MEDIA_BASE_AUDIO_PROCESSOR_CONTROLS_H_
+
+#include "base/callback.h"
+#include "media/base/media_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// Audio processing metrics that are reported by the audio service.
+struct MEDIA_EXPORT AudioProcessingStats {
+  absl::optional<double> echo_return_loss;
+  absl::optional<double> echo_return_loss_enhancement;
+};
+
+// Interactions with the audio service.
+class MEDIA_EXPORT AudioProcessorControls {
+ public:
+  using GetStatsCB =
+      base::OnceCallback<void(const media::AudioProcessingStats& stats)>;
+
+  // Request the latest stats from the audio processor. Stats are returned
+  // asynchronously through |callback|.
+  virtual void GetStats(GetStatsCB callback) = 0;
+
+  // Set preferred number of microphone channels.
+  virtual void SetPreferredNumCaptureChannels(
+      int32_t num_preferred_channels) = 0;
+
+ protected:
+  virtual ~AudioProcessorControls() = default;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_AUDIO_PROCESSOR_CONTROLS_H_
diff --git a/media/base/key_system_properties.h b/media/base/key_system_properties.h
index 6eeb877..f80ba87 100644
--- a/media/base/key_system_properties.h
+++ b/media/base/key_system_properties.h
@@ -82,7 +82,7 @@
     std::vector<std::unique_ptr<KeySystemProperties>>;
 
 using GetSupportedKeySystemsCB =
-    base::OnceCallback<void(KeySystemPropertiesVector)>;
+    base::RepeatingCallback<void(KeySystemPropertiesVector)>;
 
 }  // namespace media
 
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index adf33338..d2a1916 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -276,7 +276,6 @@
 
   // Implementation of KeySystems interface.
   void UpdateIfNeeded(base::OnceClosure done_cb) override;
-  bool IsUpToDate() override;
   std::string GetBaseKeySystemName(
       const std::string& key_system) const override;
   bool IsSupportedKeySystem(const std::string& key_system) const override;
@@ -323,7 +322,6 @@
   KeySystemsImpl();
   ~KeySystemsImpl() override;
 
-  bool IsUpdateNeeded();
   void UpdateSupportedKeySystems();
   void OnSupportedKeySystemsUpdated(KeySystemPropertiesVector key_systems);
   void ProcessSupportedKeySystems(KeySystemPropertiesVector key_systems);
@@ -387,10 +385,6 @@
     update_callbacks_.Notify();
 }
 
-bool KeySystemsImpl::IsUpdateNeeded() {
-  return GetMediaClient() && GetMediaClient()->IsKeySystemsUpdateNeeded();
-}
-
 void KeySystemsImpl::UpdateSupportedKeySystems() {
   DCHECK(!is_updating_);
   is_updating_ = true;
@@ -401,28 +395,18 @@
   }
 
   GetMediaClient()->GetSupportedKeySystems(
-      base::BindOnce(&KeySystemsImpl::OnSupportedKeySystemsUpdated,
-                     weak_factory_.GetWeakPtr()));
+      base::BindRepeating(&KeySystemsImpl::OnSupportedKeySystemsUpdated,
+                          weak_factory_.GetWeakPtr()));
 }
 
 void KeySystemsImpl::UpdateIfNeeded(base::OnceClosure done_cb) {
   if (is_updating_) {
+    // The callback will be resolved in OnSupportedKeySystemsUpdated().
     update_callbacks_.AddUnsafe(std::move(done_cb));
     return;
   }
 
-  DCHECK(update_callbacks_.empty());
-  if (!IsUpdateNeeded()) {
-    std::move(done_cb).Run();
-    return;
-  }
-
-  update_callbacks_.AddUnsafe(std::move(done_cb));
-  UpdateSupportedKeySystems();
-}
-
-bool KeySystemsImpl::IsUpToDate() {
-  return !is_updating_ && !IsUpdateNeeded();
+  std::move(done_cb).Run();
 }
 
 SupportedCodecs KeySystemsImpl::GetCodecMaskForMimeType(
@@ -483,7 +467,6 @@
     KeySystemPropertiesVector key_systems) {
   DVLOG(1) << __func__;
 
-  DCHECK(is_updating_);
   is_updating_ = false;
 
   // Clear Key is always supported.
@@ -550,6 +533,7 @@
 
 const KeySystemProperties* KeySystemsImpl::GetKeySystemProperties(
     const std::string& key_system) const {
+  DCHECK(!is_updating_);
   for (const auto& entry : key_system_properties_map_) {
     const auto& base_key_system = entry.first;
     const auto* properties = entry.second.get();
diff --git a/media/base/key_systems.h b/media/base/key_systems.h
index b13bf6d..7165a17 100644
--- a/media/base/key_systems.h
+++ b/media/base/key_systems.h
@@ -35,9 +35,6 @@
   // out of date. Calls the `done_cb` when done.
   virtual void UpdateIfNeeded(base::OnceClosure done_cb) = 0;
 
-  // Whether the list of available key systems is up to date.
-  virtual bool IsUpToDate() = 0;
-
   // Gets the base key system name, e.g. "org.chromium.foo".
   virtual std::string GetBaseKeySystemName(
       const std::string& key_system) const = 0;
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc
index cd4b792..7a9b977 100644
--- a/media/base/key_systems_unittest.cc
+++ b/media/base/key_systems_unittest.cc
@@ -247,16 +247,11 @@
   ~TestMediaClient() override;
 
   // MediaClient implementation.
-  bool IsKeySystemsUpdateNeeded() final;
   void GetSupportedKeySystems(GetSupportedKeySystemsCB cb) final;
   bool IsSupportedAudioType(const media::AudioType& type) final;
   bool IsSupportedVideoType(const media::VideoType& type) final;
   bool IsSupportedBitstreamAudioCodec(AudioCodec codec) final;
 
-  // Helper function to test the case where IsKeySystemsUpdateNeeded() is true
-  // after GetSupportedKeySystems() is called.
-  void SetKeySystemsUpdateNeeded();
-
   // Helper function to disable "kExternal" key system support so that we can
   // test the key system update case.
   void DisableExternalKeySystemSupport();
@@ -265,28 +260,21 @@
   GetAudioRendererAlgorithmParameters(AudioParameters audio_parameters) final;
 
  private:
-  bool is_update_needed_ = true;
+  KeySystemPropertiesVector GetSupportedKeySystemsInternal();
+
+  GetSupportedKeySystemsCB get_supported_key_systems_cb_;
   bool supports_external_key_system_ = true;
 };
 
 TestMediaClient::TestMediaClient() = default;
 TestMediaClient::~TestMediaClient() = default;
 
-bool TestMediaClient::IsKeySystemsUpdateNeeded() {
-  return is_update_needed_;
-}
-
 void TestMediaClient::GetSupportedKeySystems(GetSupportedKeySystemsCB cb) {
-  DCHECK(is_update_needed_);
-  KeySystemPropertiesVector key_systems;
+  // Save the callback for future updates.
+  DCHECK(!get_supported_key_systems_cb_);
+  get_supported_key_systems_cb_ = cb;
 
-  key_systems.emplace_back(new AesKeySystemProperties(kUsesAes));
-
-  if (supports_external_key_system_)
-    key_systems.emplace_back(new ExternalKeySystemProperties());
-
-  is_update_needed_ = false;
-  std::move(cb).Run(std::move(key_systems));
+  get_supported_key_systems_cb_.Run(GetSupportedKeySystemsInternal());
 }
 
 bool TestMediaClient::IsSupportedAudioType(const media::AudioType& type) {
@@ -301,12 +289,9 @@
   return false;
 }
 
-void TestMediaClient::SetKeySystemsUpdateNeeded() {
-  is_update_needed_ = true;
-}
-
 void TestMediaClient::DisableExternalKeySystemSupport() {
   supports_external_key_system_ = false;
+  get_supported_key_systems_cb_.Run(GetSupportedKeySystemsInternal());
 }
 
 absl::optional<::media::AudioRendererAlgorithmParameters>
@@ -315,6 +300,17 @@
   return absl::nullopt;
 }
 
+KeySystemPropertiesVector TestMediaClient::GetSupportedKeySystemsInternal() {
+  KeySystemPropertiesVector key_systems;
+
+  key_systems.emplace_back(new AesKeySystemProperties(kUsesAes));
+
+  if (supports_external_key_system_)
+    key_systems.emplace_back(new ExternalKeySystemProperties());
+
+  return key_systems;
+}
+
 }  // namespace
 
 class KeySystemsTest : public testing::Test {
@@ -365,7 +361,6 @@
     base::RunLoop run_loop;
     KeySystems::GetInstance()->UpdateIfNeeded(run_loop.QuitClosure());
     run_loop.Run();
-    ASSERT_TRUE(KeySystems::GetInstance()->IsUpToDate());
   }
 
   ~KeySystemsTest() override {
@@ -376,13 +371,11 @@
   }
 
   void UpdateClientKeySystems() {
-    test_media_client_.SetKeySystemsUpdateNeeded();
     test_media_client_.DisableExternalKeySystemSupport();
 
     base::RunLoop run_loop;
     KeySystems::GetInstance()->UpdateIfNeeded(run_loop.QuitClosure());
     run_loop.Run();
-    ASSERT_TRUE(KeySystems::GetInstance()->IsUpToDate());
   }
 
   typedef std::vector<std::string> CodecVector;
diff --git a/media/base/media_client.h b/media/base/media_client.h
index 7fc3ddf..5e8ac795 100644
--- a/media/base/media_client.h
+++ b/media/base/media_client.h
@@ -43,11 +43,6 @@
   // Adds properties for supported key systems.
   virtual void GetSupportedKeySystems(GetSupportedKeySystemsCB cb) = 0;
 
-  // Returns whether client key systems properties should be updated.
-  // TODO(xhwang): Refactor this to a proper change "observer" API that is
-  // less fragile (don't assume GetSupportedKeySystems has just one caller).
-  virtual bool IsKeySystemsUpdateNeeded() = 0;
-
   // Returns true if the given audio config is supported.
   virtual bool IsSupportedAudioType(const AudioType& type) = 0;
 
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index dbc853c..a5e0334c 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -858,7 +858,6 @@
 
   // MediaClient implementation.
   MOCK_METHOD1(GetSupportedKeySystems, void(GetSupportedKeySystemsCB cb));
-  MOCK_METHOD0(IsKeySystemsUpdateNeeded, bool());
   MOCK_METHOD1(IsSupportedAudioType, bool(const media::AudioType& type));
   MOCK_METHOD1(IsSupportedVideoType, bool(const media::VideoType& type));
   MOCK_METHOD1(IsSupportedBitstreamAudioCodec, bool(media::AudioCodec codec));
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index b883e0f..b9ce5f0 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -1448,9 +1448,6 @@
     return false;
   }
 
-  // Make sure new formats are properly accounted for in the method.
-  static_assert(PIXEL_FORMAT_MAX == 33,
-                "Added pixel format, please review AreSizesValid()");
   switch (frame_control_type) {
     case FrameControlType::kNone:
       // Check that software-allocated buffer formats are not empty.
diff --git a/media/gpu/chromeos/image_processor_factory.cc b/media/gpu/chromeos/image_processor_factory.cc
index 48cef57..b30b400 100644
--- a/media/gpu/chromeos/image_processor_factory.cc
+++ b/media/gpu/chromeos/image_processor_factory.cc
@@ -159,6 +159,41 @@
   }
   return nullptr;
 }
+
+std::unique_ptr<ImageProcessor> CreateLibYUVImageProcessorWithInputCandidates(
+    const std::vector<PixelLayoutCandidate>& input_candidates,
+    const gfx::Rect& input_visible_rect,
+    const gfx::Size& output_size,
+    size_t num_buffers,
+    scoped_refptr<base::SequencedTaskRunner> client_task_runner,
+    ImageProcessorFactory::PickFormatCB out_format_picker,
+    ImageProcessor::ErrorCB error_cb) {
+  if (input_candidates.size() != 1)
+    return nullptr;
+
+  if (input_candidates[0].fourcc != Fourcc(Fourcc::MM21))
+    return nullptr;
+
+  std::vector<Fourcc> supported_output_formats =
+      LibYUVImageProcessorBackend::GetSupportedOutputFormats(
+          Fourcc(Fourcc::MM21));
+  auto output_format =
+      out_format_picker.Run(supported_output_formats, Fourcc(Fourcc::NV12));
+
+  if (!output_format)
+    return nullptr;
+
+  ImageProcessor::PortConfig input_config(
+      Fourcc(Fourcc::MM21), input_candidates[0].size, /*planes=*/{},
+      input_visible_rect, {VideoFrame::STORAGE_DMABUFS});
+  ImageProcessor::PortConfig output_config(
+      *output_format, output_size, /*planes=*/{}, gfx::Rect(output_size),
+      {VideoFrame::STORAGE_GPU_MEMORY_BUFFER});
+  return ImageProcessor::Create(
+      base::BindRepeating(&LibYUVImageProcessorBackend::Create), input_config,
+      output_config, ImageProcessor::OutputMode::IMPORT, VIDEO_ROTATION_0,
+      std::move(error_cb), std::move(client_task_runner));
+}
 #endif  // BUILDFLAG(USE_V4L2_CODEC) && !BUILDFLAG(USE_VAAPI)
 
 }  // namespace
@@ -216,6 +251,12 @@
       out_format_picker, error_cb);
   if (processor)
     return processor;
+
+  processor = CreateLibYUVImageProcessorWithInputCandidates(
+      input_candidates, input_visible_rect, output_size, num_buffers,
+      client_task_runner, out_format_picker, error_cb);
+  if (processor)
+    return processor;
 #endif  // BUILDFLAG(USE_V4L2_CODEC)
 
   // TODO(crbug.com/1004727): Implement LibYUVImageProcessorBackend. When doing
diff --git a/media/gpu/chromeos/libyuv_image_processor_backend.cc b/media/gpu/chromeos/libyuv_image_processor_backend.cc
index 6245a80..f73d66e 100644
--- a/media/gpu/chromeos/libyuv_image_processor_backend.cc
+++ b/media/gpu/chromeos/libyuv_image_processor_backend.cc
@@ -100,38 +100,39 @@
   kRotation,
 };
 
+static constexpr struct {
+  uint32_t input;
+  uint32_t output;
+  Transform transform;
+  SupportResult support_result;
+} kSupportFormatConversionArray[] = {
+#define CONV(in, out, trans, result) \
+  {Fourcc::in, Fourcc::out, Transform::trans, SupportResult::result}
+    // Conversion.
+    CONV(NV12, NV12, kConversion, Supported),
+    CONV(YM16, NV12, kConversion, Supported),
+    CONV(YM16, YU12, kConversion, Supported),
+    CONV(YU12, NV12, kConversion, Supported),
+    CONV(YU12, YU12, kConversion, Supported),
+    CONV(YUYV, NV12, kConversion, Supported),
+    CONV(YUYV, YU12, kConversion, Supported),
+    CONV(YV12, NV12, kConversion, Supported),
+    CONV(MM21, NV12, kConversion, Supported),
+    // Scaling.
+    CONV(NV12, NV12, kScaling, Supported),
+    CONV(YM16, NV12, kScaling, SupportedWithNV12Pivot),
+    CONV(YM16, YU12, kScaling, SupportedWithI420Pivot),
+    CONV(YU12, YU12, kScaling, Supported),
+    CONV(YUYV, NV12, kScaling, SupportedWithNV12Pivot),
+    CONV(YUYV, YU12, kScaling, SupportedWithI420Pivot),
+    // Rotating.
+    CONV(NV12, NV12, kRotation, SupportedWithI420Pivot),
+#undef CONV
+};
+
 SupportResult IsConversionSupported(Fourcc input_fourcc,
                                     Fourcc output_fourcc,
                                     Transform transform) {
-  static constexpr struct {
-    uint32_t input;
-    uint32_t output;
-    Transform transform;
-    SupportResult support_result;
-  } kSupportFormatConversionArray[] = {
-#define CONV(in, out, trans, result) \
-  {Fourcc::in, Fourcc::out, Transform::trans, SupportResult::result}
-      // Conversion.
-      CONV(NV12, NV12, kConversion, Supported),
-      CONV(YM16, NV12, kConversion, Supported),
-      CONV(YM16, YU12, kConversion, Supported),
-      CONV(YU12, NV12, kConversion, Supported),
-      CONV(YU12, YU12, kConversion, Supported),
-      CONV(YUYV, NV12, kConversion, Supported),
-      CONV(YUYV, YU12, kConversion, Supported),
-      CONV(YV12, NV12, kConversion, Supported),
-      // Scaling.
-      CONV(NV12, NV12, kScaling, Supported),
-      CONV(YM16, NV12, kScaling, SupportedWithNV12Pivot),
-      CONV(YM16, YU12, kScaling, SupportedWithI420Pivot),
-      CONV(YU12, YU12, kScaling, Supported),
-      CONV(YUYV, NV12, kScaling, SupportedWithNV12Pivot),
-      CONV(YUYV, YU12, kScaling, SupportedWithI420Pivot),
-      // Rotating.
-      CONV(NV12, NV12, kRotation, SupportedWithI420Pivot),
-#undef CONV
-  };
-
   const auto single_input_fourcc = input_fourcc.ToSinglePlanar();
   const auto single_output_fourcc = output_fourcc.ToSinglePlanar();
   if (!single_input_fourcc || !single_output_fourcc)
@@ -407,6 +408,10 @@
         return LIBYUV_FUNC(I420ToNV12, Y_V_U_DATA(input), Y_UV_DATA(output));
 
       case PIXEL_FORMAT_NV12:
+        // MM21 mode.
+        if (input_config_.fourcc == Fourcc(Fourcc::MM21))
+          return LIBYUV_FUNC(MM21ToNV12, Y_UV_DATA(input), Y_UV_DATA(output));
+
         // Rotation mode.
         if (relative_rotation_ != VIDEO_ROTATION_0) {
           // The size of |tmp_buffer| of NV12Rotate() should be
@@ -531,4 +536,16 @@
   return true;
 }
 
+std::vector<Fourcc> LibYUVImageProcessorBackend::GetSupportedOutputFormats(
+    Fourcc input_format) {
+  std::vector<Fourcc> supported_formats;
+  for (const auto& conv : kSupportFormatConversionArray) {
+    if (Fourcc::FromUint32(conv.input) &&
+        *Fourcc::FromUint32(conv.input) == input_format &&
+        Fourcc::FromUint32(conv.output))
+      supported_formats.emplace_back(*Fourcc::FromUint32(conv.output));
+  }
+  return supported_formats;
+}
+
 }  // namespace media
diff --git a/media/gpu/chromeos/libyuv_image_processor_backend.h b/media/gpu/chromeos/libyuv_image_processor_backend.h
index 61d6008c..6a48cf5 100644
--- a/media/gpu/chromeos/libyuv_image_processor_backend.h
+++ b/media/gpu/chromeos/libyuv_image_processor_backend.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "media/gpu/chromeos/fourcc.h"
 #include "media/gpu/chromeos/image_processor_backend.h"
 #include "media/gpu/media_gpu_export.h"
 #include "ui/gfx/geometry/rect.h"
@@ -46,6 +47,8 @@
 
   bool needs_linear_output_buffers() const override;
 
+  static std::vector<Fourcc> GetSupportedOutputFormats(Fourcc input_format);
+
  private:
   LibYUVImageProcessorBackend(
       std::unique_ptr<VideoFrameMapper> input_frame_mapper,
diff --git a/media/gpu/test/video_test_helpers.cc b/media/gpu/test/video_test_helpers.cc
index 9d578ec..9d228f5 100644
--- a/media/gpu/test/video_test_helpers.cc
+++ b/media/gpu/test/video_test_helpers.cc
@@ -9,6 +9,8 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/stl_util.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
@@ -364,9 +366,9 @@
 
 struct AlignedDataHelper::VideoFrameData {
   VideoFrameData() = default;
-  VideoFrameData(mojo::ScopedSharedBufferHandle mojo_handle)
-      : mojo_handle(std::move(mojo_handle)) {}
-  VideoFrameData(gfx::GpuMemoryBufferHandle gmb_handle)
+  explicit VideoFrameData(base::UnsafeSharedMemoryRegion shmem_region)
+      : shmem_region(std::move(shmem_region)) {}
+  explicit VideoFrameData(gfx::GpuMemoryBufferHandle gmb_handle)
       : gmb_handle(std::move(gmb_handle)) {}
 
   VideoFrameData(VideoFrameData&&) = default;
@@ -374,7 +376,7 @@
   VideoFrameData(const VideoFrameData&) = delete;
   VideoFrameData& operator=(const VideoFrameData&) = delete;
 
-  mojo::ScopedSharedBufferHandle mojo_handle;
+  base::UnsafeSharedMemoryRegion shmem_region;
   gfx::GpuMemoryBufferHandle gmb_handle;
 };
 
@@ -486,11 +488,10 @@
         dummy_mailbox, base::DoNothing() /* mailbox_holder_release_cb_ */,
         frame_timestamp);
   } else {
-    const auto& mojo_handle = video_frame_data_[read_frame_index].mojo_handle;
-    auto dup_handle =
-        mojo_handle->Clone(mojo::SharedBufferHandle::AccessMode::READ_WRITE);
-    if (!dup_handle.is_valid()) {
-      LOG(ERROR) << "Failed duplicating mojo handle";
+    const auto& shmem_region = video_frame_data_[read_frame_index].shmem_region;
+    auto dup_region = shmem_region.Duplicate();
+    if (!dup_region.IsValid()) {
+      LOG(ERROR) << "Failed duplicating shmem region";
       return nullptr;
     }
 
@@ -502,10 +503,10 @@
     }
     const size_t video_frame_size =
         layout_->planes().back().offset + layout_->planes().back().size;
+    DCHECK_EQ(video_frame_size, dup_region.GetSize());
     return MojoSharedBufferVideoFrame::Create(
         layout_->format(), layout_->coded_size(), visible_rect_, natural_size_,
-        std::move(dup_handle), video_frame_size, offsets, strides,
-        frame_timestamp);
+        std::move(dup_region), offsets, strides, frame_timestamp);
   }
 }
 
@@ -542,11 +543,11 @@
   const size_t num_planes = VideoFrame::NumPlanes(pixel_format);
   const uint8_t* src_frame_ptr = &stream[0];
   for (size_t i = 0; i < num_frames_; i++) {
-    auto handle = mojo::SharedBufferHandle::Create(video_frame_size);
-    ASSERT_TRUE(handle.is_valid()) << "Failed allocating a handle";
-    auto mapping = handle->Map(video_frame_size);
-    ASSERT_TRUE(!!mapping);
-    uint8_t* buffer = reinterpret_cast<uint8_t*>(mapping.get());
+    auto region = base::UnsafeSharedMemoryRegion::Create(video_frame_size);
+    ASSERT_TRUE(region.IsValid()) << "Failed allocating a region";
+    base::WritableSharedMemoryMapping mapping = region.Map();
+    ASSERT_TRUE(mapping.IsValid());
+    uint8_t* buffer = mapping.GetMemoryAs<uint8_t>();
     for (size_t j = 0; j < num_planes; j++) {
       auto src_plane_layout = src_layout.planes()[j];
       auto dst_plane_layout = layout_->planes()[j];
@@ -557,7 +558,7 @@
                         src_plane_rows[j]);
     }
     src_frame_ptr += src_video_frame_size;
-    video_frame_data_[i] = VideoFrameData(std::move(handle));
+    video_frame_data_[i] = VideoFrameData(std::move(region));
   }
 }
 
diff --git a/media/mojo/clients/win/media_foundation_renderer_client.cc b/media/mojo/clients/win/media_foundation_renderer_client.cc
index 10cfc55..4f79b94 100644
--- a/media/mojo/clients/win/media_foundation_renderer_client.cc
+++ b/media/mojo/clients/win/media_foundation_renderer_client.cc
@@ -453,10 +453,8 @@
   // Set DirectComposition mode and get DirectComposition surface from
   // MediaFoundationRenderer.
   renderer_extension_->GetDCOMPSurface(
-      mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-          base::BindOnce(&MediaFoundationRendererClient::OnDCOMPSurfaceReceived,
-                         weak_factory_.GetWeakPtr()),
-          absl::nullopt, "disconnection error"));
+      base::BindOnce(&MediaFoundationRendererClient::OnDCOMPSurfaceReceived,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void MediaFoundationRendererClient::OnDCOMPSurfaceReceived(
@@ -466,11 +464,9 @@
   DCHECK(has_video_);
   DCHECK(media_task_runner_->BelongsToCurrentThread());
 
+  // The error should've already been handled in MediaFoundationRenderer.
   if (!token) {
-    MEDIA_LOG(ERROR, media_log_) << "GetDCOMPSurface failed: " + error;
-    MediaFoundationRenderer::ReportErrorReason(
-        MediaFoundationRenderer::ErrorReason::kOnDCompSurfaceReceivedError);
-    OnError(PIPELINE_ERROR_COULD_NOT_RENDER);
+    DLOG(ERROR) << "GetDCOMPSurface failed: " + error;
     return;
   }
 
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame.cc b/media/mojo/common/mojo_shared_buffer_video_frame.cc
index 54890bd..c195825 100644
--- a/media/mojo/common/mojo_shared_buffer_video_frame.cc
+++ b/media/mojo/common/mojo_shared_buffer_video_frame.cc
@@ -11,9 +11,10 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace media {
 
@@ -40,9 +41,8 @@
 
   // Allocate a shared memory buffer big enough to hold the desired frame.
   const size_t allocation_size = VideoFrame::AllocationSize(format, coded_size);
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(allocation_size);
-  if (!handle.is_valid()) {
+  auto region = base::UnsafeSharedMemoryRegion::Create(allocation_size);
+  if (!region.IsValid()) {
     DLOG(ERROR) << __func__ << " Unable to allocate memory.";
     return nullptr;
   }
@@ -58,13 +58,13 @@
     //  - Yplane, full size (each element represents a 1x1 block)
     //  - Uplane, quarter size (each element represents a 2x2 block)
     //  - Vplane, quarter size (each element represents a 2x2 block)
-    return Create(
-        format, coded_size, visible_rect, dimensions, std::move(handle),
-        allocation_size,
-        {0 /* y_offset */, static_cast<uint32_t>(coded_size.GetArea()),
-         static_cast<uint32_t>(coded_size.GetArea() * 5 / 4)},
-        {coded_size.width(), coded_size.width() / 2, coded_size.width() / 2},
-        timestamp);
+    const uint32_t offsets[] = {
+        0 /* y_offset */, static_cast<uint32_t>(coded_size.GetArea()),
+        static_cast<uint32_t>(coded_size.GetArea() * 5 / 4)};
+    const int32_t strides[] = {coded_size.width(), coded_size.width() / 2,
+                               coded_size.width() / 2};
+    return Create(format, coded_size, visible_rect, dimensions,
+                  std::move(region), offsets, strides, timestamp);
   } else {
     // |format| is PIXEL_FORMAT_NV12.
     // Create and initialize the frame. As this is NV12 format, the UV plane
@@ -72,11 +72,11 @@
     // as follows:
     //  - Yplane, full size (each element represents a 1x1 block)
     //  - UVplane, full width, half height (each pair represents a 2x2 block)
-    return Create(
-        format, coded_size, visible_rect, dimensions, std::move(handle),
-        allocation_size,
-        {0 /* y_offset */, static_cast<uint32_t>(coded_size.GetArea())},
-        {coded_size.width(), coded_size.width()}, timestamp);
+    const uint32_t offsets[] = {0 /* y_offset */,
+                                static_cast<uint32_t>(coded_size.GetArea())};
+    const int32_t strides[] = {coded_size.width(), coded_size.width()};
+    return Create(format, coded_size, visible_rect, dimensions,
+                  std::move(region), offsets, strides, timestamp);
   }
 }
 
@@ -99,13 +99,16 @@
     aggregate_size += sizes[i];
   }
 
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(aggregate_size);
-  if (!handle->is_valid()) {
+  auto region = base::UnsafeSharedMemoryRegion::Create(aggregate_size);
+  if (!region.IsValid()) {
     DLOG(ERROR) << "Can't create new frame backing memory";
     return nullptr;
   }
-  mojo::ScopedSharedBufferMapping dst_mapping = handle->Map(aggregate_size);
+  base::WritableSharedMemoryMapping dst_mapping = region.Map();
+  if (!dst_mapping.IsValid()) {
+    DLOG(ERROR) << "Can't create map frame backing memory";
+    return nullptr;
+  }
 
   // The data from |frame| may not be consecutive between planes. Copy data into
   // a shared memory buffer which is tightly packed. Padding inside each planes
@@ -113,8 +116,7 @@
   scoped_refptr<MojoSharedBufferVideoFrame> mojo_frame =
       MojoSharedBufferVideoFrame::Create(
           frame.format(), frame.coded_size(), frame.visible_rect(),
-          frame.natural_size(), std::move(handle), aggregate_size,
-          offsets /* don't move, we use it again */, std::move(strides),
+          frame.natural_size(), std::move(region), offsets, strides,
           frame.timestamp());
   CHECK(!!mojo_frame);
 
@@ -151,10 +153,9 @@
     const gfx::Size& coded_size,
     const gfx::Rect& visible_rect,
     const gfx::Size& natural_size,
-    mojo::ScopedSharedBufferHandle handle,
-    size_t data_size,
-    std::vector<uint32_t> offsets,
-    std::vector<int32_t> strides,
+    base::UnsafeSharedMemoryRegion region,
+    base::span<const uint32_t> offsets,
+    base::span<const int32_t> strides,
     base::TimeDelta timestamp) {
   if (!IsValidConfig(format, STORAGE_MOJO_SHARED_BUFFER, coded_size,
                      visible_rect, natural_size)) {
@@ -184,6 +185,7 @@
   // range of an int) due to the IsValidConfig() check above.
   //
   // TODO(sandersd): Allow non-sequential formats.
+  const size_t data_size = region.GetSize();
   std::vector<ColorPlaneLayout> planes(num_planes);
   for (size_t i = 0; i < num_planes; ++i) {
     if (strides[i] < 0) {
@@ -227,8 +229,8 @@
   // Now allocate the frame and initialize it.
   scoped_refptr<MojoSharedBufferVideoFrame> frame(
       new MojoSharedBufferVideoFrame(*layout, visible_rect, natural_size,
-                                     std::move(handle), data_size, timestamp));
-  if (!frame->Init(std::move(offsets))) {
+                                     std::move(region), timestamp));
+  if (!frame->Init(offsets)) {
     DLOG(ERROR) << __func__ << " MojoSharedBufferVideoFrame::Init failed.";
     return nullptr;
   }
@@ -240,23 +242,21 @@
     const VideoFrameLayout& layout,
     const gfx::Rect& visible_rect,
     const gfx::Size& natural_size,
-    mojo::ScopedSharedBufferHandle handle,
-    size_t mapped_size,
+    base::UnsafeSharedMemoryRegion region,
     base::TimeDelta timestamp)
     : VideoFrame(layout,
                  STORAGE_MOJO_SHARED_BUFFER,
                  visible_rect,
                  natural_size,
                  timestamp),
-      shared_buffer_handle_(std::move(handle)),
-      shared_buffer_size_(mapped_size) {
-  DCHECK(shared_buffer_handle_.is_valid());
+      region_(std::move(region)) {
+  DCHECK(region_.IsValid());
 }
 
-bool MojoSharedBufferVideoFrame::Init(std::vector<uint32_t> offsets) {
-  DCHECK(!shared_buffer_mapping_);
-  shared_buffer_mapping_ = shared_buffer_handle_->Map(shared_buffer_size_);
-  if (!shared_buffer_mapping_)
+bool MojoSharedBufferVideoFrame::Init(base::span<const uint32_t> offsets) {
+  DCHECK(!mapping_.IsValid());
+  mapping_ = region_.Map();
+  if (!mapping_.IsValid())
     return false;
   const size_t num_planes = NumPlanes(format());
   DCHECK_EQ(offsets.size(), num_planes);
@@ -271,8 +271,7 @@
   // Call |mojo_shared_buffer_done_cb_| to take ownership of
   // |shared_buffer_handle_|.
   if (mojo_shared_buffer_done_cb_) {
-    std::move(mojo_shared_buffer_done_cb_)
-        .Run(std::move(shared_buffer_handle_), shared_buffer_size_);
+    std::move(mojo_shared_buffer_done_cb_).Run(std::move(region_));
   }
 }
 
@@ -286,12 +285,4 @@
   mojo_shared_buffer_done_cb_ = std::move(mojo_shared_buffer_done_cb);
 }
 
-const mojo::SharedBufferHandle& MojoSharedBufferVideoFrame::Handle() const {
-  return shared_buffer_handle_.get();
-}
-
-size_t MojoSharedBufferVideoFrame::MappedSize() const {
-  return shared_buffer_size_;
-}
-
 }  // namespace media
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame.h b/media/mojo/common/mojo_shared_buffer_video_frame.h
index 8a30bc4..aa1a92f 100644
--- a/media/mojo/common/mojo_shared_buffer_video_frame.h
+++ b/media/mojo/common/mojo_shared_buffer_video_frame.h
@@ -8,13 +8,13 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <vector>
-
 #include "base/callback_forward.h"
+#include "base/containers/span.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
-#include "mojo/public/cpp/system/buffer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -28,8 +28,7 @@
   // Callback called when this object is destructed. Ownership of the shared
   // memory is transferred to the callee.
   using MojoSharedBufferDoneCB =
-      base::OnceCallback<void(mojo::ScopedSharedBufferHandle buffer,
-                              size_t capacity)>;
+      base::OnceCallback<void(base::UnsafeSharedMemoryRegion region)>;
 
   // Creates a new I420 or NV12 frame in shared memory with provided parameters
   // (coded_size() == natural_size() == visible_rect()), or returns nullptr.
@@ -58,29 +57,25 @@
       const gfx::Size& coded_size,
       const gfx::Rect& visible_rect,
       const gfx::Size& natural_size,
-      mojo::ScopedSharedBufferHandle handle,
-      size_t mapped_size,
-      std::vector<uint32_t> offsets,
-      std::vector<int32_t> strides,
+      base::UnsafeSharedMemoryRegion region,
+      base::span<const uint32_t> offsets,
+      base::span<const int32_t> strides,
       base::TimeDelta timestamp);
 
   MojoSharedBufferVideoFrame(const MojoSharedBufferVideoFrame&) = delete;
   MojoSharedBufferVideoFrame& operator=(const MojoSharedBufferVideoFrame&) =
       delete;
 
-  // Returns the offsets relative to the start of |shared_buffer| for the
+  // Returns the offsets relative to the start of the shmem mapping for the
   // |plane| specified.
   size_t PlaneOffset(size_t plane) const;
 
-  // Returns a reference to the mojo shared memory handle. Caller should
-  // duplicate the handle if they want to extend the lifetime of the buffer.
-  const mojo::SharedBufferHandle& Handle() const;
+  // Callers can `Duplicate()` the mapping to extend the lifetime of the region.
+  const base::UnsafeSharedMemoryRegion& shmem_region() const { return region_; }
 
-  // Returns the size of the shared memory.
-  size_t MappedSize() const;
-
-  // Sets the callback to be called to free the shared buffer. If not null,
-  // it is called on destruction, and is passed ownership of |handle|.
+  // Sets the callback to be called to free the shmem region. If not null,
+  // the callback is called when `this` is destroyed, and ownership of
+  // `region_` is transferred to it.
   void SetMojoSharedBufferDoneCB(
       MojoSharedBufferDoneCB mojo_shared_buffer_done_cb);
 
@@ -90,22 +85,21 @@
   MojoSharedBufferVideoFrame(const VideoFrameLayout& layout,
                              const gfx::Rect& visible_rect,
                              const gfx::Size& natural_size,
-                             mojo::ScopedSharedBufferHandle handle,
-                             size_t mapped_size,
+                             base::UnsafeSharedMemoryRegion region,
                              base::TimeDelta timestamp);
   ~MojoSharedBufferVideoFrame() override;
 
   // Initializes the MojoSharedBufferVideoFrame by creating a mapping onto
   // the shared memory, and then setting offsets as specified.
-  bool Init(std::vector<uint32_t> offsets);
+  bool Init(base::span<const uint32_t> offsets);
 
-  uint8_t* shared_buffer_data() {
-    return reinterpret_cast<uint8_t*>(shared_buffer_mapping_.get());
-  }
+  uint8_t* shared_buffer_data() { return mapping_.GetMemoryAs<uint8_t>(); }
 
-  mojo::ScopedSharedBufferHandle shared_buffer_handle_;
-  mojo::ScopedSharedBufferMapping shared_buffer_mapping_;
-  size_t shared_buffer_size_;
+  // WritableSharedMemoryRegion has strict ownership and cannot be cloned. Since
+  // the shared memory region may be reused and handed out to a producer
+  // multiple times, this must use an UnsafeSharedMemoryRegion instead.
+  base::UnsafeSharedMemoryRegion region_;
+  base::WritableSharedMemoryMapping mapping_;
   size_t offsets_[kMaxPlanes];
   MojoSharedBufferDoneCB mojo_shared_buffer_done_cb_;
 };
diff --git a/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc b/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc
index 7b24ef7..18a70f3 100644
--- a/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc
+++ b/media/mojo/common/mojo_shared_buffer_video_frame_unittest.cc
@@ -11,31 +11,15 @@
 
 #include "base/bind.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/test/bind.h"
 #include "base/time/time.h"
-#include "mojo/public/cpp/system/buffer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace media {
 
-namespace {
-
-void CompareDestructionCallbackValues(
-    mojo::SharedBufferHandle expected_handle,
-    size_t expected_handle_size,
-    bool* callback_called,
-    mojo::ScopedSharedBufferHandle actual_handle,
-    size_t actual_handle_size) {
-  // Compare expected vs actual. Ownership of the memory is transferred with
-  // |actual_handle|, thus it is a ScopedSharedBufferHandle.
-  EXPECT_EQ(expected_handle, actual_handle.get());
-  EXPECT_EQ(expected_handle_size, actual_handle_size);
-  *callback_called = true;
-}
-
-}  // namespace
-
 TEST(MojoSharedBufferVideoFrameTest, CreateFrameWithSharedMemoryI420) {
   const int kWidth = 16;
   const int kHeight = 9;
@@ -118,16 +102,16 @@
   gfx::Rect visible_rect(size);
   size_t requested_size = VideoFrame::AllocationSize(format, size);
   ASSERT_LT(y_offset, requested_size);
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(requested_size);
-  ASSERT_TRUE(handle.is_valid());
+  auto region = base::UnsafeSharedMemoryRegion::Create(requested_size);
+  ASSERT_TRUE(region.IsValid());
 
   // Allocate frame.
+  const uint32_t offsets[] = {y_offset, u_offset, v_offset};
+  const int32_t strides[] = {y_stride, u_stride, v_stride};
   scoped_refptr<MojoSharedBufferVideoFrame> frame =
-      MojoSharedBufferVideoFrame::Create(
-          format, size, visible_rect, size, std::move(handle), requested_size,
-          {y_offset, u_offset, v_offset}, {y_stride, u_stride, v_stride},
-          kTimestamp);
+      MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size,
+                                         std::move(region), offsets, strides,
+                                         kTimestamp);
   ASSERT_TRUE(frame.get());
   EXPECT_EQ(frame->format(), format);
 
@@ -166,15 +150,16 @@
   gfx::Rect visible_rect(size);
   size_t requested_size = VideoFrame::AllocationSize(format, size);
   ASSERT_LT(y_offset, requested_size);
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(requested_size);
-  ASSERT_TRUE(handle.is_valid());
+  auto region = base::UnsafeSharedMemoryRegion::Create(requested_size);
+  ASSERT_TRUE(region.IsValid());
 
   // Allocate frame.
+  const uint32_t offsets[] = {y_offset, uv_offset};
+  const int32_t strides[] = {y_stride, uv_stride};
   scoped_refptr<MojoSharedBufferVideoFrame> frame =
-      MojoSharedBufferVideoFrame::Create(
-          format, size, visible_rect, size, std::move(handle), requested_size,
-          {y_offset, uv_offset}, {y_stride, uv_stride}, kTimestamp);
+      MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size,
+                                         std::move(region), offsets, strides,
+                                         kTimestamp);
   ASSERT_TRUE(frame.get());
   EXPECT_EQ(frame->format(), format);
 
@@ -225,27 +210,41 @@
   gfx::Size size(kWidth, kHeight);
   gfx::Rect visible_rect(size);
   size_t requested_size = VideoFrame::AllocationSize(format, size);
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(requested_size);
-  ASSERT_TRUE(handle.is_valid());
+  auto region = base::UnsafeSharedMemoryRegion::Create(requested_size);
+  ASSERT_TRUE(region.IsValid());
 
-  // Keep track of the original handle. MojoSharedBufferVideoFrame::Create()
-  // will get ownership of the memory.
-  mojo::SharedBufferHandle original_handle = handle.get();
+  const char kTestData[] = "reduce reuse recycle";
+  {
+    base::WritableSharedMemoryMapping mapping = region.Map();
+    ASSERT_GT(mapping.size(), strlen(kTestData));
+    // Note: deliberately using sizeof() to include the null terminator.
+    memcpy(mapping.memory(), kTestData, sizeof(kTestData));
+  }
 
   // Allocate frame.
+  const uint32_t kOffsets[] = {0, 0, 0};
+  const int32_t kStrides[] = {kWidth, kWidth, kWidth};
   scoped_refptr<MojoSharedBufferVideoFrame> frame =
-      MojoSharedBufferVideoFrame::Create(
-          format, size, visible_rect, size, std::move(handle), requested_size,
-          {0, 0, 0}, {kWidth, kWidth, kWidth}, kTimestamp);
+      MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size,
+                                         std::move(region), kOffsets, kStrides,
+                                         kTimestamp);
   ASSERT_TRUE(frame.get());
   EXPECT_EQ(frame->format(), format);
 
   // Set the destruction callback.
   bool callback_called = false;
-  frame->SetMojoSharedBufferDoneCB(
-      base::BindOnce(&CompareDestructionCallbackValues, original_handle,
-                     requested_size, &callback_called));
+  auto destruction_cb =
+      base::BindLambdaForTesting([&](base::UnsafeSharedMemoryRegion region) {
+        callback_called = true;
+        ASSERT_EQ(requested_size, region.GetSize());
+        // Unsafe regions are always mapped as writable.
+        base::WritableSharedMemoryMapping mapping = region.Map();
+        // Check that the test data that was written there is still there as a
+        // proxy signal for checking that ownership of the shmem region has been
+        // transferred.
+        EXPECT_STREQ(kTestData, mapping.GetMemoryAs<char>());
+      });
+  frame->SetMojoSharedBufferDoneCB(std::move(destruction_cb));
   EXPECT_FALSE(callback_called);
 
   // Force destruction of |frame|.
@@ -274,16 +273,16 @@
 
   // Allocate some shared memory.
   size_t requested_size = VideoFrame::AllocationSize(format, size);
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(requested_size);
-  ASSERT_TRUE(handle.is_valid());
+  auto region = base::UnsafeSharedMemoryRegion::Create(requested_size);
+  ASSERT_TRUE(region.IsValid());
 
   // Allocate frame.
+  const uint32_t kOffsets[] = {y_offset, u_offset, v_offset};
+  const int32_t kStrides[] = {y_stride, u_stride, v_stride};
   scoped_refptr<MojoSharedBufferVideoFrame> frame =
-      MojoSharedBufferVideoFrame::Create(
-          format, size, visible_rect, size, std::move(handle), requested_size,
-          {y_offset, u_offset, v_offset}, {y_stride, u_stride, v_stride},
-          kTimestamp);
+      MojoSharedBufferVideoFrame::Create(format, size, visible_rect, size,
+                                         std::move(region), kOffsets, kStrides,
+                                         kTimestamp);
   ASSERT_TRUE(frame.get());
   EXPECT_EQ(frame->format(), format);
 
@@ -322,7 +321,8 @@
   const size_t u_stride = frame->stride(VideoFrame::kUPlane);
 
   // Verifies mapped size and offset.
-  EXPECT_EQ(mojo_frame->MappedSize(), static_cast<size_t>(3 * stride));
+  EXPECT_EQ(mojo_frame->shmem_region().GetSize(),
+            static_cast<size_t>(3 * stride));
   EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kYPlane), 0u);
   EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kUPlane), y_stride);
   EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kVPlane), y_stride + u_stride);
@@ -347,7 +347,8 @@
   const size_t y_stride = frame->stride(VideoFrame::kYPlane);
 
   // Verifies mapped size and offset.
-  EXPECT_EQ(mojo_frame->MappedSize(), static_cast<size_t>(2 * stride));
+  EXPECT_EQ(mojo_frame->shmem_region().GetSize(),
+            static_cast<size_t>(2 * stride));
   EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kYPlane), 0u);
   EXPECT_EQ(mojo_frame->PlaneOffset(VideoFrame::kUVPlane), y_stride);
 }
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn
index 8ef86fb..df4b0366 100644
--- a/media/mojo/mojom/BUILD.gn
+++ b/media/mojo/mojom/BUILD.gn
@@ -128,6 +128,20 @@
     {
       types = [
         {
+          mojom = "media.mojom.AudioProcessingSettings"
+          cpp = "::media::AudioProcessingSettings"
+        },
+        {
+          mojom = "media.mojom.AudioProcessingStats"
+          cpp = "::media::AudioProcessingStats"
+        },
+      ]
+      traits_headers = [ "audio_processing_mojom_traits.h" ]
+      traits_public_deps = [ ":shared_mojom_traits" ]
+    },
+    {
+      types = [
+        {
           mojom = "media.mojom.FullscreenVideoStatus"
           cpp = "::blink::WebFullscreenVideoStatus"
         },
@@ -164,21 +178,6 @@
     {
       types = [
         {
-          mojom = "media.mojom.AudioProcessingSettings"
-          cpp = "::media::AudioProcessingSettings"
-        },
-        {
-          mojom = "media.mojom.AudioProcessingStats"
-          cpp = "::media::AudioProcessingStats"
-        },
-      ]
-      traits_headers = [ "audio_processing_mojom_traits.h" ]
-      traits_sources = [ "audio_processing_mojom_traits.cc" ]
-      traits_public_deps = [ ":shared_mojom_traits" ]
-    },
-    {
-      types = [
-        {
           mojom = "media.mojom.AudioDecoderConfig"
           cpp = "::media::AudioDecoderConfig"
         },
@@ -745,6 +744,8 @@
 
 source_set("shared_mojom_traits") {
   sources = [
+    "audio_processing_mojom_traits.cc",
+    "audio_processing_mojom_traits.h",
     "video_frame_metadata_mojom_traits.cc",
     "video_frame_metadata_mojom_traits.h",
   ]
diff --git a/media/mojo/mojom/audio_processing_mojom_traits.h b/media/mojo/mojom/audio_processing_mojom_traits.h
index 6bd436f4..71e136a 100644
--- a/media/mojo/mojom/audio_processing_mojom_traits.h
+++ b/media/mojo/mojom/audio_processing_mojom_traits.h
@@ -5,8 +5,8 @@
 #define MEDIA_MOJO_MOJOM_AUDIO_PROCESSING_MOJOM_TRAITS_H_
 
 #include "media/base/audio_processing.h"
-
-#include "media/mojo/mojom/audio_processing.mojom.h"
+#include "media/base/audio_processor_controls.h"
+#include "media/mojo/mojom/audio_processing.mojom-shared.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace mojo {
diff --git a/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc b/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc
index c95c6330..4aa9fb0 100644
--- a/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/audio_processing_mojom_traits_unittest.cc
@@ -5,6 +5,7 @@
 #include "media/mojo/mojom/audio_processing_mojom_traits.h"
 
 #include "media/base/audio_processing.h"
+#include "media/mojo/mojom/audio_processing.mojom.h"
 #include "media/mojo/mojom/traits_test_service.mojom.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/media/mojo/mojom/key_system_support.mojom b/media/mojo/mojom/key_system_support.mojom
index de2967f..c001fc6c 100644
--- a/media/mojo/mojom/key_system_support.mojom
+++ b/media/mojo/mojom/key_system_support.mojom
@@ -14,7 +14,7 @@
   // codec profiles separately. The list of supported codecs should be unique.
   array<AudioCodec> audio_codecs;
 
-  // Map of video codecs and associated profiles supported by the CDM
+  // Map from video codecs to associated profiles supported by the CDM
   // (e.g. vp8). If no profiles for a particular codec are specified, then
   // it is assumed that all profiles are supported by the CDM. The list of
   // profiles for each codec should be unique.
@@ -29,14 +29,21 @@
   CdmCapability? hw_secure_capability;
 };
 
-// Used to query the browser to see if a specific key system is supported.
+// Process-wide observer used by the renderer to observe key system support
+// changes. `key_systems` is a map from the key system string to the
+// KeySystemCapability for that key system.
+interface KeySystemSupportObserver {
+  // Called when there's a key system support update.
+  OnKeySystemSupportUpdated(map<string, KeySystemCapability> key_systems);
+};
+
+// Browser process singleton that a renderer process can use to subscribe to
+// key system updates.
 interface KeySystemSupport {
-  // Query to determine if the browser supports the |key_system|. If supported,
-  // |key_system_capability| is non-null indicating supported capability.
-  // KeySystemSupport implementation is in the browser process, as it maintains
-  // the list of registered CDMs, and hardware secure support check also needs
-  // to run in the browser process because the render process is sandboxed.
-  // KeySystemSupport clients run in the renderer process.
-  IsKeySystemSupported(string key_system)
-    => (bool is_supported, KeySystemCapability? key_system_capability);
+  // Adds an observer to observe key system support updates. KeySystemSupport
+  // implementation is in the browser process, as it maintains the list of
+  // registered CDMs, and hardware secure support check also needs to run in the
+  // browser process because the render process is sandboxed. KeySystemSupport
+  // clients run in the renderer process.
+  AddObserver(pending_remote<KeySystemSupportObserver> observer);
 };
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom
index 8223958f..6ef2ecc4e 100644
--- a/media/mojo/mojom/media_types.mojom
+++ b/media/mojo/mojom/media_types.mojom
@@ -6,6 +6,7 @@
 
 import "gpu/ipc/common/mailbox_holder.mojom";
 import "gpu/ipc/common/vulkan_ycbcr_info.mojom";
+import "mojo/public/mojom/base/shared_memory.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/values.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
@@ -409,9 +410,8 @@
 
 // This defines video frame data stored in a Mojo shared buffer.
 struct SharedBufferVideoFrameData {
-  // Reference to the shared memory containing the frame's data.
-  handle<shared_buffer> frame_data;
-  uint64 frame_data_size;
+  // Shared memory region for the frame data.
+  mojo_base.mojom.UnsafeSharedMemoryRegion frame_data;
 
   // Stride and offsets for each plane. Offsets are relative to the start
   // of |frame_data|.
diff --git a/media/mojo/mojom/video_frame_mojom_traits.cc b/media/mojo/mojom/video_frame_mojom_traits.cc
index 2fc7f20..eaee627 100644
--- a/media/mojo/mojom/video_frame_mojom_traits.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits.cc
@@ -9,6 +9,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "build/build_config.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "media/base/color_plane_layout.h"
@@ -40,15 +41,9 @@
     const media::MojoSharedBufferVideoFrame* mojo_frame =
         static_cast<const media::MojoSharedBufferVideoFrame*>(input);
 
-    // Mojo shared buffer handles are always writable. For example,
-    // cdm_video_decoder in ToCdmVideoFrame maps a frame writable; these frames
-    // are returned via callback and reused in ToCdmVideoFrame. Since returning
-    // via callback involves a Clone(), and since cloning a region read-only
-    // makes both the source handle and the cloned handle read-only, it must be
-    // cloned writable.
-    mojo::ScopedSharedBufferHandle dup = mojo_frame->Handle().Clone(
-        mojo::SharedBufferHandle::AccessMode::READ_WRITE);
-    DCHECK(dup.is_valid());
+    base::UnsafeSharedMemoryRegion region =
+        mojo_frame->shmem_region().Duplicate();
+    DCHECK(region.IsValid());
     size_t num_planes = media::VideoFrame::NumPlanes(mojo_frame->format());
     std::vector<uint32_t> offsets(num_planes);
     std::vector<int32_t> strides(num_planes);
@@ -59,8 +54,7 @@
 
     return media::mojom::VideoFrameData::NewSharedBufferData(
         media::mojom::SharedBufferVideoFrameData::New(
-            std::move(dup), mojo_frame->MappedSize(), std::move(strides),
-            std::move(offsets)));
+            std::move(region), std::move(strides), std::move(offsets)));
   }
 
   std::vector<gpu::MailboxHolder> mailbox_holder(media::VideoFrame::kMaxPlanes);
@@ -138,18 +132,19 @@
     media::mojom::SharedBufferVideoFrameDataDataView shared_buffer_data;
     data.GetSharedBufferDataDataView(&shared_buffer_data);
 
-    std::vector<int32_t> strides;
-    if (!shared_buffer_data.ReadStrides(&strides))
+    base::UnsafeSharedMemoryRegion region;
+    if (!shared_buffer_data.ReadFrameData(&region))
       return false;
 
-    std::vector<uint32_t> offsets;
-    if (!shared_buffer_data.ReadOffsets(&offsets))
-      return false;
+    mojo::ArrayDataView<uint32_t> offsets;
+    shared_buffer_data.GetOffsetsDataView(&offsets);
+
+    mojo::ArrayDataView<int32_t> strides;
+    shared_buffer_data.GetStridesDataView(&strides);
+
     frame = media::MojoSharedBufferVideoFrame::Create(
-        format, coded_size, visible_rect, natural_size,
-        shared_buffer_data.TakeFrameData(),
-        shared_buffer_data.frame_data_size(), std::move(offsets),
-        std::move(strides), timestamp);
+        format, coded_size, visible_rect, natural_size, std::move(region),
+        offsets, strides, timestamp);
   } else if (data.is_gpu_memory_buffer_data()) {
     media::mojom::GpuMemoryBufferVideoFrameDataDataView gpu_memory_buffer_data;
     data.GetGpuMemoryBufferDataDataView(&gpu_memory_buffer_data);
diff --git a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
index efd81cd..13864dfe 100644
--- a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
@@ -94,7 +94,7 @@
     ASSERT_EQ(frame->storage_type(), VideoFrame::STORAGE_MOJO_SHARED_BUFFER);
     MojoSharedBufferVideoFrame* mojo_shared_buffer_frame =
         static_cast<MojoSharedBufferVideoFrame*>(frame.get());
-    EXPECT_TRUE(mojo_shared_buffer_frame->Handle().is_valid());
+    EXPECT_TRUE(mojo_shared_buffer_frame->shmem_region().IsValid());
   }
 }
 
diff --git a/media/mojo/services/mojo_cdm_allocator.cc b/media/mojo/services/mojo_cdm_allocator.cc
index 7f16f35..8ab6c27 100644
--- a/media/mojo/services/mojo_cdm_allocator.cc
+++ b/media/mojo/services/mojo_cdm_allocator.cc
@@ -10,13 +10,14 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
 #include "media/cdm/api/content_decryption_module.h"
 #include "media/cdm/cdm_helpers.h"
 #include "media/cdm/cdm_type_conversion.h"
 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
-#include "mojo/public/cpp/system/buffer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -29,23 +30,21 @@
 class MojoCdmBuffer final : public cdm::Buffer {
  public:
   static MojoCdmBuffer* Create(
-      mojo::ScopedSharedBufferHandle buffer,
-      size_t capacity,
+      base::UnsafeSharedMemoryRegion region,
       MojoSharedBufferVideoFrame::MojoSharedBufferDoneCB
           mojo_shared_buffer_done_cb) {
-    DCHECK(buffer.is_valid());
+    DCHECK(region.IsValid());
     DCHECK(mojo_shared_buffer_done_cb);
 
     // cdm::Buffer interface limits capacity to uint32.
-    DCHECK_LE(capacity, std::numeric_limits<uint32_t>::max());
+    CHECK_LE(region.GetSize(), std::numeric_limits<uint32_t>::max());
 
-    auto mapping = buffer->Map(capacity);
-    if (!mapping)
+    base::WritableSharedMemoryMapping mapping = region.Map();
+    if (!mapping.IsValid())
       return nullptr;
 
-    return new MojoCdmBuffer(
-        std::move(buffer), base::checked_cast<uint32_t>(capacity),
-        std::move(mapping), std::move(mojo_shared_buffer_done_cb));
+    return new MojoCdmBuffer(std::move(region), std::move(mapping),
+                             std::move(mojo_shared_buffer_done_cb));
   }
 
   MojoCdmBuffer(const MojoCdmBuffer&) = delete;
@@ -53,21 +52,18 @@
 
   // cdm::Buffer implementation.
   void Destroy() final {
-    // Unmap the memory before returning the handle to |allocator_|.
-    mapping_.reset();
-
     // If nobody has claimed the handle, then return it.
-    if (buffer_.is_valid()) {
-      std::move(mojo_shared_buffer_done_cb_).Run(std::move(buffer_), capacity_);
+    if (region_.IsValid()) {
+      std::move(mojo_shared_buffer_done_cb_).Run(std::move(region_));
     }
 
     // No need to exist anymore.
     delete this;
   }
 
-  uint32_t Capacity() const final { return capacity_; }
+  uint32_t Capacity() const final { return mapping_.size(); }
 
-  uint8_t* Data() final { return static_cast<uint8_t*>(mapping_.get()); }
+  uint8_t* Data() final { return mapping_.GetMemoryAs<uint8_t>(); }
 
   void SetSize(uint32_t size) final {
     DCHECK_LE(size, Capacity());
@@ -76,34 +72,33 @@
 
   uint32_t Size() const final { return size_; }
 
-  const mojo::SharedBufferHandle& Handle() const { return buffer_.get(); }
+  const base::UnsafeSharedMemoryRegion& Region() const { return region_; }
 
-  mojo::ScopedSharedBufferHandle TakeHandle() { return std::move(buffer_); }
+  base::UnsafeSharedMemoryRegion TakeRegion() { return std::move(region_); }
 
  private:
-  MojoCdmBuffer(mojo::ScopedSharedBufferHandle buffer,
-                uint32_t capacity,
-                mojo::ScopedSharedBufferMapping mapping,
+  MojoCdmBuffer(base::UnsafeSharedMemoryRegion region,
+                base::WritableSharedMemoryMapping mapping,
                 MojoSharedBufferVideoFrame::MojoSharedBufferDoneCB
                     mojo_shared_buffer_done_cb)
-      : buffer_(std::move(buffer)),
+      : region_(std::move(region)),
         mojo_shared_buffer_done_cb_(std::move(mojo_shared_buffer_done_cb)),
-        mapping_(std::move(mapping)),
-        capacity_(capacity) {
-    DCHECK(mapping_);
+        mapping_(std::move(mapping)) {
+    DCHECK(mapping_.IsValid());
   }
 
   ~MojoCdmBuffer() final {
     // Verify that the buffer has been returned so it can be reused.
-    DCHECK(!buffer_.is_valid());
+    DCHECK(!region_.IsValid());
   }
 
-  mojo::ScopedSharedBufferHandle buffer_;
+  // Unsafe because of the requirements of VideoFrame; see
+  // MojoSharedBufferVideoFrame for more details.
+  base::UnsafeSharedMemoryRegion region_;
   MojoSharedBufferVideoFrame::MojoSharedBufferDoneCB
       mojo_shared_buffer_done_cb_;
 
-  mojo::ScopedSharedBufferMapping mapping_;
-  const uint32_t capacity_;
+  base::WritableSharedMemoryMapping mapping_;
   uint32_t size_ = 0;
 };
 
@@ -128,10 +123,8 @@
     MojoCdmBuffer* buffer = static_cast<MojoCdmBuffer*>(FrameBuffer());
     const gfx::Size frame_size(Size().width, Size().height);
 
-    // Take ownership of the mojo::ScopedSharedBufferHandle from |buffer|.
-    uint32_t buffer_size = buffer->Size();
-    mojo::ScopedSharedBufferHandle handle = buffer->TakeHandle();
-    DCHECK(handle.is_valid());
+    base::UnsafeSharedMemoryRegion region = buffer->TakeRegion();
+    DCHECK(region.IsValid());
 
     // Clear FrameBuffer so that MojoCdmVideoFrame no longer has a reference
     // to it (memory will be transferred to MojoSharedBufferVideoFrame).
@@ -140,15 +133,16 @@
     // Destroy the MojoCdmBuffer as it is no longer needed.
     buffer->Destroy();
 
+    const uint32_t offsets[] = {PlaneOffset(cdm::kYPlane),
+                                PlaneOffset(cdm::kUPlane),
+                                PlaneOffset(cdm::kVPlane)};
+    const int32_t strides[] = {static_cast<int32_t>(Stride(cdm::kYPlane)),
+                               static_cast<int32_t>(Stride(cdm::kUPlane)),
+                               static_cast<int32_t>(Stride(cdm::kVPlane))};
     scoped_refptr<MojoSharedBufferVideoFrame> frame =
         media::MojoSharedBufferVideoFrame::Create(
             ToMediaVideoFormat(Format()), frame_size, gfx::Rect(frame_size),
-            natural_size, std::move(handle), buffer_size,
-            {PlaneOffset(cdm::kYPlane), PlaneOffset(cdm::kUPlane),
-             PlaneOffset(cdm::kVPlane)},
-            {static_cast<int32_t>(Stride(cdm::kYPlane)),
-             static_cast<int32_t>(Stride(cdm::kUPlane)),
-             static_cast<int32_t>(Stride(cdm::kVPlane))},
+            natural_size, std::move(region), offsets, strides,
             base::Microseconds(Timestamp()));
 
     // |frame| could fail to be created if the memory can't be mapped into
@@ -172,7 +166,7 @@
 MojoCdmAllocator::~MojoCdmAllocator() = default;
 
 // Creates a cdm::Buffer, reusing an existing buffer if one is available.
-// If not, a new buffer is created using AllocateNewBuffer(). The caller is
+// If not, a new buffer is created using AllocateNewRegion(). The caller is
 // responsible for calling Destroy() on the buffer when it is no longer needed.
 cdm::Buffer* MojoCdmAllocator::CreateCdmBuffer(size_t capacity) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -180,27 +174,26 @@
   if (!capacity)
     return nullptr;
 
-  // Reuse a buffer in the free map if there is one that fits |capacity|.
+  // Reuse a shmem region in the free map if there is one that fits |capacity|.
   // Otherwise, create a new one.
-  mojo::ScopedSharedBufferHandle buffer;
-  auto found = available_buffers_.lower_bound(capacity);
-  if (found == available_buffers_.end()) {
-    buffer = AllocateNewBuffer(&capacity);
-    if (!buffer.is_valid()) {
+  base::UnsafeSharedMemoryRegion region;
+  auto found = available_regions_.lower_bound(capacity);
+  if (found == available_regions_.end()) {
+    region = AllocateNewRegion(capacity);
+    if (!region.IsValid()) {
       return nullptr;
     }
   } else {
-    capacity = found->first;
-    buffer = std::move(found->second);
-    available_buffers_.erase(found);
+    region = std::move(found->second);
+    available_regions_.erase(found);
   }
 
-  // Ownership of the SharedBufferHandle is passed to MojoCdmBuffer. When it is
-  // done with the memory, it must call AddBufferToAvailableMap() to make the
-  // memory available for another MojoCdmBuffer.
+  // Ownership of `region` is passed to MojoCdmBuffer. When it is done with the
+  // memory, it must call `AddRegionrToAvailableMap()` to make the memory
+  // available for another MojoCdmBuffer.
   return MojoCdmBuffer::Create(
-      std::move(buffer), capacity,
-      base::BindOnce(&MojoCdmAllocator::AddBufferToAvailableMap,
+      std::move(region),
+      base::BindOnce(&MojoCdmAllocator::AddRegionToAvailableMap,
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -208,12 +201,12 @@
 std::unique_ptr<VideoFrameImpl> MojoCdmAllocator::CreateCdmVideoFrame() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return std::make_unique<MojoCdmVideoFrame>(
-      base::BindOnce(&MojoCdmAllocator::AddBufferToAvailableMap,
+      base::BindOnce(&MojoCdmAllocator::AddRegionToAvailableMap,
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-mojo::ScopedSharedBufferHandle MojoCdmAllocator::AllocateNewBuffer(
-    size_t* capacity) {
+base::UnsafeSharedMemoryRegion MojoCdmAllocator::AllocateNewRegion(
+    size_t capacity) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Always pad new allocated buffer so that we don't need to reallocate
@@ -227,35 +220,33 @@
   // number of free buffers exceeds a limit. This mechanism helps avoid ending
   // up with too many small buffers, which could happen if the size to be
   // allocated keeps increasing.
-  if (available_buffers_.size() >= kFreeLimit)
-    available_buffers_.erase(available_buffers_.begin());
+  if (available_regions_.size() >= kFreeLimit)
+    available_regions_.erase(available_regions_.begin());
 
   // Creation of shared memory may be expensive if it involves synchronous IPC
-  // calls. That's why we try to avoid AllocateNewBuffer() as much as we can.
-  base::CheckedNumeric<size_t> requested_capacity(*capacity);
+  // calls. That's why we try to avoid AllocateNewRegion() as much as we can.
+  base::CheckedNumeric<size_t> requested_capacity(capacity);
   requested_capacity += kBufferPadding;
-  mojo::ScopedSharedBufferHandle handle =
-      mojo::SharedBufferHandle::Create(requested_capacity.ValueOrDie());
-  if (!handle.is_valid())
-    return handle;
-  *capacity = requested_capacity.ValueOrDie();
-  return handle;
+  auto region =
+      base::UnsafeSharedMemoryRegion::Create(requested_capacity.ValueOrDie());
+  return region;
 }
 
-void MojoCdmAllocator::AddBufferToAvailableMap(
-    mojo::ScopedSharedBufferHandle buffer,
-    size_t capacity) {
+void MojoCdmAllocator::AddRegionToAvailableMap(
+    base::UnsafeSharedMemoryRegion region) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  available_buffers_.insert(std::make_pair(capacity, std::move(buffer)));
+  size_t capacity = region.GetSize();
+  available_regions_.insert({capacity, std::move(region)});
 }
 
-MojoHandle MojoCdmAllocator::GetHandleForTesting(cdm::Buffer* buffer) {
+const base::UnsafeSharedMemoryRegion& MojoCdmAllocator::GetRegionForTesting(
+    cdm::Buffer* buffer) const {
   MojoCdmBuffer* mojo_buffer = static_cast<MojoCdmBuffer*>(buffer);
-  return mojo_buffer->Handle().value();
+  return mojo_buffer->Region();
 }
 
-size_t MojoCdmAllocator::GetAvailableBufferCountForTesting() {
-  return available_buffers_.size();
+size_t MojoCdmAllocator::GetAvailableRegionCountForTesting() {
+  return available_regions_.size();
 }
 
 }  // namespace media
diff --git a/media/mojo/services/mojo_cdm_allocator.h b/media/mojo/services/mojo_cdm_allocator.h
index b051571..5f538ce 100644
--- a/media/mojo/services/mojo_cdm_allocator.h
+++ b/media/mojo/services/mojo_cdm_allocator.h
@@ -11,6 +11,7 @@
 #include <map>
 #include <memory>
 
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "media/cdm/cdm_allocator.h"
@@ -36,30 +37,29 @@
  private:
   friend class MojoCdmAllocatorTest;
 
-  // Map of available buffers. Done as a mapping of capacity to
-  // ScopedSharedBufferHandle so that we can efficiently find an available
-  // buffer of a particular size. Any buffers in the map are unmapped.
-  using AvailableBufferMap =
-      std::multimap<size_t, mojo::ScopedSharedBufferHandle>;
+  // Map of available buffers. Done as a mapping of capacity to shmem regions to
+  // make it efficient to find an available buffer of a particular size.
+  // Regions in the map are unmapped.
+  using AvailableRegionMap =
+      std::multimap<size_t, base::UnsafeSharedMemoryRegion>;
 
-  // Allocates a mojo::SharedBufferHandle of at least |capacity| bytes.
-  // |capacity| will be changed to reflect the actual size of the buffer
-  // allocated.
-  mojo::ScopedSharedBufferHandle AllocateNewBuffer(size_t* capacity);
+  // Allocates a shmem region of at least |capacity| bytes.
+  base::UnsafeSharedMemoryRegion AllocateNewRegion(size_t capacity);
 
-  // Returns |buffer| to the map of available buffers, ready to be used the
+  // Returns |region| to the map of available buffers, ready to be used the
   // next time CreateCdmBuffer() is called.
-  void AddBufferToAvailableMap(mojo::ScopedSharedBufferHandle buffer,
-                               size_t capacity);
+  void AddRegionToAvailableMap(base::UnsafeSharedMemoryRegion region);
 
-  // Returns the MojoHandle for a cdm::Buffer allocated by this class.
-  MojoHandle GetHandleForTesting(cdm::Buffer* buffer);
+  // Returns the base::UnsafeSharedMemoryRegion for a cdm::Buffer allocated by
+  // this class.
+  const base::UnsafeSharedMemoryRegion& GetRegionForTesting(
+      cdm::Buffer* buffer) const;
 
-  // Returns the number of buffers in |available_buffers_|.
-  size_t GetAvailableBufferCountForTesting();
+  // Returns the number of buffers in |available_regions_|.
+  size_t GetAvailableRegionCountForTesting();
 
   // Map of available, already allocated buffers.
-  AvailableBufferMap available_buffers_;
+  AvailableRegionMap available_regions_;
 
   // Confirms single-threaded access.
   base::ThreadChecker thread_checker_;
diff --git a/media/mojo/services/mojo_cdm_allocator_unittest.cc b/media/mojo/services/mojo_cdm_allocator_unittest.cc
index 6951115a..6a918fd 100644
--- a/media/mojo/services/mojo_cdm_allocator_unittest.cc
+++ b/media/mojo/services/mojo_cdm_allocator_unittest.cc
@@ -7,6 +7,8 @@
 
 #include <cstring>
 
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
 #include "media/base/video_frame.h"
 #include "media/cdm/api/content_decryption_module.h"
 #include "media/cdm/cdm_helpers.h"
@@ -34,12 +36,12 @@
     return allocator_.CreateCdmVideoFrame();
   }
 
-  MojoHandle GetHandle(cdm::Buffer* buffer) {
-    return allocator_.GetHandleForTesting(buffer);
+  const base::UnsafeSharedMemoryRegion& GetRegion(cdm::Buffer* buffer) {
+    return allocator_.GetRegionForTesting(buffer);
   }
 
-  size_t GetAvailableBufferCount() {
-    return allocator_.GetAvailableBufferCountForTesting();
+  size_t GetAvailableRegionCount() {
+    return allocator_.GetAvailableRegionCountForTesting();
   }
 
  private:
@@ -57,15 +59,30 @@
 TEST_F(MojoCdmAllocatorTest, ReuseCdmBuffer) {
   const size_t kRandomDataSize = 46;
 
+  const char kTestData[] = "reduce reuse recycle";
+
   // Create a small buffer.
   cdm::Buffer* buffer = CreateCdmBuffer(kRandomDataSize);
-  MojoHandle handle = GetHandle(buffer);
+  {
+    // Create a mapping and write some test data.
+    const auto& region = GetRegion(buffer);
+    base::WritableSharedMemoryMapping mapping = region.Map();
+    // Note: deliberately using sizeof() to include the null terminator.
+    memcpy(mapping.memory(), kTestData, sizeof(kTestData));
+  }
   buffer->Destroy();
 
   // Now allocate a new buffer of the same size, it should reuse the one
   // just freed.
   cdm::Buffer* new_buffer = CreateCdmBuffer(kRandomDataSize);
-  EXPECT_EQ(handle, GetHandle(new_buffer));
+  {
+    const auto& region = GetRegion(new_buffer);
+    // Unsafe regions are always mapped as writable.
+    base::WritableSharedMemoryMapping mapping = region.Map();
+    // Check that the test data that was written there is still there as a proxy
+    // signal for checking that the shmem region is reused.
+    EXPECT_STREQ(kTestData, mapping.GetMemoryAs<char>());
+  }
   new_buffer->Destroy();
 }
 
@@ -82,7 +99,7 @@
     buffer_size += kBufferSizeIncrease;
     cdm::Buffer* buffer = CreateCdmBuffer(buffer_size);
     buffer->Destroy();
-    EXPECT_LE(GetAvailableBufferCount(), kMaxExpectedFreeBuffers);
+    EXPECT_LE(GetAvailableRegionCount(), kMaxExpectedFreeBuffers);
   }
 }
 
@@ -110,7 +127,7 @@
 
   // Now create a buffer to hold the frame and assign it to the VideoFrameImpl.
   cdm::Buffer* buffer = CreateCdmBuffer(kBufferSize);
-  EXPECT_EQ(0u, GetAvailableBufferCount());
+  EXPECT_EQ(0u, GetAvailableRegionCount());
   buffer->SetSize(static_cast<uint32_t>(kBufferSize));
   video_frame->SetFrameBuffer(buffer);
   EXPECT_NE(nullptr, video_frame->FrameBuffer());
@@ -118,16 +135,16 @@
   // Transform it into a VideoFrame and make sure the buffer is no longer owned.
   scoped_refptr<VideoFrame> frame = video_frame->TransformToVideoFrame(kSize);
   EXPECT_EQ(nullptr, video_frame->FrameBuffer());
-  EXPECT_EQ(0u, GetAvailableBufferCount());
+  EXPECT_EQ(0u, GetAvailableRegionCount());
   video_frame.reset();
 
   // Check that the buffer is still in use. It will be freed when |frame|
   // is destroyed.
-  EXPECT_EQ(0u, GetAvailableBufferCount());
+  EXPECT_EQ(0u, GetAvailableRegionCount());
   frame = nullptr;
 
   // Check that the buffer is now in the free list.
-  EXPECT_EQ(1u, GetAvailableBufferCount());
+  EXPECT_EQ(1u, GetAvailableRegionCount());
 }
 
 }  // namespace media
diff --git a/media/renderers/win/media_engine_notify_impl.cc b/media/renderers/win/media_engine_notify_impl.cc
index 15f59a3..eb4ad5ee 100644
--- a/media/renderers/win/media_engine_notify_impl.cc
+++ b/media/renderers/win/media_engine_notify_impl.cc
@@ -63,14 +63,7 @@
 #undef ENUM_TO_STRING
 
 PipelineStatus MediaEngineErrorToPipelineStatus(
-    MF_MEDIA_ENGINE_ERR media_engine_error,
-    HRESULT hr) {
-  // HRESULT 0x8004CD12 is DRM_E_TEE_INVALID_HWDRM_STATE, which can happen
-  // during OS sleep/resume, or moving video to different graphics adapters.
-  // This is not an error, so special case it here.
-  if (hr == static_cast<HRESULT>(0x8004CD12))
-    return PIPELINE_ERROR_HARDWARE_CONTEXT_RESET;
-
+    MF_MEDIA_ENGINE_ERR media_engine_error) {
   switch (media_engine_error) {
     case MF_MEDIA_ENGINE_ERR_NOERROR:
       return PIPELINE_OK;
@@ -136,7 +129,7 @@
       MF_MEDIA_ENGINE_ERR error = static_cast<MF_MEDIA_ENGINE_ERR>(param1);
       HRESULT hr = param2;
       LOG(ERROR) << __func__ << ": error=" << error << ", hr=" << PrintHr(hr);
-      error_cb_.Run(MediaEngineErrorToPipelineStatus(error, hr), hr);
+      error_cb_.Run(MediaEngineErrorToPipelineStatus(error), hr);
       break;
     }
     case MF_MEDIA_ENGINE_EVENT_ENDED:
diff --git a/media/renderers/win/media_foundation_renderer.cc b/media/renderers/win/media_foundation_renderer.cc
index 3dad1ae0..c9dc53a1 100644
--- a/media/renderers/win/media_foundation_renderer.cc
+++ b/media/renderers/win/media_foundation_renderer.cc
@@ -81,13 +81,21 @@
     STRINGIFY(kFailedToSetCurrentTime);
     STRINGIFY(kFailedToPlay);
     STRINGIFY(kOnPlaybackError);
-    STRINGIFY(kOnDCompSurfaceReceivedError);
     STRINGIFY(kOnDCompSurfaceHandleSetError);
     STRINGIFY(kOnConnectionError);
+    STRINGIFY(kFailedToSetDCompMode);
+    STRINGIFY(kFailedToGetDCompSurface);
+    STRINGIFY(kFailedToDuplicateHandle);
   }
 #undef STRINGIFY
 }
 
+// INVALID_HANDLE_VALUE is the official invalid handle value. Historically, 0 is
+// not used as a handle value too.
+bool IsInvalidHandle(const HANDLE& handle) {
+  return handle == INVALID_HANDLE_VALUE || handle == nullptr;
+}
+
 }  // namespace
 
 // static
@@ -518,18 +526,20 @@
 
   HRESULT hr = SetDCompModeInternal();
   if (FAILED(hr)) {
-    std::string error = "Failed to set DComp mode: " + PrintHr(hr);
-    DLOG(ERROR) << error;
-    std::move(callback).Run(base::win::ScopedHandle(), error);
+    OnError(PIPELINE_ERROR_COULD_NOT_RENDER, ErrorReason::kFailedToSetDCompMode,
+            hr);
+    std::move(callback).Run(base::win::ScopedHandle(), PrintHr(hr));
     return;
   }
 
   HANDLE surface_handle = INVALID_HANDLE_VALUE;
   hr = GetDCompSurfaceInternal(&surface_handle);
-  if (FAILED(hr)) {
-    std::string error = "Failed to get DComp surface: " + PrintHr(hr);
-    DLOG(ERROR) << error;
-    std::move(callback).Run(base::win::ScopedHandle(), error);
+  // The handle could still be invalid after a non failure (e.g. S_FALSE) is
+  // returned. See https://crbug.com/1307065.
+  if (FAILED(hr) || IsInvalidHandle(surface_handle)) {
+    OnError(PIPELINE_ERROR_COULD_NOT_RENDER,
+            ErrorReason::kFailedToGetDCompSurface, hr);
+    std::move(callback).Run(base::win::ScopedHandle(), PrintHr(hr));
     return;
   }
 
@@ -540,11 +550,11 @@
   const BOOL result = ::DuplicateHandle(
       process, surface_handle, process, &duplicated_handle,
       GENERIC_READ | GENERIC_EXECUTE, false, DUPLICATE_CLOSE_SOURCE);
-  if (!result) {
-    std::string error =
-        "Duplicate surface_handle failed: " + PrintHr(::GetLastError());
-    DLOG(ERROR) << error;
-    std::move(callback).Run(base::win::ScopedHandle(), error);
+  if (!result || IsInvalidHandle(surface_handle)) {
+    hr = ::GetLastError();
+    OnError(PIPELINE_ERROR_COULD_NOT_RENDER,
+            ErrorReason::kFailedToDuplicateHandle, hr);
+    std::move(callback).Run(base::win::ScopedHandle(), PrintHr(hr));
     return;
   }
 
@@ -733,9 +743,6 @@
 
   base::UmaHistogramSparse("Media.MediaFoundationRenderer.PlaybackError", hr);
 
-  if (status == PIPELINE_ERROR_HARDWARE_CONTEXT_RESET && cdm_proxy_)
-    cdm_proxy_->OnHardwareContextReset();
-
   StopSendingStatistics();
   OnError(status, ErrorReason::kOnPlaybackError, hr);
 }
@@ -869,10 +876,28 @@
   const std::string error =
       "MediaFoundationRenderer error: " + GetErrorReasonString(reason) +
       (hresult.has_value() ? (" (" + PrintHr(hresult.value()) + ")") : "");
+
   DLOG(ERROR) << error;
   MEDIA_LOG(ERROR, media_log_) << error;
   ReportErrorReason(reason);
-  renderer_client_->OnError(status);
+
+  if (!hresult.has_value()) {
+    renderer_client_->OnError(status);
+    return;
+  }
+
+  // HRESULT 0x8004CD12 is DRM_E_TEE_INVALID_HWDRM_STATE, which can happen
+  // during OS sleep/resume, or moving video to different graphics adapters.
+  // This is not an error, so special case it here.
+  PipelineStatus status_to_report = status;
+  if (hresult == static_cast<HRESULT>(0x8004CD12)) {
+    status_to_report = PIPELINE_ERROR_HARDWARE_CONTEXT_RESET;
+    if (cdm_proxy_)
+      cdm_proxy_->OnHardwareContextReset();
+  }
+
+  status_to_report.WithData("hresult", static_cast<uint32_t>(hresult.value()));
+  renderer_client_->OnError(status_to_report);
 }
 
 void MediaFoundationRenderer::RequestNextFrameBetweenTimestamps(
diff --git a/media/renderers/win/media_foundation_renderer.h b/media/renderers/win/media_foundation_renderer.h
index d9cde7b..820284a 100644
--- a/media/renderers/win/media_foundation_renderer.h
+++ b/media/renderers/win/media_foundation_renderer.h
@@ -45,16 +45,19 @@
   // Reported to UMA. Do not change existing values.
   enum class ErrorReason {
     kUnknown = 0,
-    kCdmProxyReceivedInInvalidState,
-    kFailedToSetSourceOnMediaEngine,
-    kFailedToSetCurrentTime,
-    kFailedToPlay,
-    kOnPlaybackError,
-    kOnDCompSurfaceReceivedError,
-    kOnDCompSurfaceHandleSetError,
-    kOnConnectionError,
+    kCdmProxyReceivedInInvalidState = 1,
+    kFailedToSetSourceOnMediaEngine = 2,
+    kFailedToSetCurrentTime = 3,
+    kFailedToPlay = 4,
+    kOnPlaybackError = 5,
+    kOnDCompSurfaceReceivedError [[deprecated]] = 6,
+    kOnDCompSurfaceHandleSetError = 7,
+    kOnConnectionError = 8,
+    kFailedToSetDCompMode = 9,
+    kFailedToGetDCompSurface = 10,
+    kFailedToDuplicateHandle = 11,
     // Add new values here and update `kMaxValue`. Never reuse existing values.
-    kMaxValue = kOnConnectionError,
+    kMaxValue = kFailedToDuplicateHandle,
   };
 
   // Report `reason` to UMA.
@@ -157,6 +160,7 @@
   Microsoft::WRL::ComPtr<MediaEngineNotifyImpl> mf_media_engine_notify_;
   Microsoft::WRL::ComPtr<MediaEngineExtension> mf_media_engine_extension_;
   Microsoft::WRL::ComPtr<MediaFoundationSourceWrapper> mf_source_;
+
   // This enables MFMediaEngine to use hardware acceleration for video decoding
   // and video processing.
   Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> dxgi_device_manager_;
diff --git a/media/webrtc/audio_processor_controls.h b/media/webrtc/audio_processor_controls.h
deleted file mode 100644
index 9acf4ec..0000000
--- a/media/webrtc/audio_processor_controls.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_WEBRTC_AUDIO_PROCESSOR_CONTROLS_H_
-#define MEDIA_WEBRTC_AUDIO_PROCESSOR_CONTROLS_H_
-
-#include "base/callback.h"
-#include "third_party/webrtc/api/media_stream_interface.h"
-
-namespace base {
-class File;
-}
-
-namespace media {
-
-class AudioProcessorControls {
- public:
-  using GetStatsCB = base::OnceCallback<void(
-      const webrtc::AudioProcessorInterface::AudioProcessorStatistics& stats)>;
-
-  // Request the latest stats from the audio processor. Stats are returned
-  // asynchronously through |callback|.
-  virtual void GetStats(GetStatsCB callback) = 0;
-
-  // Begin dumping echo cancellation data into |file|.
-  virtual void StartEchoCancellationDump(base::File file) = 0;
-
-  // Stop any ongoin dump of echo cancellation data.
-  virtual void StopEchoCancellationDump() = 0;
-
- protected:
-  virtual ~AudioProcessorControls() = default;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_WEBRTC_AUDIO_PROCESSOR_CONTROLS_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
index f9aad62..7d98a0e 100644
--- a/mojo/public/cpp/bindings/associated_interface_ptr_info.h
+++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
@@ -24,13 +24,9 @@
 template <typename Interface>
 class AssociatedInterfacePtrInfo {
  public:
-  AssociatedInterfacePtrInfo() : version_(0u) {}
-  AssociatedInterfacePtrInfo(std::nullptr_t) : version_(0u) {}
-
   AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other)
-      : handle_(std::move(other.handle_)), version_(other.version_) {
-    other.version_ = 0u;
-  }
+      : handle_(std::move(other.handle_)),
+        version_(std::exchange(other.version_, 0u)) {}
 
   AssociatedInterfacePtrInfo(ScopedInterfaceEndpointHandle handle,
                              uint32_t version)
@@ -45,8 +41,7 @@
   AssociatedInterfacePtrInfo& operator=(AssociatedInterfacePtrInfo&& other) {
     if (this != &other) {
       handle_ = std::move(other.handle_);
-      version_ = other.version_;
-      other.version_ = 0u;
+      version_ = std::exchange(other.version_, 0u);
     }
 
     return *this;
@@ -54,27 +49,8 @@
 
   bool is_valid() const { return handle_.is_valid(); }
 
-  explicit operator bool() const { return handle_.is_valid(); }
-
-  ScopedInterfaceEndpointHandle PassHandle() {
-    return std::move(handle_);
-  }
-  const ScopedInterfaceEndpointHandle& handle() const { return handle_; }
-  void set_handle(ScopedInterfaceEndpointHandle handle) {
-    handle_ = std::move(handle);
-  }
-
+  ScopedInterfaceEndpointHandle PassHandle() { return std::move(handle_); }
   uint32_t version() const { return version_; }
-  void set_version(uint32_t version) { version_ = version; }
-
-  bool Equals(const AssociatedInterfacePtrInfo& other) const {
-    if (this == &other)
-      return true;
-
-    // Now that the two refer to different objects, they are equivalent if
-    // and only if they are both invalid.
-    return !is_valid() && !other.is_valid();
-  }
 
  private:
   ScopedInterfaceEndpointHandle handle_;
diff --git a/mojo/public/cpp/bindings/deprecated_interface_types_forward.h b/mojo/public/cpp/bindings/deprecated_interface_types_forward.h
index 9d49cc2f..e9b6f7c 100644
--- a/mojo/public/cpp/bindings/deprecated_interface_types_forward.h
+++ b/mojo/public/cpp/bindings/deprecated_interface_types_forward.h
@@ -9,8 +9,6 @@
 namespace mojo {
 
 template <typename Interface>
-class InterfacePtrInfo;
-template <typename Interface>
 class AssociatedInterfacePtrInfo;
 
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/interface_serialization.h b/mojo/public/cpp/bindings/lib/interface_serialization.h
index 2e47896..3cae724 100644
--- a/mojo/public/cpp/bindings/lib/interface_serialization.h
+++ b/mojo/public/cpp/bindings/lib/interface_serialization.h
@@ -28,33 +28,6 @@
 
 template <typename Base, typename T>
 struct Serializer<AssociatedInterfacePtrInfoDataView<Base>,
-                  AssociatedInterfacePtrInfo<T>> {
-  static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
-  static void Serialize(AssociatedInterfacePtrInfo<T>& input,
-                        AssociatedInterface_Data* output,
-                        Message* message) {
-    DCHECK(!input.handle().is_valid() || input.handle().pending_association());
-    SerializeAssociatedInterfaceInfo(input.PassHandle(), input.version(),
-                                     *message, *output);
-  }
-
-  static bool Deserialize(AssociatedInterface_Data* input,
-                          AssociatedInterfacePtrInfo<T>* output,
-                          Message* message) {
-    auto handle = DeserializeAssociatedEndpointHandle(input->handle, *message);
-    if (!handle.is_valid()) {
-      *output = AssociatedInterfacePtrInfo<T>();
-    } else {
-      output->set_handle(std::move(handle));
-      output->set_version(input->version);
-    }
-    return true;
-  }
-};
-
-template <typename Base, typename T>
-struct Serializer<AssociatedInterfacePtrInfoDataView<Base>,
                   PendingAssociatedRemote<T>> {
   static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
 
diff --git a/mojo/public/cpp/bindings/pending_associated_remote.h b/mojo/public/cpp/bindings/pending_associated_remote.h
index be230da..e70fb2a 100644
--- a/mojo/public/cpp/bindings/pending_associated_remote.h
+++ b/mojo/public/cpp/bindings/pending_associated_remote.h
@@ -37,11 +37,6 @@
                           uint32_t version)
       : handle_(std::move(handle)), version_(version) {}
 
-  // Temporary helper for transitioning away from old types. Intentionally an
-  // implicit constructor.
-  PendingAssociatedRemote(AssociatedInterfacePtrInfo<Interface>&& ptr_info)
-      : PendingAssociatedRemote(ptr_info.PassHandle(), ptr_info.version()) {}
-
   // Disabled on NaCl since it crashes old version of clang.
 #if !BUILDFLAG(IS_NACL)
   // Move conversion operator for custom remote types. Only participates in
@@ -74,12 +69,6 @@
 
   void reset() { handle_.reset(); }
 
-  // Temporary helper for transitioning away from old bindings types. This is
-  // intentionally an implicit conversion.
-  operator AssociatedInterfacePtrInfo<Interface>() {
-    return AssociatedInterfacePtrInfo<Interface>(PassHandle(), version());
-  }
-
   ScopedInterfaceEndpointHandle PassHandle() { return std::move(handle_); }
   const ScopedInterfaceEndpointHandle& handle() const { return handle_; }
   void set_handle(ScopedInterfaceEndpointHandle handle) {
diff --git a/net/base/features.cc b/net/base/features.cc
index 51a6af9..ae84020 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -254,6 +254,8 @@
 
 const base::Feature kPartitionedCookies{"PartitionedCookies",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kPartitionedCookiesBypassOriginTrial{
+    "PartitionedCookiesBypassOriginTrial", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kExtraCookieValidityChecks{
     "ExtraCookieValidityChecks", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/net/base/features.h b/net/base/features.h
index 583fbbb7..4ceedfa 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -381,6 +381,11 @@
 // be sent when the browser is on the same top-level site that it was on when
 // the cookie was set.
 NET_EXPORT extern const base::Feature kPartitionedCookies;
+// Flag to bypass the origin trial opt-in to use Partitioned cookies. This
+// allows developers to test Partitioned cookies manually in development
+// environments.
+// TODO(crbug.com/1296161): Remove this feature when the CHIPS OT ends.
+NET_EXPORT extern const base::Feature kPartitionedCookiesBypassOriginTrial;
 
 // When enabled, additional cookie-related APIs will perform cookie field size
 // and character set validation to enforce stricter conformance with RFC6265bis.
diff --git a/net/base/prioritized_task_runner.h b/net/base/prioritized_task_runner.h
index d97a2bd..086f8c7 100644
--- a/net/base/prioritized_task_runner.h
+++ b/net/base/prioritized_task_runner.h
@@ -63,8 +63,8 @@
                         base::OnceClosure reply,
                         uint32_t priority);
 
-  // Similar to base::PostTaskAndReplyWithResult, except that the task runs at
-  // |priority|. See PostTaskAndReply for a description of |priority|.
+  // Similar to TaskRunner::PostTaskAndReplyWithResult, except that the task
+  // runs at |priority|. See PostTaskAndReply for a description of |priority|.
   template <typename TaskReturnType, typename ReplyArgType>
   void PostTaskAndReplyWithResult(const base::Location& from_here,
                                   base::OnceCallback<TaskReturnType()> task,
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index f1c0b64..52b5cfb 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -673,7 +673,6 @@
         host_resolver_flags_(
             HostResolver::ParametersToHostResolverFlags(parameters_)),
         priority_(parameters_.initial_priority),
-        job_(nullptr),
         resolver_(std::move(resolver)),
         tick_clock_(tick_clock) {}
 
@@ -686,7 +685,7 @@
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK(callback);
     // Start() may only be called once per request.
-    DCHECK(!job_);
+    CHECK(!job_.has_value());
     DCHECK(!complete_);
     DCHECK(!callback_);
     // Parent HostResolver must still be alive to call Start().
@@ -694,7 +693,7 @@
 
     if (!resolve_context_) {
       complete_ = true;
-      resolver_ = nullptr;
+      resolver_.reset();
       set_error_info(ERR_CONTEXT_SHUT_DOWN, false);
       return ERR_NAME_NOT_RESOLVED;
     }
@@ -703,14 +702,14 @@
     int rv = resolver_->Resolve(this);
     DCHECK(!complete_);
     if (rv == ERR_IO_PENDING) {
-      DCHECK(job_);
+      CHECK(job_.has_value());
       callback_ = std::move(callback);
     } else {
-      DCHECK(!job_);
+      CHECK(!job_.has_value());
       complete_ = true;
       LogFinishRequest(rv, false /* async_completion */);
     }
-    resolver_ = nullptr;
+    resolver_.reset();
 
     return rv;
   }
@@ -805,46 +804,25 @@
     stale_info_ = std::move(stale_info);
   }
 
-  void AssignJob(Job* job) {
-    DCHECK(job);
-    DCHECK(!job_);
-
-    job_ = job;
+  void AssignJob(base::SafeRef<Job> job) {
+    CHECK(!job_.has_value());
+    job_ = std::move(job);
   }
 
+  bool HasJob() const { return job_.has_value(); }
+
+  // Gets the Job's key. Crashes if no Job has been assigned.
+  const JobKey& GetJobKey() const;
+
   // Unassigns the Job without calling completion callback.
-  void OnJobCancelled(Job* job) {
-    DCHECK_EQ(job_, job);
-    job_ = nullptr;
-    DCHECK(!complete_);
-    DCHECK(callback_);
-    callback_.Reset();
-
-    // No results should be set.
-    DCHECK(!results_);
-
-    LogCancelRequest();
-  }
+  void OnJobCancelled(const JobKey& key);
 
   // Cleans up Job assignment, marks request completed, and calls the completion
   // callback. |is_secure_network_error| indicates whether |error| came from a
   // secure DNS lookup.
-  void OnJobCompleted(Job* job, int error, bool is_secure_network_error) {
-    set_error_info(error, is_secure_network_error);
-
-    DCHECK_EQ(job_, job);
-    job_ = nullptr;
-
-    DCHECK(!complete_);
-    complete_ = true;
-
-    LogFinishRequest(error, true /* async_completion */);
-
-    DCHECK(callback_);
-    std::move(callback_).Run(HostResolver::SquashErrorCode(error));
-  }
-
-  Job* job() const { return job_; }
+  void OnJobCompleted(const JobKey& job_key,
+                      int error,
+                      bool is_secure_network_error);
 
   // NetLog for the source, passed in HostResolver::Resolve.
   const NetLogWithSource& source_net_log() { return source_net_log_; }
@@ -969,8 +947,8 @@
   RequestPriority priority_;
 
   // The resolve job that this request is dependent on.
-  raw_ptr<Job> job_;
-  base::WeakPtr<HostResolverManager> resolver_;
+  absl::optional<base::SafeRef<Job>> job_;
+  base::WeakPtr<HostResolverManager> resolver_ = nullptr;
 
   // The user's callback to invoke when the request completes.
   CompletionOnceCallback callback_;
@@ -2023,6 +2001,10 @@
                                  other.network_isolation_key);
   }
 
+  bool operator==(const JobKey& other) const {
+    return !(*this < other || other < *this);
+  }
+
   absl::variant<url::SchemeHostPort, std::string> host;
   NetworkIsolationKey network_isolation_key;
   DnsQueryTypeSet query_types;
@@ -2112,8 +2094,8 @@
       // Log any remaining Requests as cancelled.
       RequestImpl* req = requests_.head()->value();
       req->RemoveFromList();
-      DCHECK_EQ(this, req->job());
-      req->OnJobCancelled(this);
+      CHECK(key_ == req->GetJobKey());
+      req->OnJobCancelled(key_);
     }
   }
 
@@ -2146,7 +2128,7 @@
     // separated by scheme/port.
     DCHECK_EQ(GetHostname(key_.host), GetHostname(request->request_host()));
 
-    request->AssignJob(this);
+    request->AssignJob(weak_ptr_factory_.GetSafeRef());
 
     priority_tracker_.Add(request->priority());
 
@@ -2376,6 +2358,8 @@
     }
   }
 
+  const JobKey& key() const { return key_; }
+
   bool is_queued() const { return !handle_.is_null(); }
 
   bool is_running() const { return job_running_; }
@@ -2853,14 +2837,14 @@
     while (!requests_.empty()) {
       RequestImpl* req = requests_.head()->value();
       req->RemoveFromList();
-      DCHECK_EQ(this, req->job());
+      CHECK(key_ == req->GetJobKey());
 
       if (results.error() == OK && !req->parameters().is_speculative) {
         req->set_results(
             results.CopyWithDefaultPort(GetPort(req->request_host())));
       }
       req->OnJobCompleted(
-          this, results.error(),
+          key_, results.error(),
           secure && results.error() != OK /* is_secure_network_error */);
 
       // Check if the resolver was destroyed as a result of running the
@@ -3272,7 +3256,7 @@
 int HostResolverManager::Resolve(RequestImpl* request) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Request should not yet have a scheduled Job.
-  DCHECK(!request->job());
+  DCHECK(!request->HasJob());
   // Request may only be resolved once.
   DCHECK(!request->complete());
   // MDNS requests do not support skipping cache or stale lookups.
@@ -4244,18 +4228,57 @@
 
 HostResolverManager::RequestImpl::~RequestImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!job_)
+  if (!job_.has_value())
     return;
 
-  job_->CancelRequest(this);
+  job_.value()->CancelRequest(this);
   LogCancelRequest();
 }
 
 void HostResolverManager::RequestImpl::ChangeRequestPriority(
     RequestPriority priority) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(job_);
-  job_->ChangeRequestPriority(this, priority);
+  CHECK(job_.has_value());
+  job_.value()->ChangeRequestPriority(this, priority);
+}
+
+const HostResolverManager::JobKey& HostResolverManager::RequestImpl::GetJobKey()
+    const {
+  CHECK(job_.has_value());
+  return job_.value()->key();
+}
+
+void HostResolverManager::RequestImpl::OnJobCancelled(const JobKey& job_key) {
+  CHECK(job_.has_value());
+  CHECK(job_key == job_.value()->key());
+  job_.reset();
+  DCHECK(!complete_);
+  DCHECK(callback_);
+  callback_.Reset();
+
+  // No results should be set.
+  DCHECK(!results_);
+
+  LogCancelRequest();
+}
+
+void HostResolverManager::RequestImpl::OnJobCompleted(
+    const JobKey& job_key,
+    int error,
+    bool is_secure_network_error) {
+  set_error_info(error, is_secure_network_error);
+
+  CHECK(job_.has_value());
+  CHECK(job_key == job_.value()->key());
+  job_.reset();
+
+  DCHECK(!complete_);
+  complete_ = true;
+
+  LogFinishRequest(error, true /* async_completion */);
+
+  DCHECK(callback_);
+  std::move(callback_).Run(HostResolver::SquashErrorCode(error));
 }
 
 }  // namespace net
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 76ca003c..77d4fdb9 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -1926,7 +1926,6 @@
     { "name": "tracktivity.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "twitteroauth.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "vitrado.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "webtalis.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wevahoo.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "zentralwolke.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "zhovner.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -2378,7 +2377,6 @@
     { "name": "xtrim.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yenniferallulli.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yenniferallulli.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "yenniferallulli.es", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yenniferallulli.moda", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yenniferallulli.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "akachanikuji.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -2471,7 +2469,6 @@
     { "name": "enterdev.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ezimoeko.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eztv.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "feedthebot.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "festember.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fidelapp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "floweslawncare.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -3224,7 +3221,6 @@
     { "name": "tokke.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "unapp.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "valopv.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "varvy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "videomail.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "welovemail.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "whispeer.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -3798,7 +3794,6 @@
     { "name": "bcsytv.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "beholdthehurricane.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "beranovi.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "betterhelp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "borysek.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "broadsheet.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "broersma.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -3943,7 +3938,6 @@
     { "name": "0x90.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "alexwardweb.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "atolm.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "avastantivirus.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "berst.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bigbluedoor.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "binaryevolved.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -5243,7 +5237,6 @@
     { "name": "wegner.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wilddog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wlzhiyin.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "womf.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wondermags.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "zqhong.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dorianharmans.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -5334,7 +5327,6 @@
     { "name": "beanjuice.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "beardydave.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "beinad.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bely-mishka.by", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bendemaree.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bendingtheending.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "benk.press", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -5583,7 +5575,6 @@
     { "name": "gfm.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gigacog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gijsbertus.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "gjspunk.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "glentakahashi.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "go.ax", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "goalsetup.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -6743,7 +6734,6 @@
     { "name": "wittydonut.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "womb.city", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "woording.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "wpac.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wphostingblog.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wql.zj.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "xmr.to", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -6871,7 +6861,6 @@
     { "name": "kostya.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kunstundunrat.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "laobox.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "learntube.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lemoine.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "leonmahler.consulting", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "leovanna.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -6970,7 +6959,6 @@
     { "name": "yagihiro.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "zenithmedia.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "zhendingresources.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "zmy.im", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "3s-hosting.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "7kovrikov.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "adderall.space", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -7529,7 +7517,6 @@
     { "name": "bpadvisors.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bukkenfan.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bytesystems.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "campfourpaws.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "chaoswebs.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "chiphell.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "chorpinkpoemps.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -8084,7 +8071,6 @@
     { "name": "consciousbrand.org.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "consciousbranding.org.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "consciousbrands.net.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "consumer.gov", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "contactbig.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "containerstatistics.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "convocatoriafundacionpepsicomexico.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -8566,7 +8552,6 @@
     { "name": "hotting.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "houser.lu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "howbehealthy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hr-intranet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "http418.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "huarongdao.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "huersch.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -8583,7 +8568,6 @@
     { "name": "ibnuwebhost.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "iceloch.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "icpc2016.in.th", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "icreative.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ict-concept.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ideasmeetingpoint.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "idedr.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -8697,7 +8681,6 @@
     { "name": "juwairen.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jym.fit", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "jznet.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "kaangenc.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kaasbijwijn.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kabuabc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kadioglumakina.com.tr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -9132,7 +9115,6 @@
     { "name": "paulinewesterman.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paxwinkel.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paypaq.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "payroll.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "payslipview.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paytwopay.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pbapp.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -9606,7 +9588,6 @@
     { "name": "theurbanyoga.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thewindow.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thierfreund.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "thinktux.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thirdpartytrade.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thirty5.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "threatcentral.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -11937,7 +11918,6 @@
     { "name": "diezel.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "diff2html.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "diffnow.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "digimagical.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "digwp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dir2epub.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dir2epub.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -12149,7 +12129,6 @@
     { "name": "hasdf.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hatethe.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hcs-company.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "healthjoy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "healtious.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hearty.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hearty.space", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -13136,7 +13115,6 @@
     { "name": "4-it.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "aigcev.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "adventureally.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "abnarnro.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "anfsanchezo.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "achtzehn.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "3sreporting.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -14455,7 +14433,6 @@
     { "name": "storyland.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "stevensheffey.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sobieray.dyndns.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "snip.host", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sperohub.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "soe-server.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "seemeagain.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -14914,7 +14891,6 @@
     { "name": "consumerfiles.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bythisverse.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "chosenplaintext.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "consumidor.gov", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "comerford.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cganx.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "console.rest", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -15199,7 +15175,6 @@
     { "name": "hazyrom.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "get-on.bid", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "harrysmallbones.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hackerspace-ntnu.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "guentherhouse.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gpcsolutions.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "herebedragons.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17113,7 +17088,6 @@
     { "name": "localnetwork.nz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lecourtier.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "livejasmin.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "luludapomerania.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "grandchamproofing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "loforo.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lionlyrics.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17510,7 +17484,6 @@
     { "name": "pixdigital.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "proggersession.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "puneflowermall.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "regain.us", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "progarm.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "raymondelooff.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "probiv.biz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17776,7 +17749,6 @@
     { "name": "suempresa.cloud", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sysadminstory.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shereallyheals.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "teencounseling.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "texus.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ssbkk.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tahakomat.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -17877,7 +17849,6 @@
     { "name": "thrivewellnesshub.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tomabrafix.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "toool.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "tomaskavalek.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tuxlife.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "treino.blog.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "topdesk.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -18549,7 +18520,6 @@
     { "name": "coding.lv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "channellife.asia", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "codelitmus.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "craigwfox.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cnbs.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "coreum.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "confuddledpenguin.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -18926,7 +18896,6 @@
     { "name": "feisbed.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gaygeeks.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gdz-otvety.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "feitobrasilcosmeticos.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gdz-spishy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "g1.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gdz.tv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -18942,7 +18911,6 @@
     { "name": "ghrelinblocker.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "genesischangelog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gatemoves.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "geeks.lgbt", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "getsubs.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "getresilience.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gflame.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -20081,7 +20049,6 @@
     { "name": "souravsaha.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "streamlineautogroup.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "stickswag.cf", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "spykedigital.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sugarshin.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "static-myfxee-808795.c.cdn77.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sporter.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -20241,7 +20208,6 @@
     { "name": "top10mountainbikes.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thedailyupvote.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tretail.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "tlsbv.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "toyotamotala.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "treatprostatewithhifu.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tulsameetingroom.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -21880,7 +21846,6 @@
     { "name": "bornhack.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bosworthdental.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bottke.berlin", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bouah.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bouchard-mathieux.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bounceboxspc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "boutiqueguenaelleverdin.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -22258,7 +22223,6 @@
     { "name": "discover-mercure.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "discoverrsv.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ditch.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "diti.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "diveidc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "divinegames.studio", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dj-x.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -24051,7 +24015,6 @@
     { "name": "schwarzwald-flirt.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "scintilla.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "scintillating.stream", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "scionasset.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "scorocode.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "scredible.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "screen64.tk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -24737,7 +24700,6 @@
     { "name": "yfengs.moe", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yffengshi.ml", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yikeyong.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "yin8888.tv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yinhe12.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yiz96.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "yob.vn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -24877,7 +24839,6 @@
     { "name": "adzuna.sg", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "abn-consultants.ie", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "0005.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "adprospb.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "affordablepapers.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "africantourer.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "3778xl.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -26372,7 +26333,6 @@
     { "name": "lithianissaneugeneparts.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "livepaperhelp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "livekort.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "laforetenchantee.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lezard-com.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lexxyn.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "leavesofchangeweekly.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -26407,7 +26367,6 @@
     { "name": "lensdoctor.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lachainedesentrepreneurs.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "luffyhair.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "littleqiu.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "livrariahugodesaovitor.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lojadocristaozinho.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "logopedistalanni.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27128,7 +27087,6 @@
     { "name": "routeragency.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sdsmt.engineering", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sanitairwinkel.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "redperegrine.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "schippendale.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "schmaeh-coaching.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "schraugerrun.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27218,7 +27176,6 @@
     { "name": "shiseki.top", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "simonpaarlberg.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shiatsu-institut.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "shk.im", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "shoptec.sk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sinefili.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "simonspeich.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27453,7 +27410,6 @@
     { "name": "thesishelp.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tagdocumentary.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "swisscannabis.club", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "thebakingclass.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tekstschrijvers.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "thekrewserver.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "theosophie-afrique.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -30312,7 +30268,6 @@
     { "name": "1396.cc", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "1396.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "247medplan.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "4mybaby.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "69mentor.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "7delights.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "7delights.in", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -31189,7 +31144,6 @@
     { "name": "catdecor.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "catgirl.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cavevinsdefrance.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "centralcountiesservices.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "certmonitor.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cfno.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cgsmart.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -31334,7 +31288,6 @@
     { "name": "driver61.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "driverscollection.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "drixn.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "drixn.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "drixn.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "drixn.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "droithxn.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -32005,7 +31958,6 @@
     { "name": "portsdebalears.gob.es", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "postfalls-naturopathic.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "potworowski.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "pourout.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "powersergthisisthewebsitefuckyouscott.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pptavmdata.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "praerien-racing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -32100,7 +32052,6 @@
     { "name": "rsm-intern.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rtenews.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rtesport.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "rubbleremovalsbenoni.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rushiiworks.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ruskod.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "rwky.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -34675,7 +34626,6 @@
     { "name": "csfloors.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cu247secure.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cuecamania.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "customizeyourshower.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cyclisjumper.gallery", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cytech.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "czaw.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -34882,7 +34832,6 @@
     { "name": "lollaconcept.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lordgun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lovenwishes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lucakrebs.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lugui.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "luiscapelo.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "luizkowalski.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -35196,7 +35145,6 @@
     { "name": "anconaswine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "andoms.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "andrewensley.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "annotate.software", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "antivirusprotection.reviews", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "apiled.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "apponline.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -35442,7 +35390,6 @@
     { "name": "lacuevadechauvet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lag-gbr.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lanternalauth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "laraigneedusoir.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "larondinedisinfestazione.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lasuzefc.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "laviedalex.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -35958,7 +35905,6 @@
     { "name": "lolnames.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lon-so.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lookagain.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "losebellyfat.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lostandcash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lsvih.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "luckyfrog.hk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -36786,7 +36732,6 @@
     { "name": "galle.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ganaenergia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ganasoku.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "gaudeamus-folklor.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gavinsblog.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gayukai.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gazachallenge.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -37703,7 +37648,6 @@
     { "name": "weld.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "werkenvoorphiladelphia.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wezl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "whistler-transfers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "whitehousedrugpolicy.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "whocybered.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "willowtree.school", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39144,13 +39088,11 @@
     { "name": "remedios-caserospara.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "remissan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reservoirtp.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "resolving.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "restopro.nyc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "retefarmaciecostadamalfi.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "retireyourpassword.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rickvanderzwet.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rody-design.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "roodfruit.studio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "roughcopy.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rpmdrivingschool.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rtzoeller.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39325,7 +39267,6 @@
     { "name": "xujan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yolo-csgo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "youtubeviews.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ysicing.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yubico.co.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yubico.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yubikey.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39957,7 +39898,6 @@
     { "name": "themefoxx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "themonkeytrail.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thestudyla.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "theuucc.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "think-positive-watches.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thomasscholz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "timbrust.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -39986,7 +39926,6 @@
     { "name": "unstamps.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "usage.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "usastaffing.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "uwsoftware.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "uwvloereruit.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vapecrunch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "venenum.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41820,7 +41759,6 @@
     { "name": "mizuho-trade.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mobile-holzofenpizza.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mobilelooper.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mohela.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "more-terrain.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "morhys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mosshi.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -42007,7 +41945,6 @@
     { "name": "teplofom.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "teplomash24.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thebusinessofgoodfilm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "thesimplifiers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thestral.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thestralbot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "think-asia.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -42772,7 +42709,6 @@
     { "name": "damonline.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "danielhinterlechner.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "datalife.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dayofdays.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dearktiel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deckbuilderamerica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deelmijnreis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -45478,7 +45414,6 @@
     { "name": "reflectivity.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reflexive-engineering.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reflexive.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "refuelcollective.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reinaertvandecruys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "repology.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "residentiallocksmithsanantoniotx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46654,7 +46589,6 @@
     { "name": "taizegroep.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tallyfy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tannerryan.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "taoways.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tasks.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tearoomlints.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "techniclab.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -46778,7 +46712,6 @@
     { "name": "xn--nf1a578axkh.xn--fiqs8s", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xn--nrrdetval-v2ab.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xn--y8jarb5hca.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "xpbytes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xtips.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ygobbs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yourneighborhub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -47378,7 +47311,6 @@
     { "name": "dansdiscounttools.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dappworld.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "davesharpe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "davidforward.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "davidforward.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "davidmn.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dawgs.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -47507,7 +47439,6 @@
     { "name": "foundationspecialistmi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fracreazioni.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "frankbellamy.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "free.ac.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "freehao123.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "frontierdiscount.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "frostysummers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -48110,7 +48041,6 @@
     { "name": "tecma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tellcorpassessoria.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "termux.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tetraetc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "the-woods.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thebarrens.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thebestpersonin.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -48689,7 +48619,6 @@
     { "name": "rdjb2b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reallytrusted.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "redsquirrelcampsite.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "refinansiering.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rentalmed.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "resultsatretail.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "richardharpur.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -49365,7 +49294,6 @@
     { "name": "bethpage.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "biddle.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "biegal.ski", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "blackyau.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bliker.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bloglogistics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "blogthedayaway.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -49382,7 +49310,6 @@
     { "name": "brouwerijdeblauweijsbeer.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bwf11.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bwf55.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bwf6.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bwf66.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bwf77.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bwf99.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -49886,7 +49813,6 @@
     { "name": "rebelonline.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "receptionpoint.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reesmichael1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "refuelcreative.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "regresionavidaspasadas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "remax.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "remini.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -50851,7 +50777,6 @@
     { "name": "securityzap.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seg-sys.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seguros-de-salud-y-vida.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "senego.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "senobio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seoankara.name.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sexflare.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -51268,7 +51193,6 @@
     { "name": "lizmooredestinationweddings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "llemoz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "loanreadycredit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "locomocosec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "loli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "longtermcare.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "loteamentomontereiitu.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -52220,7 +52144,6 @@
     { "name": "ehrenburg.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eichler.work", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "einsteincapital.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "electricalfencingbedfordview.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elfussports.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eltlaw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elvcino.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -52280,7 +52203,6 @@
     { "name": "floraexpress.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "floridagulfbeachrealty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "floridasexhealth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fnh-expert.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "form3w.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "formsbyair.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fossforward.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -52476,7 +52398,6 @@
     { "name": "madisonent-facialplasticsurgery.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "madisonsquarerealestate.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "madokami.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "madrecha.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "magnate.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "magravsitalia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mailnara.co.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -53166,7 +53087,6 @@
     { "name": "bluesuncamping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bo4tracker.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "botoes-primor.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "braathe.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bramsikkens.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "breznet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brian-gordon.name", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -53539,7 +53459,6 @@
     { "name": "hks-projekt.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hm773.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hogarthdavieslloyd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "holadinero.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "holofox.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hopemeet.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hopemeet.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54279,7 +54198,6 @@
     { "name": "thomas-klubert.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thomaskaviani.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "threefantasy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "thrush.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tidy.chat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tittelbach.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tobi-server.goip.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54442,7 +54360,6 @@
     { "name": "ylwz.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yokone3-kutikomi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yosida95.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ysicing.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yuexiangzs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zagluszaczgps.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zaltv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54800,7 +54717,6 @@
     { "name": "wrd48.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xenolith.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xn--mntsamling-0cb.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "yangcs.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yhhh.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yourbittorrent.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yourbittorrent.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -55497,7 +55413,6 @@
     { "name": "wotsunduk.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wscore.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wunschpreisauto.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wuyang.ws", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xdtag.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xhotlips.date", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xinsane.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -56569,7 +56484,6 @@
     { "name": "samorazvitie.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sangyoui.health", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sanovnik.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sanych-msk.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saorview.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sarahcheyette.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sarkoziadam.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -56581,7 +56495,6 @@
     { "name": "schmidtlohwasser.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "schonstedt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "science.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "secondnature.bio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sehablazolano.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sek.ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sektor.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -57101,7 +57014,6 @@
     { "name": "scottmay.id.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "secure-computing.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "securefiletransfer.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "securityrussia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seminariruumid.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "server92.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sexar.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -57793,7 +57705,6 @@
     { "name": "shopdongho.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sietejefes.com.ar", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "simulping.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sirihouse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skillside.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skorpil.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smaltimentoamianto.campania.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58018,7 +57929,6 @@
     { "name": "legoutcheznous.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "legyenkianegykereked.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "leignier.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lenostech.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lepourquoiducomment.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "les-explos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "letsprint3d.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58215,7 +58125,6 @@
     { "name": "797715.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "877791.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "998081.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "abjay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "accreditamento.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "accrosoft.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "achat-volets-roulants.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58342,7 +58251,6 @@
     { "name": "gamerepublic.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gornergrat-kulm.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "govsurvey.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "grazitti.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ha.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "haaksmadehaanuitvaart.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "haarigerrattenarsch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -58552,7 +58460,6 @@
     { "name": "whitehouse.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wildcardcorp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wildcardfederal.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "wugniu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xrbox.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yachting-home.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yakmail.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -59825,7 +59732,6 @@
     { "name": "lucasit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "magicbeanschool.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marjorie-wiki.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "market-vanna.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "markshroyer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "matthew-cash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "medik8.com.cy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -59888,7 +59794,6 @@
     { "name": "qihl.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "qualitation.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "quentin-sauvetre.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "radomir-online.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "radzikow.ski", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rascahan.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reiciunas.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -59912,7 +59817,6 @@
     { "name": "shard.vc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shiqishidai.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skynet800.goip.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "smesitel-online.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smicompact.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "snowreport.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "somnomedics.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -62928,7 +62832,6 @@
     { "name": "9397aa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397b.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397c.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9397cc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397e.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397h.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -62940,7 +62843,6 @@
     { "name": "9397oo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397pp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9397q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397r.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397v.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9397ww.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -62949,7 +62851,6 @@
     { "name": "9721ff.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9721g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9721j.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "9721l.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9721ll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9721nn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "9721o.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63025,7 +62926,6 @@
     { "name": "adnexa.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ajhstamps.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "alexandercanton.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "allcleaningservice.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "animefire.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aquamarin.icu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "archematerial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63084,7 +62984,6 @@
     { "name": "d9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dd5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dd9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dd9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dd9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deadmorose.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "denali.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63131,7 +63030,6 @@
     { "name": "ff5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ff9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ff9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ff9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ff9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fhar.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flowchats.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63287,7 +63185,6 @@
     { "name": "n5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "n9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "n9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "n9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "n9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nadine-birkner.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nationalservice.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63319,7 +63216,6 @@
     { "name": "osom.finance", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "p9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "p9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "paratlantalalkozas.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "partin.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63350,7 +63246,6 @@
     { "name": "quedos.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "r5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "r9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "r9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "r9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "radioradicchio.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "railto.llc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63473,7 +63368,6 @@
     { "name": "w5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "w9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "w9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "w9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "w9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "walkhisway.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "walletconnector.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63486,7 +63380,6 @@
     { "name": "wiocha.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ww5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ww9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ww9397.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ww9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ww9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "x5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -63554,7 +63447,6 @@
     { "name": "yy-s.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yy5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yy9297.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "yy9297.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yy9721.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yy9728.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "z5197.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64376,14 +64268,10 @@
     { "name": "6729f.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729ff.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729g.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729g.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729gg.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729gg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729h.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729h.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729hh.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729i.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729i.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729ii.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729ipa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729j.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64391,13 +64279,11 @@
     { "name": "6729jj.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729jj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729k.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729k.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729kk.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729l.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729ll.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729ll.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729m.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729nn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729o.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729o.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729oo.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64408,20 +64294,17 @@
     { "name": "6729q.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729q.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729qq.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729qq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729r.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729rr.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729rr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729s.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729ss.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729ss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729t.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729tt.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729vv.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729w.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729w.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729ww.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729ww.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729x.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729x.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729xx.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64508,7 +64391,6 @@
     { "name": "918rw.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "987666365.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "a291.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "a6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "a6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "a6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "a88fc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64697,7 +64579,6 @@
     { "name": "htmdom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "huipc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "i6729.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "i6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "i6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "i6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iamhealthystore.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64732,7 +64613,6 @@
     { "name": "jttech.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "k6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "k6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kaleidlink.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kbc.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kids-world.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -64783,7 +64663,6 @@
     { "name": "mouche.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mrichard333.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "n6729.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "n6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "n6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "n6957.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "netexpat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -65029,7 +64908,6 @@
     { "name": "6729mm.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729mm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729n.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "6729n.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729nn.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729p.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "6729u.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -65121,7 +64999,6 @@
     { "name": "cruicky.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cyber-core.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cybercustodian.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "d6729.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "d6957.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "daniela-schwarz-individuelle-lebensberatung-coaching.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "danielluisrodriguezs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -65565,7 +65442,6 @@
     { "name": "gass-transformatoren.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gemstn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "genioideal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "gggggg.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "giaoxudongtri.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "glexia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "gobiz.com.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -66326,7 +66202,6 @@
     { "name": "gynem.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hannes.paris", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hellosalmon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "highpressuretech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "horsky.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "housemates.uk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "humanit.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -67353,7 +67228,6 @@
     { "name": "perini.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pesdacgh.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "petnow.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "petrovich.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philanima.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "photosaloncontest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pignus.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -67399,7 +67273,6 @@
     { "name": "stinkefingereinhorn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stkildaosteopathy.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "styledbysally.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sudocat.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "superpi.noip.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "supplypartnersdecolombia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "susthx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -68110,7 +67983,6 @@
     { "name": "365y7.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "365y77.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "365y9.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "365y99.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "365zg.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "380111000.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "380111777.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -68936,7 +68808,6 @@
     { "name": "euroshop.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eusarse.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "evadental.institute", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "eveaz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eventosformativos.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "everberg.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "everglowtrading.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73546,9 +73417,6 @@
     { "name": "nsine.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "octopoos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "octopoos.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "official-sensitive.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "official-sensitive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "official-sensitive.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "official-sensitive.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ontogenese.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "openwrt-dist.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -73591,7 +73459,6 @@
     { "name": "richieheijmans.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ruf888.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "runningandoutdoors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ryanjarvis.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "s-pro.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "safetysign.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "salnet.wf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -74472,7 +74339,6 @@
     { "name": "kirscrb.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kiwibird.tokyo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "klempin.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "klikweb.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "knowledgebuilds.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "krupacars.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ks89.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -75924,7 +75790,6 @@
     { "name": "penconsultants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philipdeussen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "philipdeussen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "planhub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "platform39.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "playinfinityvr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "plumbingofmesquite.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -76189,7 +76054,6 @@
     { "name": "centurion-consulting.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "certisoncologysolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chainge-re.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "change-coaching-gmbh.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chicjrajeevalochana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chmc.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chris-siedler.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -76451,7 +76315,6 @@
     { "name": "rotate4all.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "royalaubar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ruvinroshan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "rw2.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rythm.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saiyans.com.ve", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saledump.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -76885,7 +76748,6 @@
     { "name": "odegua.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "oegd.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ololmke.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "omega-marijuana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "omshivalab.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "onlinestoresite.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "opsre.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -77711,7 +77573,6 @@
     { "name": "cliffyb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cloudpole.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "clutch.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "codelei.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "comcov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "connectingrentals.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "connectingrentalsofbethel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -78375,7 +78236,6 @@
     { "name": "lucymontebello-arte.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "luisfariasgrupo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "m2h-fiscaliste.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "maichun.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "main-freedom.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maischances.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "malediven.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -78620,7 +78480,6 @@
     { "name": "tommymoya.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tomvanlaer.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "top-autoshop.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "totvs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "toujour.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "transforumation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "triangle-energie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -78777,7 +78636,6 @@
     { "name": "centurion-consulting.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "centurion-consulting.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "checkra.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "chika.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chimpmatic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chr1sbin.works", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cissofitness.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -79060,7 +78918,6 @@
     { "name": "pro-lq.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pro-lq.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "prodentalsantacruz.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "proextra.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "prograce.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pupset.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pusehusetkattehotell.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -80987,7 +80844,6 @@
     { "name": "fed-shashek.spb.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ferestre-bucuresti.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ferfer.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "finn-thorben.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fiyatgrafik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flugrecht.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fourwaysplumber24-7.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81443,7 +81299,6 @@
     { "name": "asiaflash.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aspirevc-prod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "atelier-des-apprentissages.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "audiencedefolie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "auroraofficefurniture.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "awsnuke.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "b67701.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81697,7 +81552,6 @@
     { "name": "matthewcollins.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "matthewkerley.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "media-store.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mediabookdb.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "megapl.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "meherpurnews.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mercatoitticosbt.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81888,7 +81742,6 @@
     { "name": "waaifu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wbcasaverde.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webplace4u.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "weekendcandy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "woohay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wordpress-experts.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wplibrary.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -81897,7 +81750,6 @@
     { "name": "xyzyz.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xyzzyyyz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yearli.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "yhanthydech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zeit.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zeit.sh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zjy7722.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -82252,7 +82104,6 @@
     { "name": "e-bookshelf.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "e4.chat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ebill.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "echo-n.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ecoformeurope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ecologica.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eewna.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -82462,7 +82313,6 @@
     { "name": "lesacredescouleurs.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "levidromelist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "libertytereconoce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "libertywines.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "libertywines.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lifesavvymedia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lilosaludable.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -82696,7 +82546,6 @@
     { "name": "salaminos.no-ip.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saltaranuncios.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "samisoft.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sanctum.geek.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sandra-perlbach.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saorsat.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saronikos.guide", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -84128,7 +83977,6 @@
     { "name": "yoogirls.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yxbet43.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yyr.im", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "znbr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zusterjansen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zwilla.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "11ag8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -85894,7 +85742,6 @@
     { "name": "1v9.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "22884.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "22994.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "233.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "3000peaks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "319xpj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "360gpscorp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86599,7 +86446,6 @@
     { "name": "platiniumvapes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "platodecomida.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "plexiglasssheetscuttosize.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "plumbalot.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "plzen-sadrokarton.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pmsg.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pneumatikos.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -86781,7 +86627,6 @@
     { "name": "tecnipuntoseguridad.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "telecallsrl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "televisionrepairnearme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tencar.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tenckhoff.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tenens.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tentacle.monster", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -88542,7 +88387,6 @@
     { "name": "infinitybooksindia.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "injapan.life", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "inkandtonerni.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "insideoutfuel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "investorfare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ip.dog", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "irenelove.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -89114,7 +88958,6 @@
     { "name": "soziale.email", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spabellabolivia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "standoffdrop.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "starase.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "steelshop.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stock-ai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "strefapi.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -90449,7 +90292,6 @@
     { "name": "veritalife.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "viacheslavpleshkov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vincehut.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vinumenu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vipkit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "visitationbvm.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "visualvolta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -96113,7 +95955,6 @@
     { "name": "nsoiran.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nsrc.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nssfchile.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "nubunk.com.ng", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nuclearforum.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nuclearhell.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nuclearnation.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -97161,7 +97002,6 @@
     { "name": "reyaltec.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rezistor.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rf-gamer.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "rf.studio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rfbcnet.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rfnews.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rfomega.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -99834,7 +99674,6 @@
     { "name": "blheritage-tours.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "booksmp3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "borza.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "brianfanzo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brightsparks.com.sg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bvb.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cabale.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -100668,7 +100507,6 @@
     { "name": "proficiodigital.sk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "protection-plexi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "protection-plexi.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "psicologomogidascruzes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "psychologue-grenoble.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ptcmonitoring.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "punjabdirectory.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -101300,7 +101138,6 @@
     { "name": "w3ctag.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wardpieters.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "web-worker.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "web.hr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webhr.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wego.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "whatthefoxhat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -101656,7 +101493,6 @@
     { "name": "odorucinema.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "oestemc.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ok-test.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "olacatlitter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "omegletalk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "omggo.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "opti-net.solutions", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -102286,7 +102122,6 @@
     { "name": "tienic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tinycat99.ws", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "titaniumangel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tnt.construction", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "topmejores.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "totpolyglot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tralios.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -102793,7 +102628,6 @@
     { "name": "red-eyed-tree-frogs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "redearsliderturtles.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "regamega.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "reisenbauer.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rejpaci.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "renam.md", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reypi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -102820,7 +102654,6 @@
     { "name": "sixe.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "slugify.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "smartpos.net.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "smoe.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "somoslaarmenia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sotypicallydutch.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spackmanimages.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -103547,7 +103380,6 @@
     { "name": "aiken.golf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aim.org.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aion-beritra.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "akoya.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aktarma.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "alghadpowersolutions.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "alinol.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -103782,7 +103614,6 @@
     { "name": "kescher.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kihi.news", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kingsol.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kizuki1749.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "klinikum-oldenburg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "klofteam.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kloftowel.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -105215,7 +105046,6 @@
     { "name": "laoudit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "larenas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ldbeauty.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "legowerewolf.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "leo.co.ke", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "letriolet-tignes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "link.sb", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -105240,7 +105070,6 @@
     { "name": "matrimonio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mattmorrissound.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mayacoa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "medsanuk.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "medstatix-dev.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "megatorrenthd.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mf58.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -105290,7 +105119,6 @@
     { "name": "oralb-prestazioni-odontoiatriche.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "oralbregalaoralb.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "organicseo4u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "osaka-hero-project.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "osteopathe-voisine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "outervision.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "oversightboard.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -105846,8 +105674,6 @@
     { "name": "riosoils.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rivalsa.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rncc.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "roodfruit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "roodfruit.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rubdiavila.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "russiancms.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "s-4.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -106261,7 +106087,6 @@
     { "name": "javanie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jaylee.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jcbfshr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "jeevanpaul.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jellebuitenhuis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jeneratorkiralama.name.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jezebel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -106280,7 +106105,6 @@
     { "name": "kissmateszabolcs.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kit.watch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kleincliche.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "knovator.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "koe.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kostenlosepornos.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kotaku.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -107166,7 +106990,6 @@
     { "name": "casino.viajes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "casinos.news", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cbdlession.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ccamatilfiji.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "certificateoflogistics.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chanakyanewz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chekhov.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -108108,8 +107931,6 @@
     { "name": "tafusu-support.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "taylored.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tchatland.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tci-style.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tciit.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "team-toranomon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "terraesencial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "theachero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -108612,7 +108433,6 @@
     { "name": "sayilarmuhendislik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "scrutinizer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sealandair.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "senarin.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sendai-cc.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sendai-cooking.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sensualgoth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -109866,7 +109686,6 @@
     { "name": "fincaalegranza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "findalocalaccountingfirm.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fittwon.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "florianimdahl.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fmovies.qa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fpsclasico.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "franciscoalpendre.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110610,7 +110429,6 @@
     { "name": "visscher.codes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vitario.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vitus-meppen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vusdigital.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "w2me.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wapasrd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wdesign.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -110934,7 +110752,6 @@
     { "name": "destiny.gg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "devpost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "devskyport.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dfepharma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dimaweb.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "discountcasino19.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dishwasherrepair-austin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111283,7 +111100,6 @@
     { "name": "makewebbetter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mamba.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "manage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "manopause.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mansour.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "manuelefysiotherapeut.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mapgues.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111311,7 +111127,6 @@
     { "name": "metapsychie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "meteoradar.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mettelenejohansson.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "miaoft.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "michalkozak.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "microfusion.tw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mimgnj.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111456,7 +111271,6 @@
     { "name": "pundix.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pvpserverler.pro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "q1.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "q9297.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "qhk.ci", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "qiliang.wang", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "qingniantuzhai.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -111494,7 +111308,6 @@
     { "name": "richmondcountyclerk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rightrasta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ringgitplus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "rip.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "riteboost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ro.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ro.exchange", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -112345,7 +112158,6 @@
     { "name": "imperiocursospro.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "indimaxindia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "insanb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ireps.gov.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "itarc.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iteksys.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ivkymppi.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -112635,7 +112447,6 @@
     { "name": "atc-fr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "avatype.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "awsl.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "b0x0.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "b2b-leads.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "balicyclingtours.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bamaland.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -112733,7 +112544,6 @@
     { "name": "f88vip34.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fairtrade.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fattoriabio.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fidesic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "files.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "finsify.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "firmfunding.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -113767,7 +113577,6 @@
     { "name": "samroelants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "samuel-philipp.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "santanderassetmanagement.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "scamtested.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "scholarsclub.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "scottandtammy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sdhb.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -113915,7 +113724,6 @@
     { "name": "bancoserfinanza.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "banglatypography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "baysideaba.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bazar.bg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "belmundo.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "benee-awraham.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "berela.com.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -114320,7 +114128,6 @@
     { "name": "thekitchenfarnborough.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thewrightflyer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thiasil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "timespreader.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "timotheeduran.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tinyhousesforsale-us.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tiwilandcouncil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -115693,7 +115500,6 @@
     { "name": "viktorchinkonsung.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "viktorchinkonsung.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "viperperformance.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "viseum.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vitabsolu.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vivetoluca.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vjshi.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -116823,7 +116629,6 @@
     { "name": "mord-ost.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mos-camin.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "motastore.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "motherguru.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "motherhoodinblack.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "motortrend.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "msoffice-inc.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -116883,7 +116688,6 @@
     { "name": "osaki.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ostra.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ostracize.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "oversimplifiedstatistics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "oxygenserv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pablofonta.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pac-muenchen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -117742,7 +117546,6 @@
     { "name": "j4m.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jakemansfield.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jcse.mil", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "jdinjury.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jetsadabetchoke77.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jimwoodrealty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "jiotvdth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -117865,7 +117668,6 @@
     { "name": "medicina-antiage.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "medicine-consultant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "medyascope.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "megatyumen.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "melikoff.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mentorbizlist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "menupay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -117933,7 +117735,6 @@
     { "name": "myeducationalplatform.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mylifesphotograph.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mynavi-kaigo.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mynetpay.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "myphotographytips.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nachalosbog.bg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "najprzepis.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -118187,9 +117988,7 @@
     { "name": "streamonline.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "strikevectorex.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sts-consulting.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "stubbings.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stubbingsmail.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "stubbmail.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "studboo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "studierttomnoch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sueno.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -119155,7 +118954,6 @@
     { "name": "chiromeisjes-boxberg.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chiropraktik-wildner.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chizouworld.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "chjeco.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chollospain.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chosenos.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chrisi-si.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -119273,7 +119071,6 @@
     { "name": "cooks.house", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "coolwaterevergreendrilling.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "copenhagenleadtech.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "copewithdata.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "copperexports.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "coqiptv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "corbium.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -119413,7 +119210,6 @@
     { "name": "deafsound.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dealsbythebay.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dealstreet.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dealwithstatistics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deathwarrior.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "debeer.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "decorator.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -120588,7 +120384,6 @@
     { "name": "kaiseraerospace.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kaliforniya.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kalinka-shop.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kalmar.rocks", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kaltoft.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kamandula.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kamareddine.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -120997,7 +120792,6 @@
     { "name": "matheball.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mathebau.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mathewlane.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "matinataskincare.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "matov.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "matrixfm.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "matthewimaniphotography.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -121363,7 +121157,6 @@
     { "name": "outbound.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ovalle.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ovallevirtual.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "oversimplifiedeconomics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "overthegate.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ovkerk-avezaath.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "owningless.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -121821,7 +121614,6 @@
     { "name": "scstg.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sdfamilycare.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seanchristian.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "seaspiration.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "secong.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "secretdeals.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sectrans.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -121844,7 +121636,6 @@
     { "name": "servingroddick.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "servingupsouthern.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sevacy.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sevasmos.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seveiller-simplement.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seven-seas.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "seven.social", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -123842,7 +123633,6 @@
     { "name": "princez.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "probateandplanning.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "prodigibook.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "productosdeteruel.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "prombaza31.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "propertycareincorporated.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "propertymingo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -125476,7 +125266,6 @@
     { "name": "kevindustries.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kevinfigueroamusic.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kevinmathiesen.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kevinmo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "keyblock.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "keyblock.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "keyblock.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -127766,7 +127555,6 @@
     { "name": "africanmangoforum.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agencygood.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agenter.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "agilepeopleopsframework.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agiloo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agirlknows.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agnusbostel.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -129059,7 +128847,6 @@
     { "name": "m2jest1c.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "m9t.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ma110.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "macstore.pe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "made-nous.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "madechocolaterie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "madrasareforms.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -129940,7 +129727,6 @@
     { "name": "sweatercon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sweetsugarcakes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "swellnote.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "symphony.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "synctechosting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "synthesis.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "syrianair.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -130244,7 +130030,6 @@
     { "name": "wonderbox.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wonderbox.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wong-sleweah.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "woodentreasure.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wordcharma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "workfromhomebusinessopportunities.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "workiva.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -130405,7 +130190,6 @@
     { "name": "asociaciones.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "asphy.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "assurance-emprunteur.bzh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "astacreative.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "astoure.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "astralriders.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "astronomiadecolombia.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -130433,7 +130217,6 @@
     { "name": "azsupport.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "azsupport.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "b-designer.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "babacsalogato.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "babblefeed.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "babehunt.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "babuccu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -130708,7 +130491,6 @@
     { "name": "ebilanzplus.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ebooks4gate.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ediberto.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "edukle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "efran-eliyev.gq", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "efterfest.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "el-tatwer.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -131349,7 +131131,6 @@
     { "name": "spiffsearch.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spillefuglen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spiludennemid.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "spinachcannabis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "spindelnet.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sportcenter.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sportvision.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -131557,7 +131338,6 @@
     { "name": "adrieng.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "advancedhealthmedical.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "agamabox.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "aimsoftnet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aircompressormachine.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "airmanproduction.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ak-vsk.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -131875,7 +131655,6 @@
     { "name": "californiabudgetfinance.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "camped.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cangku.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "capeprivacy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "capris.cr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cartegrise.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "casinocity.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -132382,7 +132161,6 @@
     { "name": "mlpavimentosdehormigonimpreso.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "modderday.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "modernqr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mohelafederal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mondayaftersunday.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "montre-luxe-occasion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "montyvlogs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -132627,7 +132405,6 @@
     { "name": "thewindowsclub.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "thoreauskalendar.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "threeriversopenhouse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "thrivinggracefully.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ticketunity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tiendamaspatchwork.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "timeslive.co.ke", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -135739,7 +135516,6 @@
     { "name": "poimenidou.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "politicalscore101.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pomerol-au-coeur.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "poolvilla-margarita.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "pornbabetyra.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "porumbei.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "powerbux.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -136097,7 +135873,6 @@
     { "name": "artcatch.art", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "as-aeu-ecp-dev-ecomeeting.azurewebsites.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "as-aeu-ecp-qas-ecomeeting.azurewebsites.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "asklawyersforjustice.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "astronomygcse.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "atalarmedya.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aureliavelvet.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -137515,7 +137290,6 @@
     { "name": "bubbleclips.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bubbleclipsnetwork.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "budgetwebsites.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "burnayavoda.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "burstequity.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "burstequity.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "burstequity.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -139810,7 +139584,6 @@
     { "name": "ladykarame.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lafeepraline.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lagroza.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lamedubois-parquet.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lamudi.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lamuixeranga.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lancelucido.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -139969,7 +139742,6 @@
     { "name": "masskick.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "masteranimal.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "matematik-ozel-ders.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mathez.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mavente.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maxmind-test.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maxmusic.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -141805,7 +141577,6 @@
     { "name": "mhasika.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mi-amigo.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "miao.team", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "miaovps.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "michaelcailloux.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mido4link.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "midrandsplumbing.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -142509,7 +142280,6 @@
     { "name": "declared.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "declaredme.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "definethenoise.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "degrootenslot.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "despondentrock.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "detale.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deusadaseducao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -142599,7 +142369,6 @@
     { "name": "finndel.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flemishopelclub.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "forhims.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "formosus.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "forsbenin.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "freecatz.pe.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "freesteam.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -143806,7 +143575,6 @@
     { "name": "immortalelf.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "imrozrum.k12.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "individuals.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ingenes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "intelly.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "invento.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "investor.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -143953,7 +143721,6 @@
     { "name": "magnetremodeling.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mahdi.style", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maisproduzida.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "makebadge.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "malacat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maldivestraveller.mv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mamilove.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -145853,7 +145620,6 @@
     { "name": "never-mind.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "newbabylon.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "newstj.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "next.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ng.edu.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nicolaschelly.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "nielsdesign.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -146899,7 +146665,6 @@
     { "name": "davidsdika.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dawnbyte.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deinjoghurt.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "dejongebeth.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "deliuksta.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "delprete.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "departure-transfer-reservation.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -147230,7 +146995,6 @@
     { "name": "quickquote.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "quirkycruise.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "radiocommande-forestiere.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "radiopanikos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "radopsec.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "radopsec.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ragstores.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -147758,7 +147522,6 @@
     { "name": "mahmoodmehrabi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mahmoodmehrabi.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mail-verifier.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mangatafestas.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marcelburger.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "markuspooch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marmelo.digital", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -148161,7 +147924,6 @@
     { "name": "concreteworksplus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "conform.one", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "congdongvietnhat.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "coronalab.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cpflsolucoes.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "craftersmarket.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "craftyun.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -149167,7 +148929,6 @@
     { "name": "saintshopoficial.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "saiwebtv.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "salussafety.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sandholevets.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "santeracristal.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "savicki.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "savicki.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -149595,7 +149356,6 @@
     { "name": "labinsights.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ladbroke.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lakashirdetesek.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lalunedangkor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "leaguecloud.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "leism.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "leismann.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -150042,7 +149802,6 @@
     { "name": "ejkgroep.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ejkinternet.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ejkradio.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "electricalfencingfourways.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "electricfencesouthafrica.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elevatedarborcare.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "emergingindustryassociation.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -150133,7 +149892,6 @@
     { "name": "khouloud.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "kiehost.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "koalabur.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "kocovi.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "krafting.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lady-sadieann.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lakashirdetes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -150257,7 +150015,6 @@
     { "name": "serve.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "servercore.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "serviciotecnicoencomputacion.com.ve", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sg-guentersleben.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shoppingonlinecoffee2u.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "shoveltoss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sidralmundet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -150431,7 +150188,6 @@
     { "name": "ayein.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "baas.agency", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "badrap.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "baederlacke.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bankcustomer.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "baratzegrowshop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "barz.link", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -150586,7 +150342,6 @@
     { "name": "faroitalia.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fcblueboys.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "femmenordic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fermenting.studio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ferrarigreen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fh-toolbox.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fiducoldex.com.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -150923,7 +150678,6 @@
     { "name": "realact-sawater.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "realamiga.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "reduxlineberryfactorycart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "reparo.pe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "revolutionhealth.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rhinesuchus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "riberasalines.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -151638,7 +151392,6 @@
     { "name": "lymphaticclinic.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lysa-hora.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "m426.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "macbach.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "machelpnashville.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maker.systems", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "makulatura.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -152155,7 +151908,6 @@
     { "name": "alberguecovadonga.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "alelectricista.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "alemautos.com.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "alfagroup-aluminium.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "aliancadesentupidora.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "allattaremoda.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "allianceautomation.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -152653,7 +152405,6 @@
     { "name": "potsandplanters.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "powerapp.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ppgod.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "presence-relation.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "prodoric.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "profservice.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "progettoforme.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -152866,7 +152617,6 @@
     { "name": "utahonlinedivorce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vanheede.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "variance.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vasectomie-pierre-boucher.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vaultlabs1226.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vcudu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "velopinion.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -153194,7 +152944,6 @@
     { "name": "dring.tf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "drjoesimmigration.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "drsoul.band", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "duckmob.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dumbcryptopunks.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dumbmeta.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "dunassyn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -153499,7 +153248,6 @@
     { "name": "mortilki.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ms-rss.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mydabb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "myhealthchecked.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mysilvershield.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "n3oneglass-uat.azurewebsites.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "naacam.org.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -153865,7 +153613,6 @@
     { "name": "weirdcompany.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wenablog.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "westcoastdrones.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "westrandgardeningservices.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "what.ink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "whichphish.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "whizkidpcservices.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -153943,9 +153690,7 @@
     { "name": "altcoinfiyatlari.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "amarildoruci.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "amondial.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ampnuts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "andar-reuma.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "andrew-simon.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "androx.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "androx.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "animaproduksiyon.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -153978,7 +153723,6 @@
     { "name": "boulangerie-patisserie.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bouvetmarechal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "brownsville360.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bx.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "canadapet.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "capslock.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "carstar.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -154294,7 +154038,6 @@
     { "name": "sonbilgi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sontaycamera.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "soopy.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "srealmoreno.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "starrosesandplants.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "statscrew.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "stepin.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -155567,7 +155310,6 @@
     { "name": "bigbangco.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bigbeautysecrets.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bigcitylife.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "bikesiliconvalley.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bilhos.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bilisimdanismani.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "billigflug.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -155859,7 +155601,6 @@
     { "name": "chakanaherb.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chaletapartmentrentals.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chaletverzekeringen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "chamberlinfamilyphilanthropy.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chantero.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chaoticevil.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "chaowan.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -156020,7 +155761,6 @@
     { "name": "coniglione.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "connectedandsmart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "connectivityparty.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "connectnedcommunity.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "connectnow.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "conpsy.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "consideratio.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -156417,7 +156157,6 @@
     { "name": "ecolive.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "economie2.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ecotrade-disinfestazioni.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "eczanemimarlik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ederasrl.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "edging.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "edilondon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -156448,7 +156187,6 @@
     { "name": "eimeko.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eintoepfe-bruchsal.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eirikyrolae.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ekaceluller.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ekozercy.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ekspertemerytalny.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ela-n.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -156538,7 +156276,6 @@
     { "name": "epoch-film.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eprint-grimsby.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eprom.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "equalto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "equilibrium.med.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "equine-dentistry-endoscope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "equine-dentistry-scope.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -156553,7 +156290,6 @@
     { "name": "ericsilva.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ericvantijn.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eriksen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "error.tools", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "es-ramonage.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "es.gratis", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "escspain.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -157277,7 +157013,6 @@
     { "name": "hopepartnershipproject.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hopi.com.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "horionimoveis.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "horncastleanglingcentre.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "horoscopo.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "horseridingdurban.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "hoshino-naika.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -157945,7 +157680,6 @@
     { "name": "limatolavvocati.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "lindoors.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "linea-nova.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "lingualinx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "linholiver.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "link26.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "link2u.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -158112,7 +157846,6 @@
     { "name": "mamtastationers.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "managedcontractors.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "manawa.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mangaplay.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mangelot-hosting.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "maniasoft.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "manueldelgadohomes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -158260,7 +157993,6 @@
     { "name": "mightyfive.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "migrinfo.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mijnonesie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "mijnzusenik.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mikeschaffnerphotography.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mikhail-youzhny.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "miki.community", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -159271,7 +159003,6 @@
     { "name": "ski-outdoor-shop.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skill-x.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skinnation.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "skinplaybeauty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skinrender.ga", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "skky.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sklep-proekologia.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -159429,7 +159160,6 @@
     { "name": "sqrl.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "squareeye.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "squid.gay", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "sscnic.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "sslgram.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ssmd.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ssmwebportal.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -159608,7 +159338,6 @@
     { "name": "tedyst.ro", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "teengamingnights.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "teestore.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "tegus.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tehnomagija.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tekcan.av.tr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "teknik-sipil.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -160108,7 +159837,6 @@
     { "name": "vollenberg.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vonnu.edu.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "voorde.lol", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "vox.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "voxelcat.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vrchat.community", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "vrdennis.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -160147,7 +159875,6 @@
     { "name": "webal.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webcreative.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webdesigncompanyindia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "webdoxclm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "webeck-information-systems.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "weblightnovel.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "weblocus.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -160485,6 +160212,67 @@
     { "name": "go4rest.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "godark.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zuu.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "10961096.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "10minutesemail.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "18pee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "19gold.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "21hours.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "21pet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "21property.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "21stcenturyoptics.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "21up.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "21venture.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "247vision.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24action.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24alarm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24ball.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24control.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24fair.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24fan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24gis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24h.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24hod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24hunter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24images.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24meg.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24read.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24share.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24slot.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24status.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "24vod.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2boost.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2cv-co.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2handcar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2head.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2hypeenterprises.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2serious.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2steel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2target.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2tinteractive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2value.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "2x2x.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "7sec.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "7thcircledesigns.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "801hao.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "802hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "803hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "805hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "807hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "808mao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "809hao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "80kittens.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "888806.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "97m.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9ccn.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "9de.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "a-air.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aaa.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abacus-marketing.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abrange.inf.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "abuse.cat", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "narodne.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ncctouring.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zmsp.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc
index c0cb53d..6f30967 100644
--- a/net/url_request/http_with_dns_over_https_unittest.cc
+++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -39,6 +39,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -80,7 +81,6 @@
  public:
   HttpWithDnsOverHttpsTest()
       : host_resolver_proc_(new TestHostResolverProc()),
-        request_context_(true),
         test_server_(EmbeddedTestServer::Type::TYPE_HTTPS),
         test_https_requests_served_(0) {
     EmbeddedTestServer::ServerCertificateConfig cert_config;
@@ -108,23 +108,22 @@
     manager_options.dns_config_overrides.dns_over_https_config =
         *DnsOverHttpsConfig::FromString(doh_server_.GetPostOnlyTemplate());
     manager_options.dns_config_overrides.use_local_ipv6 = true;
-    resolver_ = HostResolver::CreateStandaloneContextResolver(
+    auto resolver = HostResolver::CreateStandaloneContextResolver(
         /*net_log=*/nullptr, manager_options);
 
     // Configure `resolver_` to use `host_resolver_proc_` to resolve
     // `doh_server_` itself. Additionally, without an explicit HostResolverProc,
     // HostResolverManager::HaveTestProcOverride disables the built-in DNS
     // client.
-    resolver_->SetProcParamsForTesting(
+    resolver->SetProcParamsForTesting(
         ProcTaskParams(host_resolver_proc_.get(), 1));
 
-    resolver_->SetRequestContext(&request_context_);
-    request_context_.set_host_resolver(resolver_.get());
-
-    request_context_.Init();
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_host_resolver(std::move(resolver));
+    request_context_ = context_builder->Build();
   }
 
-  URLRequestContext* context() { return &request_context_; }
+  URLRequestContext* context() { return request_context_.get(); }
 
   std::unique_ptr<test_server::HttpResponse> HandleDefaultRequest(
       const test_server::HttpRequest& request) {
@@ -137,9 +136,8 @@
   }
 
  protected:
-  std::unique_ptr<ContextHostResolver> resolver_;
   scoped_refptr<net::TestHostResolverProc> host_resolver_proc_;
-  TestURLRequestContext request_context_;
+  std::unique_ptr<URLRequestContext> request_context_;
   TestDohServer doh_server_;
   EmbeddedTestServer test_server_;
   uint32_t test_https_requests_served_;
@@ -202,7 +200,7 @@
 
   // Set up an idle socket.
   HttpTransactionFactory* transaction_factory =
-      request_context_.http_transaction_factory();
+      request_context_->http_transaction_factory();
   HttpStreamFactory::JobFactory default_job_factory;
   HttpNetworkSession* network_session = transaction_factory->GetSession();
   base::RunLoop loop;
diff --git a/net/url_request/report_sender_unittest.cc b/net/url_request/report_sender_unittest.cc
index a675f5bb..49d2695 100644
--- a/net/url_request/report_sender_unittest.cc
+++ b/net/url_request/report_sender_unittest.cc
@@ -20,6 +20,8 @@
 #include "net/test/url_request/url_request_mock_data_job.h"
 #include "net/test/url_request/url_request_mock_http_job.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -202,9 +204,11 @@
 
 class ReportSenderTest : public TestWithTaskEnvironment {
  public:
-  ReportSenderTest() : context_(true) {
-    context_.set_network_delegate(&network_delegate_);
-    context_.Init();
+  ReportSenderTest() {
+    auto builder = CreateTestURLRequestContextBuilder();
+    builder->set_network_delegate(
+        std::make_unique<TestReportSenderNetworkDelegate>());
+    context_ = builder->Build();
   }
 
   void SetUp() override {
@@ -218,7 +222,14 @@
 
   void TearDown() override { URLRequestFilter::GetInstance()->ClearHandlers(); }
 
-  TestURLRequestContext* context() { return &context_; }
+  URLRequestContext* context() { return context_.get(); }
+
+  TestReportSenderNetworkDelegate& network_delegate() {
+    // This cast is safe because we set a TestReportSenderNetworkDelegate in the
+    // constructor.
+    return *static_cast<TestReportSenderNetworkDelegate*>(
+        context_->network_delegate());
+  }
 
  protected:
   void SendReport(
@@ -232,15 +243,16 @@
         NetworkIsolationKey::CreateTransient();
 
     base::RunLoop run_loop;
-    network_delegate_.set_url_request_destroyed_callback(
+    network_delegate().set_url_request_destroyed_callback(
         run_loop.QuitClosure());
 
-    network_delegate_.set_expect_url(url);
-    network_delegate_.ExpectReport(report);
-    network_delegate_.set_expected_content_type("application/foobar");
-    network_delegate_.set_expected_network_isolation_key(network_isolation_key);
+    network_delegate().set_expect_url(url);
+    network_delegate().ExpectReport(report);
+    network_delegate().set_expected_content_type("application/foobar");
+    network_delegate().set_expected_network_isolation_key(
+        network_isolation_key);
 
-    EXPECT_EQ(request_sequence_number, network_delegate_.num_requests());
+    EXPECT_EQ(request_sequence_number, network_delegate().num_requests());
 
     reporter->Send(url, "application/foobar", report, network_isolation_key,
                    std::move(success_callback), std::move(error_callback));
@@ -250,7 +262,7 @@
     // sent.
     run_loop.Run();
 
-    EXPECT_EQ(request_sequence_number + 1, network_delegate_.num_requests());
+    EXPECT_EQ(request_sequence_number + 1, network_delegate().num_requests());
   }
 
   void SendReport(ReportSender* reporter,
@@ -262,10 +274,8 @@
                base::OnceCallback<void(const GURL&, int, int)>());
   }
 
-  TestReportSenderNetworkDelegate network_delegate_;
-
  private:
-  TestURLRequestContext context_;
+  std::unique_ptr<URLRequestContext> context_;
 };
 
 // Test that ReportSender::Send creates a URLRequest for the
@@ -285,18 +295,18 @@
 
 TEST_F(ReportSenderTest, SendMultipleReportsSimultaneously) {
   base::RunLoop run_loop;
-  network_delegate_.set_all_url_requests_destroyed_callback(
+  network_delegate().set_all_url_requests_destroyed_callback(
       run_loop.QuitClosure());
 
   GURL url = URLRequestMockDataJob::GetMockHttpsUrl("dummy data", 1);
-  network_delegate_.set_expect_url(url);
-  network_delegate_.ExpectReport(kDummyReport);
-  network_delegate_.ExpectReport(kSecondDummyReport);
-  network_delegate_.set_expected_content_type("application/foobar");
+  network_delegate().set_expect_url(url);
+  network_delegate().ExpectReport(kDummyReport);
+  network_delegate().ExpectReport(kSecondDummyReport);
+  network_delegate().set_expected_content_type("application/foobar");
 
   ReportSender reporter(context(), TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  EXPECT_EQ(0u, network_delegate_.num_requests());
+  EXPECT_EQ(0u, network_delegate().num_requests());
 
   reporter.Send(url, "application/foobar", kDummyReport, NetworkIsolationKey(),
                 base::OnceCallback<void()>(),
@@ -307,23 +317,23 @@
 
   run_loop.Run();
 
-  EXPECT_EQ(2u, network_delegate_.num_requests());
+  EXPECT_EQ(2u, network_delegate().num_requests());
 }
 
 // Test that pending URLRequests get cleaned up when the report sender
 // is deleted.
 TEST_F(ReportSenderTest, PendingRequestGetsDeleted) {
   bool url_request_destroyed = false;
-  network_delegate_.set_url_request_destroyed_callback(base::BindRepeating(
+  network_delegate().set_url_request_destroyed_callback(base::BindRepeating(
       &MarkURLRequestDestroyed, base::Unretained(&url_request_destroyed)));
 
   GURL url = URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
       URLRequestFailedJob::START, ERR_IO_PENDING);
-  network_delegate_.set_expect_url(url);
-  network_delegate_.ExpectReport(kDummyReport);
-  network_delegate_.set_expected_content_type("application/foobar");
+  network_delegate().set_expect_url(url);
+  network_delegate().ExpectReport(kDummyReport);
+  network_delegate().set_expected_content_type("application/foobar");
 
-  EXPECT_EQ(0u, network_delegate_.num_requests());
+  EXPECT_EQ(0u, network_delegate().num_requests());
 
   std::unique_ptr<ReportSender> reporter(
       new ReportSender(context(), TRAFFIC_ANNOTATION_FOR_TESTS));
@@ -332,7 +342,7 @@
                  base::OnceCallback<void(const GURL&, int, int)>());
   reporter.reset();
 
-  EXPECT_EQ(1u, network_delegate_.num_requests());
+  EXPECT_EQ(1u, network_delegate().num_requests());
   EXPECT_TRUE(url_request_destroyed);
 }
 
diff --git a/net/url_request/url_fetcher_impl_unittest.cc b/net/url_request/url_fetcher_impl_unittest.cc
index 4288aff..9e4dbb08 100644
--- a/net/url_request/url_fetcher_impl_unittest.cc
+++ b/net/url_request/url_fetcher_impl_unittest.cc
@@ -48,6 +48,8 @@
 #include "net/test/test_with_task_environment.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_test_util.h"
 #include "net/url_request/url_request_throttler_manager.h"
@@ -184,45 +186,13 @@
       FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt"));
 }
 
-// A TestURLRequestContext with a ThrottleManager and a MockHostResolver.
-class FetcherTestURLRequestContext : public TestURLRequestContext {
- public:
-  // All requests for |hanging_domain| will hang on host resolution until the
-  // mock_resolver()->ResolveAllPending() is called.
-  FetcherTestURLRequestContext(
-      const std::string& hanging_domain,
-      std::unique_ptr<ProxyResolutionService> proxy_resolution_service)
-      : TestURLRequestContext(true), mock_resolver_(new MockHostResolver()) {
-    mock_resolver_->set_ondemand_mode(true);
-    mock_resolver_->rules()->AddRule(hanging_domain, "127.0.0.1");
-    // Pass ownership to ContextStorage to ensure correct destruction order.
-    context_storage_.set_host_resolver(
-        std::unique_ptr<HostResolver>(mock_resolver_));
-    context_storage_.set_throttler_manager(
-        std::make_unique<URLRequestThrottlerManager>());
-    context_storage_.set_proxy_resolution_service(
-        std::move(proxy_resolution_service));
-    Init();
-  }
-
-  FetcherTestURLRequestContext(const FetcherTestURLRequestContext&) = delete;
-  FetcherTestURLRequestContext& operator=(const FetcherTestURLRequestContext&) =
-      delete;
-
-  MockHostResolver* mock_resolver() { return mock_resolver_; }
-
- private:
-  raw_ptr<MockHostResolver> mock_resolver_;
-};
-
 class FetcherTestURLRequestContextGetter : public URLRequestContextGetter {
  public:
   FetcherTestURLRequestContextGetter(
       scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
       const std::string& hanging_domain)
       : network_task_runner_(network_task_runner),
-        hanging_domain_(hanging_domain),
-        shutting_down_(false) {}
+        hanging_domain_(hanging_domain) {}
 
   FetcherTestURLRequestContextGetter(
       const FetcherTestURLRequestContextGetter&) = delete;
@@ -234,8 +204,14 @@
     on_destruction_callback_ = std::move(on_destruction_callback);
   }
 
+  MockHostResolver* mock_resolver() {
+    DCHECK(context_);
+    // This cast is safe because we set a MockHostResolver in the constructor.
+    return static_cast<MockHostResolver*>(context_->host_resolver());
+  }
+
   // URLRequestContextGetter:
-  FetcherTestURLRequestContext* GetURLRequestContext() override {
+  URLRequestContext* GetURLRequestContext() override {
     // Calling this on the wrong thread may be either a bug in the test or a bug
     // in production code.
     EXPECT_TRUE(network_task_runner_->BelongsToCurrentThread());
@@ -244,8 +220,19 @@
       return nullptr;
 
     if (!context_) {
-      context_ = std::make_unique<FetcherTestURLRequestContext>(
-          hanging_domain_, std::move(proxy_resolution_service_));
+      auto mock_resolver = std::make_unique<MockHostResolver>();
+      mock_resolver->set_ondemand_mode(true);
+      mock_resolver->rules()->AddRule(hanging_domain_, "127.0.0.1");
+
+      auto builder = CreateTestURLRequestContextBuilder();
+      builder->set_host_resolver(std::move(mock_resolver));
+      builder->set_throttling_enabled(true);
+      if (proxy_resolution_service_) {
+        builder->set_proxy_resolution_service(
+            std::move(proxy_resolution_service_));
+      }
+
+      context_ = builder->Build();
     }
 
     return context_.get();
@@ -310,9 +297,8 @@
     context_.reset();
   }
 
-  // Convenience method to access the context as a FetcherTestURLRequestContext
-  // without going through GetURLRequestContext.
-  FetcherTestURLRequestContext* context() {
+  // Convenience method to access the context.
+  URLRequestContext* context() {
     DCHECK(network_task_runner_->BelongsToCurrentThread());
     return context_.get();
   }
@@ -339,8 +325,8 @@
   // May be null.
   std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
 
-  std::unique_ptr<FetcherTestURLRequestContext> context_;
-  bool shutting_down_;
+  std::unique_ptr<URLRequestContext> context_;
+  bool shutting_down_ = false;
 
   base::OnceClosure on_destruction_callback_;
 };
@@ -630,7 +616,7 @@
       CreateSameThreadContextGetter());
   // Force context creation.
   context_getter->GetURLRequestContext();
-  MockHostResolver* mock_resolver = context_getter->context()->mock_resolver();
+  MockHostResolver* mock_resolver = context_getter->mock_resolver();
 
   WaitingURLFetcherDelegate delegate;
   delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter);
@@ -652,7 +638,7 @@
       CreateSameThreadContextGetter());
   // Force context creation.
   context_getter->GetURLRequestContext();
-  MockHostResolver* mock_resolver = context_getter->context()->mock_resolver();
+  MockHostResolver* mock_resolver = context_getter->mock_resolver();
 
   WaitingURLFetcherDelegate delegate;
   delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter);
@@ -684,7 +670,7 @@
       CreateSameThreadContextGetter());
   // Force context creation.
   context_getter->GetURLRequestContext();
-  MockHostResolver* mock_resolver = context_getter->context()->mock_resolver();
+  MockHostResolver* mock_resolver = context_getter->mock_resolver();
 
   WaitingURLFetcherDelegate delegate;
   delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter);
@@ -730,7 +716,7 @@
       CreateSameThreadContextGetter());
   // Force context creation.
   context_getter->GetURLRequestContext();
-  MockHostResolver* mock_resolver = context_getter->context()->mock_resolver();
+  MockHostResolver* mock_resolver = context_getter->mock_resolver();
 
   WaitingURLFetcherDelegate delegate;
   delegate.CreateFetcher(hanging_url(), URLFetcher::GET, context_getter);
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index e210e0d..34bec81 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -295,6 +295,7 @@
 void URLRequestContextBuilder::SetCreateHttpTransactionFactoryCallback(
     CreateHttpTransactionFactoryCallback
         create_http_network_transaction_factory) {
+  http_transaction_factory_.reset();
   create_http_network_transaction_factory_ =
       std::move(create_http_network_transaction_factory);
 }
@@ -563,7 +564,9 @@
       http_network_session_params_, network_session_context));
 
   std::unique_ptr<HttpTransactionFactory> http_transaction_factory;
-  if (!create_http_network_transaction_factory_.is_null()) {
+  if (http_transaction_factory_) {
+    http_transaction_factory = std::move(http_transaction_factory_);
+  } else if (!create_http_network_transaction_factory_.is_null()) {
     http_transaction_factory =
         std::move(create_http_network_transaction_factory_)
             .Run(storage->http_network_session());
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index 43223f0..4b002f96 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -322,6 +322,13 @@
       CreateHttpTransactionFactoryCallback
           create_http_network_transaction_factory);
 
+  template <typename T>
+  T* SetHttpTransactionFactoryForTesting(std::unique_ptr<T> factory) {
+    create_http_network_transaction_factory_.Reset();
+    http_transaction_factory_ = std::move(factory);
+    return static_cast<T*>(http_transaction_factory_.get());
+  }
+
   // Sets a ClientSocketFactory so a test can mock out sockets. This must
   // outlive the URLRequestContext that will be built.
   void set_client_socket_factory_for_testing(
@@ -387,6 +394,7 @@
   HttpCacheParams http_cache_params_;
   HttpNetworkSessionParams http_network_session_params_;
   CreateHttpTransactionFactoryCallback create_http_network_transaction_factory_;
+  std::unique_ptr<HttpTransactionFactory> http_transaction_factory_;
   base::FilePath transport_security_persister_file_path_;
   std::vector<std::string> hsts_policy_bypass_list_;
   raw_ptr<NetLog> net_log_ = nullptr;
diff --git a/net/url_request/url_request_filter_unittest.cc b/net/url_request/url_request_filter_unittest.cc
index 91fd91a..2f653dc 100644
--- a/net/url_request/url_request_filter_unittest.cc
+++ b/net/url_request/url_request_filter_unittest.cc
@@ -13,6 +13,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_interceptor.h"
 #include "net/url_request/url_request_job.h"
 #include "net/url_request/url_request_test_job.h"
@@ -53,15 +54,15 @@
   base::test::TaskEnvironment task_environment(
       base::test::TaskEnvironment::MainThreadType::IO);
   TestDelegate delegate;
-  TestURLRequestContext request_context;
+  auto context = CreateTestURLRequestContextBuilder()->Build();
   URLRequestFilter* filter = URLRequestFilter::GetInstance();
 
   const GURL kUrl1("http://foo.com/");
-  std::unique_ptr<URLRequest> request1(request_context.CreateRequest(
+  std::unique_ptr<URLRequest> request1(context->CreateRequest(
       kUrl1, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
 
   const GURL kUrl2("http://bar.com/");
-  std::unique_ptr<URLRequest> request2(request_context.CreateRequest(
+  std::unique_ptr<URLRequest> request2(context->CreateRequest(
       kUrl2, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
 
   // Check AddUrlInterceptor checks for invalid URLs.
diff --git a/net/url_request/url_request_fuzzer.cc b/net/url_request/url_request_fuzzer.cc
index 8a191ec6..fca705d 100644
--- a/net/url_request/url_request_fuzzer.cc
+++ b/net/url_request/url_request_fuzzer.cc
@@ -17,6 +17,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "url/gurl.h"
 
@@ -37,17 +38,18 @@
     return 0;
 
   FuzzedDataProvider data_provider(data, size);
-  net::TestURLRequestContext url_request_context(true);
+  auto context_builder = net::CreateTestURLRequestContextBuilder();
   net::FuzzedSocketFactory fuzzed_socket_factory(&data_provider);
-  url_request_context.set_client_socket_factory(&fuzzed_socket_factory);
-  url_request_context.Init();
+  context_builder->set_client_socket_factory_for_testing(
+      &fuzzed_socket_factory);
+  auto url_request_context = context_builder->Build();
 
   net::TestDelegate delegate;
 
   std::unique_ptr<net::URLRequest> url_request(
-      url_request_context.CreateRequest(GURL("http://foo/"),
-                                        net::DEFAULT_PRIORITY, &delegate,
-                                        TRAFFIC_ANNOTATION_FOR_TESTS));
+      url_request_context->CreateRequest(GURL("http://foo/"),
+                                         net::DEFAULT_PRIORITY, &delegate,
+                                         TRAFFIC_ANNOTATION_FOR_TESTS));
   url_request->Start();
   // TestDelegate quits the message loop on completion.
   base::RunLoop().Run();
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index ac8891c0..52d93b4 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -52,6 +52,8 @@
 #include "net/test/test_with_task_environment.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "net/url_request/websocket_handshake_userdata_key.h"
 #include "net/websockets/websocket_test_util.h"
@@ -131,15 +133,16 @@
 
 class URLRequestHttpJobSetUpSourceTest : public TestWithTaskEnvironment {
  public:
-  URLRequestHttpJobSetUpSourceTest() : context_(true) {
-    context_.set_client_socket_factory(&socket_factory_);
-    context_.Init();
+  URLRequestHttpJobSetUpSourceTest() {
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_client_socket_factory_for_testing(&socket_factory_);
+    context_ = context_builder->Build();
   }
 
  protected:
   MockClientSocketFactory socket_factory_;
 
-  TestURLRequestContext context_;
+  std::unique_ptr<URLRequestContext> context_;
   TestDelegate delegate_;
 };
 
@@ -154,8 +157,8 @@
   socket_factory_.AddSocketDataProvider(&socket_data);
 
   std::unique_ptr<URLRequest> request =
-      context_.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
-                             &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
+      context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
+                              &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
   auto job = std::make_unique<TestURLRequestHttpJob>(request.get());
   job->set_use_null_source_stream(true);
   TestScopedURLInterceptor interceptor(request->url(), std::move(job));
@@ -178,8 +181,8 @@
   socket_factory_.AddSocketDataProvider(&socket_data);
 
   std::unique_ptr<URLRequest> request =
-      context_.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
-                             &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
+      context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
+                              &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
   auto job = std::make_unique<TestURLRequestHttpJob>(request.get());
   TestScopedURLInterceptor interceptor(request->url(), std::move(job));
   request->Start();
@@ -197,13 +200,14 @@
 class URLRequestHttpJobWithProxy {
  public:
   explicit URLRequestHttpJobWithProxy(
-      std::unique_ptr<ProxyResolutionService> proxy_resolution_service)
-      : proxy_resolution_service_(std::move(proxy_resolution_service)),
-        context_(new TestURLRequestContext(true)) {
-    context_->set_client_socket_factory(&socket_factory_);
-    context_->set_network_delegate(&network_delegate_);
-    context_->set_proxy_resolution_service(proxy_resolution_service_.get());
-    context_->Init();
+      std::unique_ptr<ProxyResolutionService> proxy_resolution_service) {
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_client_socket_factory_for_testing(&socket_factory_);
+    if (proxy_resolution_service) {
+      context_builder->set_proxy_resolution_service(
+          std::move(proxy_resolution_service));
+    }
+    context_ = context_builder->Build();
   }
 
   URLRequestHttpJobWithProxy(const URLRequestHttpJobWithProxy&) = delete;
@@ -211,9 +215,7 @@
       delete;
 
   MockClientSocketFactory socket_factory_;
-  TestNetworkDelegate network_delegate_;
-  std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
-  std::unique_ptr<TestURLRequestContext> context_;
+  std::unique_ptr<URLRequestContext> context_;
 };
 
 // Tests that when proxy is not used, the proxy server is set correctly on the
@@ -340,19 +342,37 @@
 
 class URLRequestHttpJobTest : public TestWithTaskEnvironment {
  protected:
-  URLRequestHttpJobTest() : context_(true) {
-    context_.set_http_transaction_factory(&network_layer_);
-    context_.set_net_log(NetLog::Get());
-    context_.Init();
+  URLRequestHttpJobTest() {
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->SetHttpTransactionFactoryForTesting(
+        std::make_unique<MockNetworkLayer>());
+    context_builder->DisableHttpCache();
+    context_builder->set_net_log(NetLog::Get());
+    context_ = context_builder->Build();
 
-    req_ =
-        context_.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
-                               &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
+    req_ = context_->CreateRequest(GURL("http://www.example.com"),
+                                   DEFAULT_PRIORITY, &delegate_,
+                                   TRAFFIC_ANNOTATION_FOR_TESTS);
   }
 
-  MockNetworkLayer network_layer_;
+  MockNetworkLayer& network_layer() {
+    // This cast is safe because we set a MockNetworkLayer in the constructor.
+    return *static_cast<MockNetworkLayer*>(
+        context_->http_transaction_factory());
+  }
 
-  TestURLRequestContext context_;
+  std::unique_ptr<URLRequest> CreateFirstPartyRequest(
+      const URLRequestContext& context,
+      const GURL& url,
+      URLRequest::Delegate* delegate) {
+    auto req = context.CreateRequest(url, DEFAULT_PRIORITY, delegate,
+                                     TRAFFIC_ANNOTATION_FOR_TESTS);
+    req->set_initiator(url::Origin::Create(url));
+    req->set_site_for_cookies(SiteForCookies::FromUrl(url));
+    return req;
+  }
+
+  std::unique_ptr<URLRequestContext> context_;
   TestDelegate delegate_;
   RecordingNetLogObserver net_log_observer_;
   std::unique_ptr<URLRequest> req_;
@@ -360,16 +380,14 @@
 
 class URLRequestHttpJobWithMockSocketsTest : public TestWithTaskEnvironment {
  protected:
-  URLRequestHttpJobWithMockSocketsTest()
-      : context_(new TestURLRequestContext(true)) {
-    context_->set_client_socket_factory(&socket_factory_);
-    context_->set_network_delegate(&network_delegate_);
-    context_->Init();
+  URLRequestHttpJobWithMockSocketsTest() {
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_client_socket_factory_for_testing(&socket_factory_);
+    context_ = context_builder->Build();
   }
 
   MockClientSocketFactory socket_factory_;
-  TestNetworkDelegate network_delegate_;
-  std::unique_ptr<TestURLRequestContext> context_;
+  std::unique_ptr<URLRequestContext> context_;
 };
 
 TEST_F(URLRequestHttpJobWithMockSocketsTest,
@@ -1039,15 +1057,14 @@
 }
 
 TEST_F(URLRequestHttpJobTest, TestCancelWhileReadingCookies) {
-  DelayedCookieMonster cookie_monster;
-  TestURLRequestContext context(true);
-  context.set_cookie_store(&cookie_monster);
-  context.Init();
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(std::make_unique<DelayedCookieMonster>());
+  auto context = context_builder->Build();
 
   TestDelegate delegate;
   std::unique_ptr<URLRequest> request =
-      context.CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
-                            &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+      context->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
+                             &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
 
   request->Start();
   request->Cancel();
@@ -1076,12 +1093,12 @@
       req_->url(), std::make_unique<TestURLRequestHttpJob>(req_.get()));
   req_->SetPriority(LOW);
 
-  EXPECT_FALSE(network_layer_.last_transaction());
+  EXPECT_FALSE(network_layer().last_transaction());
 
   req_->Start();
 
-  ASSERT_TRUE(network_layer_.last_transaction());
-  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
+  ASSERT_TRUE(network_layer().last_transaction());
+  EXPECT_EQ(LOW, network_layer().last_transaction()->priority());
 }
 
 // Make sure that URLRequestHttpJob passes on its priority updates to
@@ -1091,20 +1108,20 @@
       req_->url(), std::make_unique<TestURLRequestHttpJob>(req_.get()));
   req_->SetPriority(LOW);
   req_->Start();
-  ASSERT_TRUE(network_layer_.last_transaction());
-  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
+  ASSERT_TRUE(network_layer().last_transaction());
+  EXPECT_EQ(LOW, network_layer().last_transaction()->priority());
 
   req_->SetPriority(HIGHEST);
-  EXPECT_EQ(HIGHEST, network_layer_.last_transaction()->priority());
+  EXPECT_EQ(HIGHEST, network_layer().last_transaction()->priority());
 }
 
 TEST_F(URLRequestHttpJobTest, HSTSInternalRedirectTest) {
   // Setup HSTS state.
-  context_.transport_security_state()->AddHSTS(
+  context_->transport_security_state()->AddHSTS(
       "upgrade.test", base::Time::Now() + base::Seconds(10), true);
   ASSERT_TRUE(
-      context_.transport_security_state()->ShouldUpgradeToSSL("upgrade.test"));
-  ASSERT_FALSE(context_.transport_security_state()->ShouldUpgradeToSSL(
+      context_->transport_security_state()->ShouldUpgradeToSSL("upgrade.test"));
+  ASSERT_FALSE(context_->transport_security_state()->ShouldUpgradeToSSL(
       "no-upgrade.test"));
 
   struct TestCase {
@@ -1134,7 +1151,7 @@
 
     TestDelegate d;
     TestNetworkDelegate network_delegate;
-    std::unique_ptr<URLRequest> r(context_.CreateRequest(
+    std::unique_ptr<URLRequest> r(context_->CreateRequest(
         url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS,
         is_for_websockets));
 
@@ -1165,11 +1182,11 @@
   https_test.AddDefaultHandlers(base::FilePath());
   ASSERT_TRUE(https_test.Start());
 
-  TestURLRequestContext context;
-  context.transport_security_state()->AddHSTS(
+  auto context = CreateTestURLRequestContextBuilder()->Build();
+  context->transport_security_state()->AddHSTS(
       "127.0.0.1", base::Time::Now() + base::Seconds(10), true);
   ASSERT_TRUE(
-      context.transport_security_state()->ShouldUpgradeToSSL("127.0.0.1"));
+      context->transport_security_state()->ShouldUpgradeToSSL("127.0.0.1"));
 
   GURL::Replacements replace_scheme;
   replace_scheme.SetSchemeStr("http");
@@ -1183,7 +1200,7 @@
 
     HttpRawRequestHeaders raw_req_headers;
 
-    std::unique_ptr<URLRequest> r(context.CreateRequest(
+    std::unique_ptr<URLRequest> r(context->CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetExtraRequestHeaders(extra_headers);
     r->SetRequestHeadersCallback(base::BindRepeating(
@@ -1214,7 +1231,7 @@
 
     HttpRawRequestHeaders raw_req_headers;
 
-    std::unique_ptr<URLRequest> r(context.CreateRequest(
+    std::unique_ptr<URLRequest> r(context->CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetRequestHeadersCallback(base::BindRepeating(
         &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
@@ -1233,7 +1250,7 @@
 
     HttpRawRequestHeaders raw_req_headers;
 
-    std::unique_ptr<URLRequest> r(context.CreateRequest(
+    std::unique_ptr<URLRequest> r(context->CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetRequestHeadersCallback(base::BindRepeating(
         &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
@@ -1247,17 +1264,17 @@
 
 class URLRequestHttpJobWithBrotliSupportTest : public TestWithTaskEnvironment {
  protected:
-  URLRequestHttpJobWithBrotliSupportTest()
-      : context_(new TestURLRequestContext(true)) {
-    auto params = std::make_unique<HttpNetworkSessionParams>();
-    context_->set_enable_brotli(true);
-    context_->set_http_network_session_params(std::move(params));
-    context_->set_client_socket_factory(&socket_factory_);
-    context_->Init();
+  URLRequestHttpJobWithBrotliSupportTest() {
+    HttpNetworkSessionParams params;
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_enable_brotli(true);
+    context_builder->set_http_network_session_params(params);
+    context_builder->set_client_socket_factory_for_testing(&socket_factory_);
+    context_ = context_builder->Build();
   }
 
   MockClientSocketFactory socket_factory_;
-  std::unique_ptr<TestURLRequestContext> context_;
+  std::unique_ptr<URLRequestContext> context_;
 };
 
 TEST_F(URLRequestHttpJobWithBrotliSupportTest, NoBrotliAdvertisementOverHttp) {
@@ -1393,7 +1410,7 @@
 
 #if BUILDFLAG(IS_ANDROID)
 TEST_F(URLRequestHttpJobTest, AndroidCleartextPermittedTest) {
-  context_.set_check_cleartext_permitted(true);
+  context_->set_check_cleartext_permitted(true);
 
   static constexpr struct TestCase {
     const char* url;
@@ -1424,8 +1441,8 @@
 
     TestDelegate delegate;
     std::unique_ptr<URLRequest> request =
-        context_.CreateRequest(GURL(test.url), DEFAULT_PRIORITY, &delegate,
-                               TRAFFIC_ANNOTATION_FOR_TESTS);
+        context_->CreateRequest(GURL(test.url), DEFAULT_PRIORITY, &delegate,
+                                TRAFFIC_ANNOTATION_FOR_TESTS);
     request->Start();
     delegate.RunUntilComplete();
 
@@ -1450,21 +1467,17 @@
 
 class URLRequestHttpJobWebSocketTest : public TestWithTaskEnvironment {
  protected:
-  URLRequestHttpJobWebSocketTest() : context_(true) {
-    context_.set_network_delegate(&network_delegate_);
-    context_.set_client_socket_factory(&socket_factory_);
-    context_.Init();
+  URLRequestHttpJobWebSocketTest() {
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_client_socket_factory_for_testing(&socket_factory_);
+    context_ = context_builder->Build();
     req_ =
-        context_.CreateRequest(GURL("ws://www.example.org"), DEFAULT_PRIORITY,
-                               &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS,
-                               /*is_for_websockets=*/true);
+        context_->CreateRequest(GURL("ws://www.example.org"), DEFAULT_PRIORITY,
+                                &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS,
+                                /*is_for_websockets=*/true);
   }
 
-  // A Network Delegate is required for the WebSocketHandshakeStreamBase
-  // object to be passed on to the HttpNetworkTransaction.
-  TestNetworkDelegate network_delegate_;
-
-  TestURLRequestContext context_;
+  std::unique_ptr<URLRequestContext> context_;
   MockClientSocketFactory socket_factory_;
   TestDelegate delegate_;
   std::unique_ptr<URLRequest> req_;
@@ -1550,7 +1563,7 @@
   return callback.result().status.IsInclude();
 }
 
-void RunRequest(TestURLRequestContext* context, const GURL& url) {
+void RunRequest(URLRequestContext* context, const GURL& url) {
   TestDelegate delegate;
   std::unique_ptr<URLRequest> request = context->CreateRequest(
       url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
@@ -1568,11 +1581,13 @@
   base::HistogramTester histograms;
   const std::string test_histogram = "Cookie.CookieSchemeRequestScheme";
 
-  CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr,
-                   /*first_party_sets_enabled=*/false);
-  TestURLRequestContext context(true);
-  context.set_cookie_store(&cm);
-  context.Init();
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(std::make_unique<CookieMonster>(
+      /*store=*/nullptr, /*net_log=*/nullptr,
+      /*first_party_sets_enabled=*/false));
+  auto context = context_builder->Build();
+
+  auto* cookie_store = static_cast<CookieMonster*>(context->cookie_store());
 
   // Secure set cookie marked as Unset source scheme.
   // Using port 7 because it fails the transaction without sending a request and
@@ -1593,12 +1608,12 @@
   unset_cookie1->SetSourceScheme(net::CookieSourceScheme::kUnset);
 
   CookieList list1 = {*unset_cookie1};
-  EXPECT_TRUE(SetAllCookies(&cm, list1));
-  RunRequest(&context, nonsecure_url_for_unset1);
+  EXPECT_TRUE(SetAllCookies(cookie_store, list1));
+  RunRequest(context.get(), nonsecure_url_for_unset1);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 1);
-  RunRequest(&context, secure_url_for_unset1);
+  RunRequest(context.get(), secure_url_for_unset1);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 2);
@@ -1614,12 +1629,12 @@
   unset_cookie2->SetSourceScheme(net::CookieSourceScheme::kUnset);
 
   CookieList list2 = {*unset_cookie2};
-  EXPECT_TRUE(SetAllCookies(&cm, list2));
-  RunRequest(&context, nonsecure_url_for_unset2);
+  EXPECT_TRUE(SetAllCookies(cookie_store, list2));
+  RunRequest(context.get(), nonsecure_url_for_unset2);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 3);
-  RunRequest(&context, secure_url_for_unset2);
+  RunRequest(context.get(), secure_url_for_unset2);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 4);
@@ -1628,13 +1643,13 @@
   GURL nonsecure_url_for_secure_set("http://secureset.example:7");
   GURL secure_url_for_secure_set("https://secureset.example:7");
 
-  EXPECT_TRUE(
-      CreateAndSetCookie(&cm, secure_url_for_secure_set, "SecureScheme=val"));
-  RunRequest(&context, nonsecure_url_for_secure_set);
+  EXPECT_TRUE(CreateAndSetCookie(cookie_store, secure_url_for_secure_set,
+                                 "SecureScheme=val"));
+  RunRequest(context.get(), nonsecure_url_for_secure_set);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kSecureSetNonsecureRequest, 1);
-  RunRequest(&context, secure_url_for_secure_set);
+  RunRequest(context.get(), secure_url_for_secure_set);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kSecureSetSecureRequest, 1);
@@ -1643,13 +1658,13 @@
   GURL nonsecure_url_for_nonsecure_set("http://nonsecureset.example:7");
   GURL secure_url_for_nonsecure_set("https://nonsecureset.example:7");
 
-  EXPECT_TRUE(CreateAndSetCookie(&cm, nonsecure_url_for_nonsecure_set,
+  EXPECT_TRUE(CreateAndSetCookie(cookie_store, nonsecure_url_for_nonsecure_set,
                                  "NonSecureScheme=val"));
-  RunRequest(&context, nonsecure_url_for_nonsecure_set);
+  RunRequest(context.get(), nonsecure_url_for_nonsecure_set);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kNonsecureSetNonsecureRequest, 1);
-  RunRequest(&context, secure_url_for_nonsecure_set);
+  RunRequest(context.get(), secure_url_for_nonsecure_set);
   histograms.ExpectBucketCount(
       test_histogram,
       URLRequestHttpJob::CookieRequestScheme::kNonsecureSetSecureRequest, 1);
@@ -1661,13 +1676,13 @@
   HttpTestServer test_server;
   ASSERT_TRUE(test_server.Start());
 
-  FilteringTestNetworkDelegate network_delegate;
-  CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr,
-                   /*first_party_sets_enabled=*/false);
-  TestURLRequestContext context(true);
-  context.set_cookie_store(&cm);
-  context.set_network_delegate(&network_delegate);
-  context.Init();
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(std::make_unique<CookieMonster>(
+      /*store=*/nullptr, /*net_log=*/nullptr,
+      /*first_party_sets_enabled=*/false));
+  auto& network_delegate = *context_builder->set_network_delegate(
+      std::make_unique<FilteringTestNetworkDelegate>());
+  auto context = context_builder->Build();
 
   // Set cookies.
   {
@@ -1676,8 +1691,8 @@
         "/set-cookie?one=1&"
         "two=2&"
         "three=3");
-    std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
-        test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+    std::unique_ptr<URLRequest> req =
+        CreateFirstPartyRequest(*context, test_url, &d);
     req->Start();
     d.RunUntilComplete();
   }
@@ -1690,9 +1705,8 @@
   // `allow_credentials`, since that skips querying the cookie store).
   network_delegate.set_force_privacy_mode(true);
   TestDelegate d;
-  std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
-      test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
-      TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<URLRequest> req = CreateFirstPartyRequest(
+      *context, test_server.GetURL("/echoheader?Cookie"), &d);
   req->Start();
   d.RunUntilComplete();
 
@@ -1731,15 +1745,15 @@
   HttpTestServer test_server;
   ASSERT_TRUE(test_server.Start());
 
-  FilteringTestNetworkDelegate network_delegate;
-  network_delegate.set_block_get_cookies_by_name(true);
-  network_delegate.SetCookieFilter("blocked_");
-  CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr,
-                   /*first_party_sets_enabled=*/false);
-  TestURLRequestContext context(true);
-  context.set_cookie_store(&cm);
-  context.set_network_delegate(&network_delegate);
-  context.Init();
+  auto network_delegate = std::make_unique<FilteringTestNetworkDelegate>();
+  network_delegate->set_block_get_cookies_by_name(true);
+  network_delegate->SetCookieFilter("blocked_");
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(std::make_unique<CookieMonster>(
+      /*store=*/nullptr, /*net_log=*/nullptr,
+      /*first_party_sets_enabled=*/false));
+  context_builder->set_network_delegate(std::move(network_delegate));
+  auto context = context_builder->Build();
 
   // Set cookies.
   {
@@ -1748,17 +1762,16 @@
         "/set-cookie?blocked_one=1;SameSite=Lax;Secure&"
         "blocked_two=1;SameSite=Lax;Secure&"
         "allowed=1;SameSite=Lax;Secure");
-    std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
-        test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+    std::unique_ptr<URLRequest> req =
+        CreateFirstPartyRequest(*context, test_url, &d);
     req->Start();
     d.RunUntilComplete();
   }
 
   // Get cookies.
   TestDelegate d;
-  std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
-      test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
-      TRAFFIC_ANNOTATION_FOR_TESTS));
+  std::unique_ptr<URLRequest> req = CreateFirstPartyRequest(
+      *context, test_server.GetURL("/echoheader?Cookie"), &d);
   req->Start();
   d.RunUntilComplete();
 
@@ -1811,13 +1824,14 @@
   https_test.AddDefaultHandlers(base::FilePath());
   ASSERT_TRUE(https_test.Start());
 
-  TestURLRequestContext context;
-  CookieMonster cookie_monster(nullptr, nullptr,
-                               false /* first_party_sets_enabled */);
-  context.set_cookie_store(&cookie_monster);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(std::make_unique<CookieMonster>(
+      /*store=*/nullptr, /*net_log=*/nullptr,
+      /*first_party_sets_enabled=*/false));
+  auto context = context_builder->Build();
 
   TestDelegate delegate;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       https_test.GetURL("/set-cookie?__Host-foo=bar;SameSite=None;Secure;Path=/"
                         ";Partitioned;"),
       DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
@@ -1834,7 +1848,7 @@
 
   {  // Test request from the same top-level site.
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kTestIsolationInfo);
@@ -1850,7 +1864,7 @@
         IsolationInfo::CreateForInternalRequest(kOtherTopFrameOrigin);
 
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kOtherTestIsolationInfo);
@@ -1894,16 +1908,18 @@
   first_party_sets.insert(std::make_pair(
       kOwnerSite, std::set<SchemefulSite>({kOwnerSite, kMemberSite})));
 
-  TestURLRequestContext context;
-  CookieMonster cookie_monster(nullptr, nullptr,
-                               false /* first_party_sets_enabled */);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto cookie_monster = std::make_unique<CookieMonster>(
+      /*store=*/nullptr, /*net_log=*/nullptr,
+      /*first_party_sets_enabled=*/false);
   auto cookie_access_delegate = std::make_unique<TestCookieAccessDelegate>();
   cookie_access_delegate->SetFirstPartySets(first_party_sets);
-  cookie_monster.SetCookieAccessDelegate(std::move(cookie_access_delegate));
-  context.set_cookie_store(&cookie_monster);
+  cookie_monster->SetCookieAccessDelegate(std::move(cookie_access_delegate));
+  context_builder->SetCookieStore(std::move(cookie_monster));
+  auto context = context_builder->Build();
 
   TestDelegate delegate;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       https_test.GetURL("/set-cookie?__Host-foo=0;SameSite=None;Secure;Path=/"
                         ";Partitioned;"),
       DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
@@ -1918,7 +1934,7 @@
     // Test the cookie is present in a request with the same top-frame site as
     // when the cookie was set.
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kOwnerIsolationInfo);
@@ -1931,7 +1947,7 @@
     // Requests whose top-frame site are in the set should have access to the
     // partitioned cookie.
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kMemberIsolationInfo);
@@ -1941,7 +1957,7 @@
   }
 
   // Set a cookie from the member site.
-  req = context.CreateRequest(
+  req = context->CreateRequest(
       https_test.GetURL("/set-cookie?__Host-bar=1;SameSite=None;Secure;Path=/"
                         ";Partitioned;"),
       DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
@@ -1954,7 +1970,7 @@
     // Check request whose top-frame site is the owner site has the cookie set
     // on the member site.
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kOwnerIsolationInfo);
@@ -1968,7 +1984,7 @@
     // in the set. If partitioned cookies are disabled, then the cookies should
     // be available.
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kNonMemberIsolationInfo);
@@ -1987,13 +2003,13 @@
   https_test.AddDefaultHandlers(base::FilePath());
   ASSERT_TRUE(https_test.Start());
 
-  FilteringTestNetworkDelegate network_delegate;
-  CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr,
-                   /*first_party_sets_enabled=*/false);
-  TestURLRequestContext context(true);
-  context.set_cookie_store(&cm);
-  context.set_network_delegate(&network_delegate);
-  context.Init();
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(
+      std::make_unique<CookieMonster>(/*store=*/nullptr, /*net_log=*/nullptr,
+                                      /*first_party_sets_enabled=*/false));
+  auto& network_delegate = *context_builder->set_network_delegate(
+      std::make_unique<FilteringTestNetworkDelegate>());
+  auto context = context_builder->Build();
 
   const url::Origin kTopFrameOrigin =
       url::Origin::Create(GURL("https://www.toplevelsite.com"));
@@ -2002,7 +2018,7 @@
 
   // Set an unpartitioned and partitioned cookie.
   TestDelegate delegate;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       https_test.GetURL(
           "/set-cookie?__Host-partitioned=0;SameSite=None;Secure;Path=/"
           ";Partitioned;&__Host-unpartitioned=1;SameSite=None;Secure;Path=/"),
@@ -2014,7 +2030,7 @@
 
   {  // Get both cookies when privacy mode is disabled.
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kTestIsolationInfo);
@@ -2028,7 +2044,7 @@
     network_delegate.set_force_privacy_mode(true);
     network_delegate.set_partitioned_state_allowed(true);
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kTestIsolationInfo);
@@ -2063,7 +2079,7 @@
     network_delegate.set_force_privacy_mode(true);
     network_delegate.set_partitioned_state_allowed(false);
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
         TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kTestIsolationInfo);
@@ -2098,13 +2114,14 @@
   https_test.AddDefaultHandlers(base::FilePath());
   ASSERT_TRUE(https_test.Start());
 
-  TestURLRequestContext context;
-  CookieMonster cookie_monster(nullptr, nullptr,
-                               false /* first_party_sets_enabled */);
-  context.set_cookie_store(&cookie_monster);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(
+      std::make_unique<CookieMonster>(/*store=*/nullptr, /*net_log=*/nullptr,
+                                      /*first_party_sets_enabled=*/false));
+  auto context = context_builder->Build();
 
   TestDelegate delegate;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       https_test.GetURL("/set-cookie?__Host-foo=bar;SameSite=None;Secure;Path=/"
                         ";Partitioned;"),
       DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
@@ -2121,7 +2138,7 @@
 
   {  // Test request from the same top-level site.
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?sec-ch-partitioned-cookies"),
         DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kTestIsolationInfo);
@@ -2141,7 +2158,7 @@
         IsolationInfo::CreateForInternalRequest(kOtherTopFrameOrigin);
 
     TestDelegate delegate;
-    std::unique_ptr<URLRequest> req(context.CreateRequest(
+    std::unique_ptr<URLRequest> req(context->CreateRequest(
         https_test.GetURL("/echoheader?sec-ch-partitioned-cookies"),
         DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     req->set_isolation_info(kOtherTestIsolationInfo);
diff --git a/net/url_request/url_request_job_factory_unittest.cc b/net/url_request/url_request_job_factory_unittest.cc
index 9d2faec..f52c6635 100644
--- a/net/url_request/url_request_job_factory_unittest.cc
+++ b/net/url_request/url_request_job_factory_unittest.cc
@@ -15,6 +15,8 @@
 #include "net/test/gtest_util.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_job.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -58,10 +60,10 @@
   base::test::TaskEnvironment task_environment(
       base::test::TaskEnvironment::MainThreadType::IO);
   TestDelegate delegate;
-  TestURLRequestContext request_context;
+  auto request_context = CreateTestURLRequestContextBuilder()->Build();
   std::unique_ptr<URLRequest> request(
-      request_context.CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY,
-                                    &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+      request_context->CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY,
+                                     &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
   request->Start();
 
   base::RunLoop().Run();
@@ -72,31 +74,19 @@
   base::test::TaskEnvironment task_environment(
       base::test::TaskEnvironment::MainThreadType::IO);
   TestDelegate delegate;
-  URLRequestJobFactory job_factory;
-  TestURLRequestContext request_context;
-  request_context.set_job_factory(&job_factory);
-  job_factory.SetProtocolHandler("foo",
-                                 std::make_unique<DummyProtocolHandler>());
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetProtocolHandler("foo",
+                                      std::make_unique<DummyProtocolHandler>());
+  auto request_context = context_builder->Build();
   std::unique_ptr<URLRequest> request(
-      request_context.CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY,
-                                    &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+      request_context->CreateRequest(GURL("foo://bar"), DEFAULT_PRIORITY,
+                                     &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
   request->Start();
 
   base::RunLoop().Run();
   EXPECT_EQ(OK, delegate.request_status());
 }
 
-TEST(URLRequestJobFactoryTest, DeleteProtocolHandler) {
-  base::test::TaskEnvironment task_environment(
-      base::test::TaskEnvironment::MainThreadType::IO);
-  URLRequestJobFactory job_factory;
-  TestURLRequestContext request_context;
-  request_context.set_job_factory(&job_factory);
-  job_factory.SetProtocolHandler("foo",
-                                 std::make_unique<DummyProtocolHandler>());
-  job_factory.SetProtocolHandler("foo", nullptr);
-}
-
 }  // namespace
 
 }  // namespace net
diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc
index 51942c4..6eacf022 100644
--- a/net/url_request/url_request_job_unittest.cc
+++ b/net/url_request/url_request_job_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "net/base/features.h"
 #include "net/base/request_priority.h"
@@ -18,6 +19,8 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/referrer_policy.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -291,14 +294,16 @@
 using URLRequestJobTest = TestWithTaskEnvironment;
 
 TEST_F(URLRequestJobTest, TransactionNoFilter) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kNoFilterTransaction.url), DEFAULT_PRIORITY,
-                            &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kNoFilterTransaction.url), DEFAULT_PRIORITY,
+                             &d, TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kNoFilterTransaction);
 
   req->set_method("GET");
@@ -309,7 +314,7 @@
   EXPECT_FALSE(d.request_failed());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ("hello", d.data_received());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
   // When there's no filter and a Content-Length, expected content size should
   // be available.
   EXPECT_EQ(30, req->GetExpectedContentSize());
@@ -318,12 +323,14 @@
 }
 
 TEST_F(URLRequestJobTest, TransactionNoFilterWithInvalidLength) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       GURL(kNoFilterTransactionWithInvalidLength.url), DEFAULT_PRIORITY, &d,
       TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kNoFilterTransactionWithInvalidLength);
@@ -336,7 +343,7 @@
   EXPECT_FALSE(d.request_failed());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ("hello", d.data_received());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
   // Invalid Content-Lengths that start with a + should not be reported.
   EXPECT_EQ(-1, req->GetExpectedContentSize());
 
@@ -344,14 +351,16 @@
 }
 
 TEST_F(URLRequestJobTest, TransactionNotifiedWhenDone) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
-                            TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
+                             TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kGZipTransaction);
 
   req->set_method("GET");
@@ -363,7 +372,7 @@
   EXPECT_EQ(OK, d.request_status());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ("", d.data_received());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
   // When there's a filter and a Content-Length, expected content size should
   // not be available.
   EXPECT_EQ(-1, req->GetExpectedContentSize());
@@ -372,14 +381,16 @@
 }
 
 TEST_F(URLRequestJobTest, SyncTransactionNotifiedWhenDone) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
-                            TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
+                             TRAFFIC_ANNOTATION_FOR_TESTS));
   MockTransaction transaction(kGZipTransaction);
   transaction.test_mode = TEST_MODE_SYNC_ALL;
   AddMockTransaction(&transaction);
@@ -393,7 +404,7 @@
   EXPECT_EQ(OK, d.request_status());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ("", d.data_received());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
   // When there's a filter and a Content-Length, expected content size should
   // not be available.
   EXPECT_EQ(-1, req->GetExpectedContentSize());
@@ -403,14 +414,16 @@
 
 // Tests processing a large gzip header one byte at a time.
 TEST_F(URLRequestJobTest, SyncSlowTransaction) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
-                            TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
+                             TRAFFIC_ANNOTATION_FOR_TESTS));
   MockTransaction transaction(kGZipTransaction);
   transaction.test_mode = TEST_MODE_SYNC_ALL | TEST_MODE_SLOW_READ;
   transaction.handler = &BigGZipServer;
@@ -425,21 +438,23 @@
   EXPECT_EQ(OK, d.request_status());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ("", d.data_received());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
   EXPECT_EQ(-1, req->GetExpectedContentSize());
 
   RemoveMockTransaction(&transaction);
 }
 
 TEST_F(URLRequestJobTest, RedirectTransactionNotifiedWhenDone) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kRedirectTransaction.url), DEFAULT_PRIORITY,
-                            &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kRedirectTransaction.url), DEFAULT_PRIORITY,
+                             &d, TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kRedirectTransaction);
 
   req->set_method("GET");
@@ -447,7 +462,7 @@
 
   d.RunUntilComplete();
 
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
 
   RemoveMockTransaction(&kRedirectTransaction);
 }
@@ -495,14 +510,16 @@
                                       request_headers.c_str(),
                                       test.response_headers, &transaction);
 
-    MockNetworkLayer network_layer;
-    TestURLRequestContext context;
-    context.set_http_transaction_factory(&network_layer);
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+        std::make_unique<MockNetworkLayer>());
+    context_builder->DisableHttpCache();
+    auto context = context_builder->Build();
 
     TestDelegate d;
     std::unique_ptr<URLRequest> req(
-        context.CreateRequest(GURL(transaction.url), DEFAULT_PRIORITY, &d,
-                              TRAFFIC_ANNOTATION_FOR_TESTS));
+        context->CreateRequest(GURL(transaction.url), DEFAULT_PRIORITY, &d,
+                               TRAFFIC_ANNOTATION_FOR_TESTS));
     AddMockTransaction(&transaction);
 
     req->set_referrer_policy(test.original_referrer_policy);
@@ -513,7 +530,7 @@
 
     d.RunUntilComplete();
 
-    EXPECT_TRUE(network_layer.done_reading_called());
+    EXPECT_TRUE(network_layer->done_reading_called());
 
     RemoveMockTransaction(&transaction);
 
@@ -525,17 +542,19 @@
 }
 
 TEST_F(URLRequestJobTest, TransactionNotCachedWhenNetworkDelegateRedirects) {
-  MockNetworkLayer network_layer;
-  TestNetworkDelegate network_delegate;
-  network_delegate.set_redirect_on_headers_received_url(GURL("http://foo"));
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
-  context.set_network_delegate(&network_delegate);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  auto network_delegate = std::make_unique<TestNetworkDelegate>();
+  network_delegate->set_redirect_on_headers_received_url(GURL("http://foo"));
+  context_builder->DisableHttpCache();
+  context_builder->set_network_delegate(std::move(network_delegate));
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
-                            TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kGZipTransaction.url), DEFAULT_PRIORITY, &d,
+                             TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kGZipTransaction);
 
   req->set_method("GET");
@@ -543,7 +562,7 @@
 
   d.RunUntilComplete();
 
-  EXPECT_TRUE(network_layer.stop_caching_called());
+  EXPECT_TRUE(network_layer->stop_caching_called());
 
   RemoveMockTransaction(&kGZipTransaction);
 }
@@ -552,12 +571,14 @@
 // calling ReadFilteredData.
 // Regression test for crbug.com/553300.
 TEST_F(URLRequestJobTest, EmptyBodySkipFilter) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       GURL(kEmptyBodyGzipTransaction.url), DEFAULT_PRIORITY, &d,
       TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kEmptyBodyGzipTransaction);
@@ -570,19 +591,21 @@
   EXPECT_FALSE(d.request_failed());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_TRUE(d.data_received().empty());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
 
   RemoveMockTransaction(&kEmptyBodyGzipTransaction);
 }
 
 // Regression test for crbug.com/575213.
 TEST_F(URLRequestJobTest, InvalidContentGZipTransaction) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       GURL(kInvalidContentGZipTransaction.url), DEFAULT_PRIORITY, &d,
       TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kInvalidContentGZipTransaction);
@@ -598,21 +621,23 @@
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, d.request_status());
   EXPECT_TRUE(d.data_received().empty());
-  EXPECT_FALSE(network_layer.done_reading_called());
+  EXPECT_FALSE(network_layer->done_reading_called());
 
   RemoveMockTransaction(&kInvalidContentGZipTransaction);
 }
 
 // Regression test for crbug.com/553300.
 TEST_F(URLRequestJobTest, SlowFilterRead) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kGzipSlowTransaction.url), DEFAULT_PRIORITY,
-                            &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kGzipSlowTransaction.url), DEFAULT_PRIORITY,
+                             &d, TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kGzipSlowTransaction);
 
   req->set_method("GET");
@@ -623,20 +648,22 @@
   EXPECT_FALSE(d.request_failed());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ("hello\n", d.data_received());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
 
   RemoveMockTransaction(&kGzipSlowTransaction);
 }
 
 TEST_F(URLRequestJobTest, SlowBrotliRead) {
-  MockNetworkLayer network_layer;
-  TestURLRequestContext context;
-  context.set_http_transaction_factory(&network_layer);
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  auto* network_layer = context_builder->SetHttpTransactionFactoryForTesting(
+      std::make_unique<MockNetworkLayer>());
+  context_builder->DisableHttpCache();
+  auto context = context_builder->Build();
 
   TestDelegate d;
   std::unique_ptr<URLRequest> req(
-      context.CreateRequest(GURL(kBrotliSlowTransaction.url), DEFAULT_PRIORITY,
-                            &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+      context->CreateRequest(GURL(kBrotliSlowTransaction.url), DEFAULT_PRIORITY,
+                             &d, TRAFFIC_ANNOTATION_FOR_TESTS));
   AddMockTransaction(&kBrotliSlowTransaction);
 
   req->set_method("GET");
@@ -647,7 +674,7 @@
   EXPECT_FALSE(d.request_failed());
   EXPECT_EQ(200, req->GetResponseCode());
   EXPECT_EQ(kHelloData, d.data_received());
-  EXPECT_TRUE(network_layer.done_reading_called());
+  EXPECT_TRUE(network_layer->done_reading_called());
   // When there's a filter and a Content-Length, expected content size should
   // not be available.
   EXPECT_EQ(-1, req->GetExpectedContentSize());
diff --git a/net/url_request/url_request_quic_perftest.cc b/net/url_request/url_request_quic_perftest.cc
index e09ddb94..7ea9e6c 100644
--- a/net/url_request/url_request_quic_perftest.cc
+++ b/net/url_request/url_request_quic_perftest.cc
@@ -39,6 +39,8 @@
 #include "net/tools/quic/quic_simple_server.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -105,7 +107,6 @@
     base::trace_event::InitializeMemoryDumpManagerForInProcessTesting(
         /*is_coordinator_process=*/false);
     memory_dump_manager_->set_dumper_registrations_ignored_for_testing(false);
-    context_ = std::make_unique<TestURLRequestContext>(true);
     memory_dump_manager_->set_dumper_registrations_ignored_for_testing(true);
     StartTcpServer();
     StartQuicServer();
@@ -113,29 +114,31 @@
     // Host mapping.
     std::unique_ptr<MockHostResolver> resolver(new MockHostResolver());
     resolver->rules()->AddRule(kAltSvcHost, "127.0.0.1");
-    host_resolver_ = std::make_unique<MappedHostResolver>(std::move(resolver));
+    auto host_resolver =
+        std::make_unique<MappedHostResolver>(std::move(resolver));
     std::string map_rule = base::StringPrintf("MAP %s 127.0.0.1:%d",
                                               kOriginHost, tcp_server_->port());
-    EXPECT_TRUE(host_resolver_->AddRuleFromString(map_rule));
+    EXPECT_TRUE(host_resolver->AddRuleFromString(map_rule));
 
     net::HttpNetworkSessionContext network_session_context;
-    network_session_context.cert_verifier = &cert_verifier_;
-    auto params = std::make_unique<HttpNetworkSessionParams>();
-    params->enable_quic = true;
-    params->enable_user_alternate_protocol_ports = true;
-    quic_context_.params()->allow_remote_alt_svc = true;
-    context_->set_host_resolver(host_resolver_.get());
-    context_->set_http_network_session_params(std::move(params));
-    context_->set_cert_verifier(&cert_verifier_);
-    context_->set_quic_context(&quic_context_);
-    context_->Init();
+    HttpNetworkSessionParams params;
+    params.enable_quic = true;
+    params.enable_user_alternate_protocol_ports = true;
+    auto quic_context = std::make_unique<QuicContext>();
+    quic_context->params()->allow_remote_alt_svc = true;
+    auto context_builder = CreateTestURLRequestContextBuilder();
+    context_builder->set_host_resolver(std::move(host_resolver));
+    context_builder->set_http_network_session_params(params);
+    context_builder->SetCertVerifier(std::make_unique<MockCertVerifier>());
+    context_builder->set_quic_context(std::move(quic_context));
+    context_ = context_builder->Build();
   }
 
   void TearDown() override {
     if (quic_server_) {
       quic_server_->Shutdown();
-      // If possible, deliver the conncetion close packet to the client before
-      // destruct the TestURLRequestContext.
+      // If possible, deliver the connection close packet to the client before
+      // destruct the URLRequestContext.
       base::RunLoop().RunUntilIdle();
     }
     // |tcp_server_| shuts down in EmbeddedTestServer destructor.
@@ -169,8 +172,8 @@
     verify_result.verified_cert = ImportCertFromFile(
         GetTestCertsDirectory(), "quic-chain.pem");
     verify_result.is_issued_by_known_root = true;
-    cert_verifier_.AddResultForCert(verify_result.verified_cert.get(),
-                                    verify_result, OK);
+    cert_verifier().AddResultForCert(verify_result.verified_cert.get(),
+                                     verify_result, OK);
   }
 
   void StartTcpServer() {
@@ -181,19 +184,21 @@
 
     CertVerifyResult verify_result;
     verify_result.verified_cert = tcp_server_->GetCertificate();
-    cert_verifier_.AddResultForCert(tcp_server_->GetCertificate(),
-                                    verify_result, OK);
+    cert_verifier().AddResultForCert(tcp_server_->GetCertificate(),
+                                     verify_result, OK);
+  }
+
+  MockCertVerifier& cert_verifier() {
+    // This cast is safe because we set a MockCertVerifier in the constructor.
+    return *static_cast<MockCertVerifier*>(context_->cert_verifier());
   }
 
   std::unique_ptr<base::trace_event::MemoryDumpManager> memory_dump_manager_;
-  std::unique_ptr<MappedHostResolver> host_resolver_;
   std::unique_ptr<EmbeddedTestServer> tcp_server_;
   std::unique_ptr<QuicSimpleServer> quic_server_;
   std::unique_ptr<base::test::SingleThreadTaskEnvironment> task_environment_;
-  std::unique_ptr<TestURLRequestContext> context_;
+  std::unique_ptr<URLRequestContext> context_;
   quic::QuicMemoryCacheBackend memory_cache_backend_;
-  MockCertVerifier cert_verifier_;
-  QuicContext quic_context_;
 };
 
 void CheckScalarInDump(const MemoryAllocatorDump* dump,
diff --git a/net/url_request/url_request_quic_unittest.cc b/net/url_request/url_request_quic_unittest.cc
index 9568fcae..e64f233 100644
--- a/net/url_request/url_request_quic_unittest.cc
+++ b/net/url_request/url_request_quic_unittest.cc
@@ -39,6 +39,8 @@
 #include "net/tools/quic/quic_simple_server.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -120,58 +122,56 @@
     : public TestWithTaskEnvironment,
       public ::testing::WithParamInterface<quic::ParsedQuicVersion> {
  protected:
-  URLRequestQuicTest() : context_(new TestURLRequestContext(true)) {
+  URLRequestQuicTest()
+      : context_builder_(CreateTestURLRequestContextBuilder()) {
     QuicEnableVersion(version());
     StartQuicServer(version());
 
-    auto params = std::make_unique<HttpNetworkSessionParams>();
+    HttpNetworkSessionParams params;
     CertVerifyResult verify_result;
     verify_result.verified_cert = ImportCertFromFile(
         GetTestCertsDirectory(), "quic-chain.pem");
-    cert_verifier_.AddResultForCertAndHost(verify_result.verified_cert.get(),
+    auto cert_verifier = std::make_unique<MockCertVerifier>();
+    cert_verifier->AddResultForCertAndHost(verify_result.verified_cert.get(),
                                            kTestServerHost, verify_result, OK);
     // To simplify the test, and avoid the race with the HTTP request, we force
     // QUIC for these requests.
-    context_->set_quic_context(&quic_context_);
-    quic_context_.params()->supported_versions = {version()};
-    quic_context_.params()->origins_to_force_quic_on.insert(
+    auto quic_context = std::make_unique<QuicContext>();
+    quic_context->params()->supported_versions = {version()};
+    quic_context->params()->origins_to_force_quic_on.insert(
         HostPortPair(kTestServerHost, 443));
-    params->enable_quic = true;
-    params->enable_server_push_cancellation = true;
-    context_->set_host_resolver(host_resolver_.get());
-    context_->set_http_network_session_params(std::move(params));
-    context_->set_cert_verifier(&cert_verifier_);
-    context_->set_net_log(NetLog::Get());
-    transport_security_state_.SetExpectCTReporter(&expect_ct_reporter_);
-    context_->set_transport_security_state(&transport_security_state_);
+    context_builder_->set_quic_context(std::move(quic_context));
+    params.enable_quic = true;
+    params.enable_server_push_cancellation = true;
+    context_builder_->set_host_resolver(std::move(host_resolver_));
+    context_builder_->set_http_network_session_params(params);
+    context_builder_->SetCertVerifier(std::move(cert_verifier));
+    context_builder_->set_net_log(NetLog::Get());
   }
 
   void TearDown() override {
     if (server_) {
       server_->Shutdown();
-      // If possible, deliver the conncetion close packet to the client before
-      // destruct the TestURLRequestContext.
       base::RunLoop().RunUntilIdle();
     }
   }
 
-  // Sets a NetworkDelegate to use for |context_|. Must be done before Init().
-  void SetNetworkDelegate(NetworkDelegate* network_delegate) {
-    context_->set_network_delegate(network_delegate);
+  URLRequestContextBuilder* context_builder() { return context_builder_.get(); }
+
+  std::unique_ptr<URLRequestContext> BuildContext() {
+    auto context = context_builder_->Build();
+
+    context->transport_security_state()->SetExpectCTReporter(
+        &expect_ct_reporter_);
+    return context;
   }
 
-  // Can be used to modify |context_|. Only safe to modify before Init() is
-  // called.
-  TestURLRequestContext* context() { return context_.get(); }
-
-  // Initializes the TestURLRequestContext |context_|.
-  void Init() { context_->Init(); }
-
-  std::unique_ptr<URLRequest> CreateRequest(const GURL& url,
-                                            RequestPriority priority,
-                                            URLRequest::Delegate* delegate) {
-    return context_->CreateRequest(url, priority, delegate,
-                                   TRAFFIC_ANNOTATION_FOR_TESTS);
+  static std::unique_ptr<URLRequest> CreateRequest(
+      URLRequestContext* context,
+      const GURL& url,
+      URLRequest::Delegate* delegate) {
+    return context->CreateRequest(url, DEFAULT_PRIORITY, delegate,
+                                  TRAFFIC_ANNOTATION_FOR_TESTS);
   }
 
   unsigned int GetRstErrorCountReceivedByServer(
@@ -212,10 +212,6 @@
 
   MockExpectCTReporter* expect_ct_reporter() { return &expect_ct_reporter_; }
 
-  TransportSecurityState* transport_security_state() {
-    return &transport_security_state_;
-  }
-
  protected:
   // Returns a fully-qualified URL for |path| on the test server.
   std::string UrlFromPath(base::StringPiece path) {
@@ -279,14 +275,11 @@
   }
 
   MockExpectCTReporter expect_ct_reporter_;
-  TransportSecurityState transport_security_state_;
 
   std::unique_ptr<MappedHostResolver> host_resolver_;
   std::unique_ptr<QuicSimpleServer> server_;
-  std::unique_ptr<TestURLRequestContext> context_;
-  QuicContext quic_context_;
   quic::QuicMemoryCacheBackend memory_cache_backend_;
-  MockCertVerifier cert_verifier_;
+  std::unique_ptr<URLRequestContextBuilder> context_builder_;
   QuicFlagSaver flags_;  // Save/restore all QUIC flag values.
 };
 
@@ -376,10 +369,10 @@
                          ::testing::PrintToStringParamName());
 
 TEST_P(URLRequestQuicTest, TestGetRequest) {
-  Init();
+  auto context = BuildContext();
   CheckLoadTimingDelegate delegate(false);
   std::unique_ptr<URLRequest> request =
-      CreateRequest(GURL(UrlFromPath(kHelloPath)), DEFAULT_PRIORITY, &delegate);
+      CreateRequest(context.get(), GURL(UrlFromPath(kHelloPath)), &delegate);
 
   request->Start();
   ASSERT_TRUE(request->is_pending());
@@ -394,10 +387,10 @@
 // should not have |LoadTimingInfo::connect_timing|.
 TEST_P(URLRequestQuicTest, TestTwoRequests) {
   base::RunLoop run_loop;
-  WaitForCompletionNetworkDelegate network_delegate(
-      run_loop.QuitClosure(), /*num_expected_requests=*/2);
-  SetNetworkDelegate(&network_delegate);
-  Init();
+  context_builder()->set_network_delegate(
+      std::make_unique<WaitForCompletionNetworkDelegate>(
+          run_loop.QuitClosure(), /*num_expected_requests=*/2));
+  auto context = BuildContext();
 
   GURL url = GURL(UrlFromPath(kHelloPath));
   auto isolation_info =
@@ -406,13 +399,13 @@
   CheckLoadTimingDelegate delegate(false);
   delegate.set_on_complete(base::DoNothing());
   std::unique_ptr<URLRequest> request =
-      CreateRequest(url, DEFAULT_PRIORITY, &delegate);
+      CreateRequest(context.get(), url, &delegate);
   request->set_isolation_info(isolation_info);
 
   CheckLoadTimingDelegate delegate2(true);
   delegate2.set_on_complete(base::DoNothing());
   std::unique_ptr<URLRequest> request2 =
-      CreateRequest(url, DEFAULT_PRIORITY, &delegate2);
+      CreateRequest(context.get(), url, &delegate2);
   request2->set_isolation_info(isolation_info);
 
   request->Start();
@@ -428,15 +421,14 @@
 }
 
 TEST_P(URLRequestQuicTest, RequestHeadersCallback) {
-  Init();
+  auto context = BuildContext();
   HttpRawRequestHeaders raw_headers;
   TestDelegate delegate;
-  TestURLRequestContext context;
   HttpRequestHeaders extra_headers;
   extra_headers.SetHeader("X-Foo", "bar");
 
   std::unique_ptr<URLRequest> request =
-      CreateRequest(GURL(UrlFromPath(kHelloPath)), DEFAULT_PRIORITY, &delegate);
+      CreateRequest(context.get(), GURL(UrlFromPath(kHelloPath)), &delegate);
 
   request->SetExtraRequestHeaders(extra_headers);
   request->SetRequestHeadersCallback(
@@ -477,20 +469,20 @@
       // disabled_features
       {});
 
-  MockCTPolicyEnforcerNonCompliant ct_enforcer;
-  context()->set_ct_policy_enforcer(&ct_enforcer);
-  Init();
+  context_builder()->set_ct_policy_enforcer(
+      std::make_unique<MockCTPolicyEnforcerNonCompliant>());
+  auto context = BuildContext();
 
   GURL report_uri("https://report.test/");
   IsolationInfo isolation_info = IsolationInfo::CreateTransient();
-  transport_security_state()->AddExpectCT(
+  context->transport_security_state()->AddExpectCT(
       kTestServerHost, base::Time::Now() + base::Days(1), true /* enforce */,
       report_uri, isolation_info.network_isolation_key());
 
   base::RunLoop run_loop;
   TestDelegate delegate;
   std::unique_ptr<URLRequest> request =
-      CreateRequest(GURL(UrlFromPath(kHelloPath)), DEFAULT_PRIORITY, &delegate);
+      CreateRequest(context.get(), GURL(UrlFromPath(kHelloPath)), &delegate);
   request->set_isolation_info(isolation_info);
   request->Start();
   delegate.RunUntilComplete();
diff --git a/net/url_request/url_request_throttler_simulation_unittest.cc b/net/url_request/url_request_throttler_simulation_unittest.cc
index edb0cf7a..e55ed330 100644
--- a/net/url_request/url_request_throttler_simulation_unittest.cc
+++ b/net/url_request/url_request_throttler_simulation_unittest.cc
@@ -26,6 +26,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "net/url_request/url_request_throttler_manager.h"
 #include "net/url_request/url_request_throttler_test_support.h"
@@ -125,10 +126,11 @@
         num_current_tick_queries_(0),
         num_overloaded_ticks_(0),
         max_experienced_queries_per_tick_(0),
-        mock_request_(context_.CreateRequest(GURL(),
-                                             DEFAULT_PRIORITY,
-                                             nullptr,
-                                             TRAFFIC_ANNOTATION_FOR_TESTS)) {}
+        context_(CreateTestURLRequestContextBuilder()->Build()),
+        mock_request_(context_->CreateRequest(GURL(),
+                                              DEFAULT_PRIORITY,
+                                              nullptr,
+                                              TRAFFIC_ANNOTATION_FOR_TESTS)) {}
 
   Server(const Server&) = delete;
   Server& operator=(const Server&) = delete;
@@ -283,7 +285,7 @@
     return output;
   }
 
-  const URLRequestContext& context() const { return context_; }
+  const URLRequestContext& context() const { return *context_; }
 
  private:
   TimeTicks now_;
@@ -297,7 +299,7 @@
   int max_experienced_queries_per_tick_;
   std::vector<int> requests_per_tick_;
 
-  TestURLRequestContext context_;
+  std::unique_ptr<URLRequestContext> context_;
   std::unique_ptr<URLRequest> mock_request_;
 };
 
diff --git a/net/url_request/url_request_throttler_unittest.cc b/net/url_request/url_request_throttler_unittest.cc
index 6b34917..2f748210 100644
--- a/net/url_request/url_request_throttler_unittest.cc
+++ b/net/url_request/url_request_throttler_unittest.cc
@@ -16,6 +16,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_test_util.h"
 #include "net/url_request/url_request_throttler_manager.h"
 #include "net/url_request/url_request_throttler_test_support.h"
@@ -160,10 +161,11 @@
 class URLRequestThrottlerEntryTest : public TestWithTaskEnvironment {
  protected:
   URLRequestThrottlerEntryTest()
-      : request_(context_.CreateRequest(GURL(),
-                                        DEFAULT_PRIORITY,
-                                        nullptr,
-                                        TRAFFIC_ANNOTATION_FOR_TESTS)) {}
+      : context_(CreateTestURLRequestContextBuilder()->Build()),
+        request_(context_->CreateRequest(GURL(),
+                                         DEFAULT_PRIORITY,
+                                         nullptr,
+                                         TRAFFIC_ANNOTATION_FOR_TESTS)) {}
 
   void SetUp() override;
 
@@ -171,7 +173,7 @@
   MockURLRequestThrottlerManager manager_;  // Dummy object, not used.
   scoped_refptr<MockURLRequestThrottlerEntry> entry_;
 
-  TestURLRequestContext context_;
+  std::unique_ptr<URLRequestContext> context_;
   std::unique_ptr<URLRequest> request_;
 };
 
@@ -312,15 +314,16 @@
 class URLRequestThrottlerManagerTest : public TestWithTaskEnvironment {
  protected:
   URLRequestThrottlerManagerTest()
-      : request_(context_.CreateRequest(GURL(),
-                                        DEFAULT_PRIORITY,
-                                        nullptr,
-                                        TRAFFIC_ANNOTATION_FOR_TESTS)) {}
+      : context_(CreateTestURLRequestContextBuilder()->Build()),
+        request_(context_->CreateRequest(GURL(),
+                                         DEFAULT_PRIORITY,
+                                         nullptr,
+                                         TRAFFIC_ANNOTATION_FOR_TESTS)) {}
 
   void SetUp() override { request_->SetLoadFlags(0); }
 
   // context_ must be declared before request_.
-  TestURLRequestContext context_;
+  std::unique_ptr<URLRequestContext> context_;
   std::unique_ptr<URLRequest> request_;
 };
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 0b38995..9f7ec13 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -142,6 +142,7 @@
 #include "net/url_request/referrer_policy.h"
 #include "net/url_request/static_http_user_agent_settings.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_http_job.h"
@@ -13100,26 +13101,25 @@
     : public URLRequestTest,
       public testing::WithParamInterface<bool> {
  public:
-  URLRequestMaybeAsyncFirstPartySetsTest()
-      : cookie_monster_(/*store=*/nullptr,
-                        /*net_log=*/nullptr,
-                        /*first_party_sets_enabled=*/true) {
+  URLRequestMaybeAsyncFirstPartySetsTest() { CHECK(test_server_.Start()); }
+
+  std::unique_ptr<CookieStore> CreateCookieStore() {
+    auto cookie_monster =
+        std::make_unique<CookieMonster>(/*store=*/nullptr,
+                                        /*net_log=*/nullptr,
+                                        /*first_party_sets_enabled=*/true);
     auto cookie_access_delegate = std::make_unique<TestCookieAccessDelegate>();
     cookie_access_delegate->set_invoke_callbacks_asynchronously(
         invoke_callbacks_asynchronously());
-    cookie_monster_.SetCookieAccessDelegate(std::move(cookie_access_delegate));
-
-    CHECK(test_server_.Start());
+    cookie_monster->SetCookieAccessDelegate(std::move(cookie_access_delegate));
+    return cookie_monster;
   }
 
   bool invoke_callbacks_asynchronously() { return GetParam(); }
 
-  CookieStore* cookie_store() { return &cookie_monster_; }
-
   HttpTestServer& test_server() { return test_server_; }
 
  private:
-  CookieMonster cookie_monster_;
   HttpTestServer test_server_;
 };
 
@@ -13129,12 +13129,12 @@
       url::Origin::Create(test_server().GetURL(kHost, "/"));
   const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin);
 
-  TestURLRequestContext context(/*delay_initialization=*/true);
-  context.set_cookie_store(cookie_store());
-  context.Init();
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(CreateCookieStore());
+  auto context = context_builder->Build();
 
   TestDelegate d;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       test_server().GetURL(kHost, "/echo"), DEFAULT_PRIORITY, &d,
       TRAFFIC_ANNOTATION_FOR_TESTS));
   req->set_isolation_info(
@@ -13154,12 +13154,12 @@
       url::Origin::Create(test_server().GetURL(kHost, "/"));
   const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin);
 
-  TestURLRequestContext context(/*delay_initialization=*/true);
-  context.set_cookie_store(cookie_store());
-  context.Init();
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->SetCookieStore(CreateCookieStore());
+  auto context = context_builder->Build();
 
   TestDelegate d;
-  std::unique_ptr<URLRequest> req(context.CreateRequest(
+  std::unique_ptr<URLRequest> req(context->CreateRequest(
       test_server().GetURL(kHost,
                            base::StrCat({
                                "/server-redirect?",
diff --git a/pdf/pdf_view_plugin_base.cc b/pdf/pdf_view_plugin_base.cc
index b4f03096..40ef7bd5 100644
--- a/pdf/pdf_view_plugin_base.cc
+++ b/pdf/pdf_view_plugin_base.cc
@@ -927,10 +927,6 @@
 
   engine()->PageOffsetUpdated(available_area_.OffsetFromOrigin());
   engine()->PluginSizeUpdated(available_area_.size());
-
-  if (document_size_.IsEmpty())
-    return;
-  paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
 }
 
 void PdfViewPluginBase::CalculateBackgroundParts() {
@@ -1088,7 +1084,10 @@
 void PdfViewPluginBase::SetZoom(double scale) {
   double old_zoom = zoom_;
   zoom_ = scale;
+
   OnGeometryChanged(old_zoom, device_scale_);
+  if (!document_size_.IsEmpty())
+    paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
 }
 
 // static
@@ -1310,7 +1309,10 @@
 
     // TODO(crbug.com/1013800): Eliminate need to get document size from here.
     document_size_ = engine()->ApplyDocumentLayout(layout_options);
+
     OnGeometryChanged(zoom_, device_scale_);
+    if (!document_size_.IsEmpty())
+      paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
 
     // Send 100% loading progress only after initial layout negotiated.
     if (last_progress_sent_ < 100 &&
@@ -1759,7 +1761,10 @@
     print_preview_loaded_page_count_ = 1;
     AppendBlankPrintPreviewPages();
   }
+
   OnGeometryChanged(0, 0);
+  if (!document_size_.IsEmpty())
+    paint_manager_.InvalidateRect(gfx::Rect(plugin_rect_.size()));
 }
 
 void PdfViewPluginBase::AppendBlankPrintPreviewPages() {
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index d916c59c..4eb78bd 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -781,15 +781,16 @@
     if (base::win::GetVersion() >= base::win::Version::WIN8 ||
         (::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job) &&
          !in_job)) {
-      static HANDLE job_object_handle = nullptr;
-      if (!job_object_handle) {
-        Job job_obj;
-        DWORD result = job_obj.Init(JOB_UNPROTECTED, nullptr, 0, 0);
-        if (result != ERROR_SUCCESS)
+      static Job* job_object = nullptr;
+      if (!job_object) {
+        job_object = new Job;
+        DWORD result = job_object->Init(JOB_UNPROTECTED, nullptr, 0, 0);
+        if (result != ERROR_SUCCESS) {
+          job_object = nullptr;
           return SBOX_ERROR_CANNOT_INIT_JOB;
-        job_object_handle = job_obj.Take().Take();
+        }
       }
-      options.job_handle = job_object_handle;
+      options.job_handle = job_object->GetHandle();
     }
   }
 
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc
index 6bce0cc3..0f5c975 100644
--- a/sandbox/win/src/broker_services.cc
+++ b/sandbox/win/src/broker_services.cc
@@ -80,18 +80,15 @@
 // Helper structure that allows the Broker to associate a job notification
 // with a job object and with a policy.
 struct JobTracker {
-  JobTracker(base::win::ScopedHandle job,
-             scoped_refptr<sandbox::PolicyBase> policy,
-             DWORD process_id)
-      : job(std::move(job)), policy(policy), process_id(process_id) {}
+  JobTracker(scoped_refptr<sandbox::PolicyBase> policy, DWORD process_id)
+      : policy(policy), process_id(process_id) {}
   ~JobTracker() {
     // As if TerminateProcess() was called for all associated processes.
     // Handles are still valid.
-    ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK);
-    policy->OnJobEmpty(job.Get());
+    ::TerminateJobObject(policy->GetJobHandle(), sandbox::SBOX_ALL_OK);
+    policy->OnJobEmpty();
   }
 
-  base::win::ScopedHandle job;
   scoped_refptr<sandbox::PolicyBase> policy;
   DWORD process_id;
 };
@@ -204,13 +201,11 @@
           // with it has terminated. It is safe to free the tracker
           // and release its reference to the associated policy object
           // which will Close the job handle.
-          HANDLE job_handle = tracker->job.Get();
 
-          // Erase by comparing with the job handle.
-          jobs.erase(std::remove_if(jobs.begin(), jobs.end(),
-                                    [&](auto&& p) -> bool {
-                                      return p->job.Get() == job_handle;
-                                    }),
+          // Erase directly.
+          jobs.erase(std::remove_if(
+                         jobs.begin(), jobs.end(),
+                         [&](auto&& p) -> bool { return p.get() == tracker; }),
                      jobs.end());
           break;
         }
@@ -255,7 +250,7 @@
         }
 
         case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: {
-          bool res = ::TerminateJobObject(tracker->job.Get(),
+          bool res = ::TerminateJobObject(tracker->policy->GetJobHandle(),
                                           sandbox::SBOX_FATAL_MEMORY_EXCEEDED);
           DCHECK(res);
           break;
@@ -269,7 +264,7 @@
     } else if (THREAD_CTRL_NEW_JOB_TRACKER == key) {
       std::unique_ptr<JobTracker> tracker;
       tracker.reset(reinterpret_cast<JobTracker*>(ovl));
-      DCHECK(tracker->job.IsValid());
+      DCHECK(tracker->policy->HasJob());
 
       child_process_ids.insert(tracker->process_id);
       jobs.push_back(std::move(tracker));
@@ -494,8 +489,7 @@
     return SBOX_ERROR_BAD_PARAMS;
   }
 
-  base::win::ScopedHandle job;
-  result = policy_base->MakeJobObject(&job);
+  result = policy_base->InitJob();
   if (SBOX_ALL_OK != result)
     return result;
 
@@ -525,8 +519,9 @@
     startup_info->SetAppContainer(container);
 
   // On Win10, jobs are associated via startup_info.
-  if (base::win::GetVersion() >= base::win::Version::WIN10 && job.IsValid()) {
-    startup_info->AddJobToAssociate(job.Get());
+  if (base::win::GetVersion() >= base::win::Version::WIN10 &&
+      policy_base->HasJob()) {
+    startup_info->AddJobToAssociate(policy_base->GetJobHandle());
   }
 
   if (!startup_info->BuildStartupInformation())
@@ -543,8 +538,8 @@
     }
   }
   std::unique_ptr<TargetProcess> target = std::make_unique<TargetProcess>(
-      std::move(initial_token), std::move(lockdown_token), job.Get(),
-      thread_pool_, imp_caps);
+      std::move(initial_token), std::move(lockdown_token),
+      policy_base->GetJobHandle(), thread_pool_, imp_caps);
 
   result = target->Create(exe_path, command_line, std::move(startup_info),
                           &process_info, last_error);
@@ -554,11 +549,11 @@
     return result;
   }
 
-  if (job.IsValid() && policy_base->GetJobLevel() <= JOB_LIMITED_USER) {
+  if (policy_base->HasJob() && policy_base->GetJobLevel() <= JOB_LIMITED_USER) {
     // Restrict the job from containing any processes. Job restrictions
     // are only applied at process creation, so the target process is
     // unaffected.
-    result = policy_base->DropActiveProcessLimit(&job);
+    result = policy_base->DropActiveProcessLimit();
     if (result != SBOX_ALL_OK) {
       target->Terminate();
       return result;
@@ -583,9 +578,9 @@
     return result;
   }
 
-  if (job.IsValid()) {
+  if (policy_base->HasJob()) {
     JobTracker* tracker =
-        new JobTracker(std::move(job), policy_base, process_info.process_id());
+        new JobTracker(policy_base, process_info.process_id());
 
     // Post the tracker to the tracking thread, then associate the job with
     // the tracker. The worker thread takes ownership of these objects.
@@ -593,8 +588,8 @@
         job_port_.Get(), 0, THREAD_CTRL_NEW_JOB_TRACKER,
         reinterpret_cast<LPOVERLAPPED>(tracker)));
     // There is no obvious cleanup here.
-    CHECK(
-        AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), tracker));
+    CHECK(AssociateCompletionPort(policy_base->GetJobHandle(), job_port_.Get(),
+                                  tracker));
   } else {
     // Duplicate the process handle to give the tracking machinery
     // something valid to wait on in the tracking thread.
diff --git a/sandbox/win/src/job.cc b/sandbox/win/src/job.cc
index d12a565..f9097b6 100644
--- a/sandbox/win/src/job.cc
+++ b/sandbox/win/src/job.cc
@@ -39,9 +39,6 @@
     case JOB_LOCKDOWN: {
       jeli.BasicLimitInformation.LimitFlags |=
           JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
-      [[fallthrough]];
-    }
-    case JOB_RESTRICTED: {
       jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
       jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD;
       jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
@@ -90,6 +87,14 @@
   return ERROR_SUCCESS;
 }
 
+bool Job::IsValid() {
+  return job_handle_.IsValid();
+}
+
+HANDLE Job::GetHandle() {
+  return job_handle_.Get();
+}
+
 DWORD Job::UserHandleGrantAccess(HANDLE handle) {
   if (!job_handle_.IsValid())
     return ERROR_NO_DATA;
@@ -102,10 +107,6 @@
   return ERROR_SUCCESS;
 }
 
-base::win::ScopedHandle Job::Take() {
-  return std::move(job_handle_);
-}
-
 DWORD Job::AssignProcessToJob(HANDLE process_handle) {
   if (!job_handle_.IsValid())
     return ERROR_NO_DATA;
@@ -116,23 +117,25 @@
   return ERROR_SUCCESS;
 }
 
-// static
-DWORD Job::SetActiveProcessLimit(base::win::ScopedHandle* job_handle,
-                                 DWORD processes) {
+DWORD Job::SetActiveProcessLimit(DWORD processes) {
   JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
 
-  if (!::QueryInformationJobObject(job_handle->Get(),
-                                   JobObjectExtendedLimitInformation, &jeli,
-                                   sizeof(jeli), nullptr))
-    return ::GetLastError();
+  if (!job_handle_.IsValid())
+    return ERROR_NO_DATA;
 
+  if (!::QueryInformationJobObject(job_handle_.Get(),
+                                   JobObjectExtendedLimitInformation, &jeli,
+                                   sizeof(jeli), nullptr)) {
+    return ::GetLastError();
+  }
   jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
   jeli.BasicLimitInformation.ActiveProcessLimit = processes;
 
-  if (!::SetInformationJobObject(job_handle->Get(),
+  if (!::SetInformationJobObject(job_handle_.Get(),
                                  JobObjectExtendedLimitInformation, &jeli,
-                                 sizeof(jeli)))
+                                 sizeof(jeli))) {
     return ::GetLastError();
+  }
 
   return ERROR_SUCCESS;
 }
diff --git a/sandbox/win/src/job.h b/sandbox/win/src/job.h
index 6922338c..1bef847c 100644
--- a/sandbox/win/src/job.h
+++ b/sandbox/win/src/job.h
@@ -52,13 +52,14 @@
   // the error.
   DWORD UserHandleGrantAccess(HANDLE handle);
 
-  // Revokes ownership to the job handle and returns it.
-  // If the object is not yet initialized, it returns an invalid handle.
-  base::win::ScopedHandle Take();
+  // True if the job has been initialized and has a valid handle.
+  bool IsValid();
 
-  // Updates the active process limit for |job_handle|.
-  static DWORD SetActiveProcessLimit(base::win::ScopedHandle* job_handle,
-                                     DWORD processes);
+  // If the object is not yet initialized, returns nullptr.
+  HANDLE GetHandle();
+
+  // Updates the active process limit.
+  DWORD SetActiveProcessLimit(DWORD processes);
 
  private:
   // Handle to the job referenced by the object.
diff --git a/sandbox/win/src/job_unittest.cc b/sandbox/win/src/job_unittest.cc
index 536900dc..2af3f27c 100644
--- a/sandbox/win/src/job_unittest.cc
+++ b/sandbox/win/src/job_unittest.cc
@@ -35,41 +35,9 @@
   ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError());
 }
 
-// Tests the method "Take".
-TEST(JobTest, Take) {
-  base::win::ScopedHandle job_handle;
-  // Scope the creation of Job.
-  {
-    // Create the job.
-    Job job;
-    ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-              job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
-
-    job_handle = job.Take();
-    ASSERT_TRUE(job_handle.IsValid());
-  }
-
-  // Check to be sure that the job is still alive even after the object is gone
-  // out of scope.
-  HANDLE job_handle_dup =
-      ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
-  ASSERT_TRUE(job_handle_dup);
-
-  // Remove all references.
-  if (job_handle_dup)
-    ::CloseHandle(job_handle_dup);
-
-  job_handle.Close();
-
-  // Check if the jbo is really dead.
-  job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
-  ASSERT_TRUE(!job_handle_dup);
-  ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError());
-}
-
 // Tests the ui exceptions
 TEST(JobTest, TestExceptions) {
-  base::win::ScopedHandle job_handle;
+  HANDLE job_handle;
   // Scope the creation of Job.
   {
     // Create the job.
@@ -78,16 +46,15 @@
               job.Init(JOB_LOCKDOWN, L"my_test_job_name",
                        JOB_OBJECT_UILIMIT_READCLIPBOARD, 0));
 
-    job_handle = job.Take();
-    ASSERT_TRUE(job_handle.IsValid());
+    job_handle = job.GetHandle();
+    ASSERT_TRUE(job_handle != INVALID_HANDLE_VALUE);
 
     JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
     DWORD size = sizeof(jbur);
     ASSERT_TRUE(::QueryInformationJobObject(
-        job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size));
+        job_handle, JobObjectBasicUIRestrictions, &jbur, size, &size));
 
     ASSERT_EQ(0u, jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD);
-    job_handle.Close();
   }
 
   // Scope the creation of Job.
@@ -97,13 +64,13 @@
     ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
               job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
 
-    job_handle = job.Take();
-    ASSERT_TRUE(job_handle.IsValid());
+    job_handle = job.GetHandle();
+    ASSERT_TRUE(job_handle != INVALID_HANDLE_VALUE);
 
     JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
     DWORD size = sizeof(jbur);
     ASSERT_TRUE(::QueryInformationJobObject(
-        job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size));
+        job_handle, JobObjectBasicUIRestrictions, &jbur, size, &size));
 
     ASSERT_EQ(static_cast<DWORD>(JOB_OBJECT_UILIMIT_READCLIPBOARD),
               jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD);
@@ -127,39 +94,36 @@
   ASSERT_EQ(static_cast<DWORD>(ERROR_NO_DATA),
             job.UserHandleGrantAccess(nullptr));
   ASSERT_EQ(static_cast<DWORD>(ERROR_NO_DATA), job.AssignProcessToJob(nullptr));
-  ASSERT_FALSE(job.Take().IsValid());
+  ASSERT_FALSE(job.GetHandle() == INVALID_HANDLE_VALUE);
 }
 
-// Tests the initialization of the job with different security level.
+// Tests the initialization of the job with different security levels.
 TEST(JobTest, SecurityLevel) {
-  Job job1;
+  Job job_lockdown;
   ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            job1.Init(JOB_LOCKDOWN, L"job1", 0, 0));
+            job_lockdown.Init(JOB_LOCKDOWN, L"job_lockdown", 0, 0));
 
-  Job job2;
+  Job job_limited_user;
   ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            job2.Init(JOB_RESTRICTED, L"job2", 0, 0));
+            job_limited_user.Init(JOB_LIMITED_USER, L"job_limited_user", 0, 0));
 
-  Job job3;
+  Job job_interactive;
   ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            job3.Init(JOB_LIMITED_USER, L"job3", 0, 0));
+            job_interactive.Init(JOB_INTERACTIVE, L"job_interactive", 0, 0));
 
-  Job job4;
+  Job job_unprotected;
   ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            job4.Init(JOB_INTERACTIVE, L"job4", 0, 0));
-
-  Job job5;
-  ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            job5.Init(JOB_UNPROTECTED, L"job5", 0, 0));
+            job_unprotected.Init(JOB_UNPROTECTED, L"job_unprotected", 0, 0));
 
   // JOB_NONE means we run without a job object so Init should fail.
-  Job job6;
+  Job job_none;
   ASSERT_EQ(static_cast<DWORD>(ERROR_BAD_ARGUMENTS),
-            job6.Init(JOB_NONE, L"job6", 0, 0));
+            job_none.Init(JOB_NONE, L"job_none", 0, 0));
 
-  Job job7;
+  Job job_bad_args;
   ASSERT_EQ(static_cast<DWORD>(ERROR_BAD_ARGUMENTS),
-            job7.Init(static_cast<JobLevel>(JOB_NONE + 1), L"job7", 0, 0));
+            job_bad_args.Init(static_cast<JobLevel>(JOB_NONE + 1),
+                              L"job_bad_args", 0, 0));
 }
 
 // Tests the method "AssignProcessToJob".
@@ -179,13 +143,13 @@
             job.AssignProcessToJob(pi.process_handle()));
 
   // Get the job handle.
-  base::win::ScopedHandle job_handle = job.Take();
+  HANDLE job_handle = job.GetHandle();
 
   // Check if the process is in the job.
   JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0};
   DWORD size = sizeof(jbpidl);
   EXPECT_TRUE(::QueryInformationJobObject(
-      job_handle.Get(), JobObjectBasicProcessIdList, &jbpidl, size, &size));
+      job_handle, JobObjectBasicProcessIdList, &jbpidl, size, &size));
 
   EXPECT_EQ(1u, jbpidl.NumberOfAssignedProcesses);
   EXPECT_EQ(1u, jbpidl.NumberOfProcessIdsInList);
diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h
index 2d7b176..dd3c1b1 100644
--- a/sandbox/win/src/sandbox_policy.h
+++ b/sandbox/win/src/sandbox_policy.h
@@ -118,7 +118,7 @@
   // length in:
   //   http://msdn2.microsoft.com/en-us/library/ms684152.aspx
   //
-  // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN.
+  // Note: the recommended level is JOB_LOCKDOWN.
   virtual ResultCode SetJobLevel(JobLevel job_level,
                                  uint32_t ui_exceptions) = 0;
 
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
index 9b770d9..77c0d1a 100644
--- a/sandbox/win/src/sandbox_policy_base.cc
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -110,7 +110,8 @@
       lockdown_default_dacl_(false),
       add_restricting_random_sid_(false),
       effective_token_(nullptr),
-      allow_no_sandbox_job_(false) {
+      allow_no_sandbox_job_(false),
+      job_() {
   dispatcher_ = std::make_unique<TopLevelDispatcher>(this);
 }
 
@@ -149,6 +150,9 @@
 }
 
 ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) {
+  // Cannot set this after the job has been initialized.
+  if (job_.IsValid())
+    return SBOX_ERROR_BAD_PARAMS;
   if (memory_limit_ && job_level == JOB_NONE) {
     return SBOX_ERROR_BAD_PARAMS;
   }
@@ -390,28 +394,37 @@
   return handles_to_share_;
 }
 
-ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) {
-  if (job_level_ == JOB_NONE) {
-    job->Close();
-    return SBOX_ALL_OK;
-  }
+ResultCode PolicyBase::InitJob() {
+  if (job_.IsValid())
+    return SBOX_ERROR_BAD_PARAMS;
 
-  // Create the windows job object.
-  Job job_obj;
-  DWORD result =
-      job_obj.Init(job_level_, nullptr, ui_exceptions_, memory_limit_);
+  if (job_level_ == JOB_NONE)
+    return SBOX_ALL_OK;
+
+  // Create the Windows job object.
+  DWORD result = job_.Init(job_level_, nullptr, ui_exceptions_, memory_limit_);
   if (ERROR_SUCCESS != result)
     return SBOX_ERROR_CANNOT_INIT_JOB;
 
-  *job = job_obj.Take();
   return SBOX_ALL_OK;
 }
 
-ResultCode PolicyBase::DropActiveProcessLimit(base::win::ScopedHandle* job) {
+HANDLE PolicyBase::GetJobHandle() {
+  return job_.GetHandle();
+}
+
+bool PolicyBase::HasJob() {
+  return job_.IsValid();
+}
+
+ResultCode PolicyBase::DropActiveProcessLimit() {
+  if (!job_.IsValid())
+    return SBOX_ERROR_BAD_PARAMS;
+
   if (job_level_ >= JOB_INTERACTIVE)
     return SBOX_ALL_OK;
 
-  if (ERROR_SUCCESS != Job::SetActiveProcessLimit(job, 0))
+  if (ERROR_SUCCESS != job_.SetActiveProcessLimit(0))
     return SBOX_ERROR_CANNOT_UPDATE_JOB_PROCESS_LIMIT;
 
   return SBOX_ALL_OK;
@@ -544,9 +557,9 @@
   return SBOX_ALL_OK;
 }
 
-bool PolicyBase::OnJobEmpty(HANDLE job) {
-  if (target_->Job() == job)
-    target_.reset();
+// Can only be called if a job was associated with this policy.
+bool PolicyBase::OnJobEmpty() {
+  target_.reset();
   return true;
 }
 
diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h
index a88a95b..137beb82 100644
--- a/sandbox/win/src/sandbox_policy_base.h
+++ b/sandbox/win/src/sandbox_policy_base.h
@@ -23,6 +23,7 @@
 #include "sandbox/win/src/app_container_base.h"
 #include "sandbox/win/src/handle_closer.h"
 #include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/job.h"
 #include "sandbox/win/src/policy_engine_opcodes.h"
 #include "sandbox/win/src/policy_engine_params.h"
 #include "sandbox/win/src/sandbox_policy.h"
@@ -87,11 +88,18 @@
 
   // Creates a Job object with the level specified in a previous call to
   // SetJobLevel().
-  ResultCode MakeJobObject(base::win::ScopedHandle* job);
+  ResultCode InitJob();
 
-  // Updates the active process limit on the job to zero. Has no effect
-  // if the job is allowed to spawn processes.
-  ResultCode DropActiveProcessLimit(base::win::ScopedHandle* job);
+  // Returns the handle for this policy's job, or INVALID_HANDLE_VALUE if the
+  // job is not initialized.
+  HANDLE GetJobHandle();
+
+  // Returns true if a job is associated with this policy.
+  bool HasJob();
+
+  // Updates the active process limit on the policy's job to zero.
+  // Has no effect if the job is allowed to spawn processes.
+  ResultCode DropActiveProcessLimit();
 
   // Creates the two tokens with the levels specified in a previous call to
   // SetTokenLevel(). Also creates a lowbox token if specified based on the
@@ -104,10 +112,9 @@
   // call to TargetProcess::Init() is issued.
   ResultCode ApplyToTarget(std::unique_ptr<TargetProcess> target);
 
-  // Called when there are no more active processes in a Job.
-  // Removes a Job object associated with this policy and the target associated
-  // with the job. If a process is not in a job, call OnProcessFinished().
-  bool OnJobEmpty(HANDLE job);
+  // Called when there are no more active processes in the policy's Job.
+  // If a process is not in a job, call OnProcessFinished().
+  bool OnJobEmpty();
 
   // Called when a process no longer needs to be tracked. Processes in jobs
   // should be notified via OnJobEmpty instead.
@@ -186,6 +193,7 @@
 
   HANDLE effective_token_;
   bool allow_no_sandbox_job_;
+  Job job_;
 };
 
 }  // namespace sandbox
diff --git a/sandbox/win/src/sandbox_policy_diagnostic.cc b/sandbox/win/src/sandbox_policy_diagnostic.cc
index 19ed93f..8356419f 100644
--- a/sandbox/win/src/sandbox_policy_diagnostic.cc
+++ b/sandbox/win/src/sandbox_policy_diagnostic.cc
@@ -77,8 +77,6 @@
   switch (job) {
     case JOB_LOCKDOWN:
       return "Lockdown";
-    case JOB_RESTRICTED:
-      return "Restricted";
     case JOB_LIMITED_USER:
       return "Limited User";
     case JOB_INTERACTIVE:
diff --git a/sandbox/win/src/security_level.h b/sandbox/win/src/security_level.h
index 2965a8a..4110bc67 100644
--- a/sandbox/win/src/security_level.h
+++ b/sandbox/win/src/security_level.h
@@ -114,27 +114,21 @@
 //                  | *Forbid changes to the display    |  limit.            |
 //                  |  settings.                        | *Kill on Job close.|
 // -----------------|---------------------------------- |--------------------|
-// JOB_RESTRICTED   | Same as LIMITED_USER plus:        | *One active process|
+// JOB_LOCKDOWN     | Same as LIMITED_USER plus:        | *One active process|
 //                  | * No read/write to the clipboard. |  limit.            |
 //                  | * No access to User Handles that  | *Kill on Job close.|
-//                  |   belong to other processes.      |                    |
-//                  | * Forbid message broadcasts.      |                    |
+//                  |   belong to other processes.      | *Kill on unhandled |
+//                  | * Forbid message broadcasts.      |  exception.        |
 //                  | * Forbid setting global hooks.    |                    |
 //                  | * No access to the global atoms   |                    |
 //                  |   table.                          |                    |
 // -----------------|-----------------------------------|--------------------|
-// JOB_LOCKDOWN     | Same as RESTRICTED                | *One active process|
-//                  |                                   |  limit.            |
-//                  |                                   | *Kill on Job close.|
-//                  |                                   | *Kill on unhandled |
-//                  |                                   |  exception.        |
-//                  |                                   |                    |
+//
 // In the context of the above table, 'user handles' refers to the handles of
 // windows, bitmaps, menus, etc. Files, treads and registry handles are kernel
 // handles and are not affected by the job level settings.
 enum JobLevel {
   JOB_LOCKDOWN = 0,
-  JOB_RESTRICTED,
   JOB_LIMITED_USER,
   JOB_INTERACTIVE,
   JOB_UNPROTECTED,
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index 6ada15b5..67f45a2 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -21,6 +21,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/features.h"
 #include "net/base/isolation_info.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cookies/canonical_cookie.h"
@@ -399,6 +400,22 @@
   return cookie_partition_key_.has_value();
 }
 
+namespace {
+
+bool PartitionedCookiesAllowed(
+    bool partitioned_cookies_runtime_feature_enabled,
+    const absl::optional<net::CookiePartitionKey>& cookie_partition_key) {
+  if (base::FeatureList::IsEnabled(
+          net::features::kPartitionedCookiesBypassOriginTrial) ||
+      partitioned_cookies_runtime_feature_enabled)
+    return true;
+  // We allow partition keys which have a nonce since the Origin Trial is
+  // only meant to test cookies set with the Partitioned attribute.
+  return cookie_partition_key && cookie_partition_key->nonce();
+}
+
+}  // namespace
+
 void RestrictedCookieManager::GetAllForUrl(
     const GURL& url,
     const net::SiteForCookies& site_for_cookies,
@@ -424,10 +441,8 @@
 
   cookie_store_->GetCookieListWithOptionsAsync(
       url, net_options,
-      // We allow partition keys which have a nonce since the Origin Trial is
-      // only meant to test cookies set with the Partitioned attribute.
-      partitioned_cookies_runtime_feature_enabled ||
-              (cookie_partition_key_ && cookie_partition_key_->nonce())
+      PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled,
+                                cookie_partition_key_)
           ? cookie_partition_key_collection_
           : net::CookiePartitionKeyCollection(),
       base::BindOnce(&RestrictedCookieManager::CookieListToGetAllForUrlCallback,
@@ -766,8 +781,8 @@
   std::unique_ptr<net::CanonicalCookie> parsed_cookie =
       net::CanonicalCookie::Create(
           url, cookie, base::Time::Now(), absl::nullopt /* server_time */,
-          partitioned_cookies_runtime_feature_enabled ||
-                  (cookie_partition_key_ && cookie_partition_key_->nonce())
+          PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled,
+                                    cookie_partition_key_)
               ? cookie_partition_key_
               : absl::nullopt,
           &status);
diff --git a/services/network/restricted_cookie_manager_unittest.cc b/services/network/restricted_cookie_manager_unittest.cc
index 50bfe674..9c94d92 100644
--- a/services/network/restricted_cookie_manager_unittest.cc
+++ b/services/network/restricted_cookie_manager_unittest.cc
@@ -2260,6 +2260,43 @@
   EXPECT_TRUE(cookies[0].IsPartitioned());
 }
 
+TEST_P(PartitionedCookiesRestrictedCookieManagerTest,
+       RuntimeEnabledFeature_BypassOriginTrial) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      {net::features::kPartitionedCookies,
+       net::features::kPartitionedCookiesBypassOriginTrial},
+      {});
+
+  const GURL kCookieURL("https://example.com");
+  const GURL kTopFrameURL("https://sub.foo.com");
+  const net::SiteForCookies kSiteForCookies =
+      net::SiteForCookies::FromUrl(kTopFrameURL);
+  const url::Origin kTopFrameOrigin = url::Origin::Create(kTopFrameURL);
+  const net::IsolationInfo kIsolationInfo =
+      net::IsolationInfo::CreateForInternalRequest(kTopFrameOrigin);
+
+  service_->OverrideIsolationInfoForTesting(kIsolationInfo);
+
+  // Setting a partitioned cookie when the RuntimeEnabledFeature is disabled but
+  // the "bypass origin trial" feature is enabled should result in a partitioned
+  // cookie.
+  sync_service_->SetCookieFromString(
+      kCookieURL, kSiteForCookies, kTopFrameOrigin,
+      "__Host-foo=bar; Secure; SameSite=None; Path=/; Partitioned",
+      /*partitioned_cookies_runtime_feature_enabled=*/false);
+
+  auto options = mojom::CookieManagerGetOptions::New();
+  options->name = "";
+  options->match_type = mojom::CookieMatchType::STARTS_WITH;
+
+  auto cookies = sync_service_->GetAllForUrl(
+      kCookieURL, kSiteForCookies, kTopFrameOrigin, std::move(options),
+      /*partitioned_cookies_runtime_feature_enabled=*/true);
+  ASSERT_EQ(1u, cookies.size());
+  EXPECT_TRUE(cookies[0].IsPartitioned());
+}
+
 INSTANTIATE_TEST_SUITE_P(
     PartitionedCookies,
     PartitionedCookiesRestrictedCookieManagerTest,
diff --git a/services/network/websocket_factory.cc b/services/network/websocket_factory.cc
index 3837c8e..98a1fb6 100644
--- a/services/network/websocket_factory.cc
+++ b/services/network/websocket_factory.cc
@@ -24,7 +24,7 @@
 WebSocketFactory::~WebSocketFactory() {
   // Subtle: This is important to avoid WebSocketFactory::Remove calls during
   // |connections_| destruction.
-  connections_.clear();
+  WebSocketSet connections = std::move(connections_);
 }
 
 void WebSocketFactory::CreateWebSocket(
diff --git a/services/network/websocket_factory.h b/services/network/websocket_factory.h
index a088489c..6a4e4a4 100644
--- a/services/network/websocket_factory.h
+++ b/services/network/websocket_factory.h
@@ -77,8 +77,10 @@
   void Remove(WebSocket* impl);
 
  private:
+  using WebSocketSet =
+      std::set<std::unique_ptr<WebSocket>, base::UniquePtrComparator>;
   // The connections held by this factory.
-  std::set<std::unique_ptr<WebSocket>, base::UniquePtrComparator> connections_;
+  WebSocketSet connections_;
 
   WebSocketThrottler throttler_;
 
diff --git a/storage/browser/quota/quota_internals.mojom b/storage/browser/quota/quota_internals.mojom
index 3e882091..6b7a9ce5 100644
--- a/storage/browser/quota/quota_internals.mojom
+++ b/storage/browser/quota/quota_internals.mojom
@@ -19,8 +19,16 @@
     mojo_base.mojom.Time last_modified;
 };
 
+// Represents the Storage Type for a given host.
+// This is a subset of blink::mojom::StorageType.
+enum StorageType {
+  kTemporary = 0,
+  kPersistent = 1,
+  kSyncable = 2,
+};
+
 // Interface for controlling Quota Internals.
-// Hosted on chrome://quota-internals" for WebUI content::QuotaInternalsUI.
+// Hosted on "chrome://quota-internals" for WebUI content::QuotaInternalsUI.
 interface QuotaInternalsHandler {
     // Returns the total and available disk space in bits for a user,
     // which is then converted to bytes and displayed on the Quota Internals UI.
@@ -37,4 +45,8 @@
 
     // Returns an array of Storage Bucket entries stored in the QuotaDatabase.
     RetrieveBucketsTable() => (array<BucketTableEntry> entries);
+
+    // Returns a host's usage for a given storage type.
+    GetHostUsageForInternals(string host, StorageType storage_type) =>
+        (int64 host_usage);
 };
\ No newline at end of file
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index 7b09d41e..3ca7d8d 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -117,6 +117,17 @@
   }
 }
 
+StorageType GetBlinkStorageType(storage::mojom::StorageType type) {
+  switch (type) {
+    case storage::mojom::StorageType::kTemporary:
+      return StorageType::kTemporary;
+    case storage::mojom::StorageType::kPersistent:
+      return StorageType::kPersistent;
+    case storage::mojom::StorageType::kSyncable:
+      return StorageType::kSyncable;
+  }
+}
+
 QuotaErrorOr<BucketInfo> GetOrCreateBucketOnDBThread(
     const StorageKey& storage_key,
     const std::string& bucket_name,
@@ -1688,6 +1699,22 @@
   usage_tracker->GetHostUsageWithBreakdown(host, std::move(callback));
 }
 
+void QuotaManagerImpl::GetHostUsageForInternals(
+    const std::string& host,
+    storage::mojom::StorageType storage_type,
+    GetHostUsageForInternalsCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  EnsureDatabaseOpened();
+
+  StorageType type = GetBlinkStorageType(storage_type);
+  UsageTracker* usage_tracker = GetUsageTracker(type);
+  DCHECK(usage_tracker);
+
+  usage_tracker->GetHostUsageWithBreakdown(
+      host, base::BindOnce(&QuotaManagerImpl::OnGetHostUsageForInternals,
+                           weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 bool QuotaManagerImpl::IsStorageUnlimited(const StorageKey& storage_key,
                                           StorageType type) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -1897,6 +1924,16 @@
   return nullptr;
 }
 
+void QuotaManagerImpl::OnGetHostUsageForInternals(
+    GetHostUsageForInternalsCallback callback,
+    int64_t usage,
+    blink::mojom::UsageBreakdownPtr usage_breakdown) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_GE(usage, -1);
+
+  std::move(callback).Run(usage);
+}
+
 void QuotaManagerImpl::NotifyStorageAccessed(const StorageKey& storage_key,
                                              StorageType type,
                                              base::Time access_time) {
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 5e8fff9..6d33518 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -355,6 +355,10 @@
   void GetDiskAvailability(GetDiskAvailabilityCallback callback) override;
   void GetStatistics(GetStatisticsCallback callback) override;
   void RetrieveBucketsTable(RetrieveBucketsTableCallback callback) override;
+  void GetHostUsageForInternals(
+      const std::string& host,
+      storage::mojom::StorageType storage_type,
+      GetHostUsageForInternalsCallback callback) override;
 
   // Called by UI and internal modules.
   void GetPersistentHostQuota(const std::string& host, QuotaCallback callback);
@@ -544,6 +548,10 @@
   void DumpBucketTable(DumpBucketTableCallback callback);
   void DidRetrieveBucketsTable(RetrieveBucketsTableCallback callback,
                                const BucketTableEntries& entries);
+  void OnGetHostUsageForInternals(
+      GetHostUsageForInternalsCallback callback,
+      int64_t usage,
+      blink::mojom::UsageBreakdownPtr usage_breakdown);
 
   // Runs BucketDataDeleter which calls QuotaClients to clear data for the
   // bucket. Once the task is complete, calls the QuotaDatabase to delete the
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index 33bad3a..242461ac 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -2579,6 +2579,32 @@
   EXPECT_EQ(0, usage());
 }
 
+TEST_F(QuotaManagerImplTest, GetHostUsageForInternals) {
+  static const ClientBucketData kData[] = {
+      {"http://example.com/", kDefaultBucketName, kTemp, 400},
+      {"http://example.com/", kDefaultBucketName, kPerm, 2},
+  };
+  MockQuotaClient* client =
+      CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp, kPerm});
+  RegisterClientBucketData(client, kData);
+
+  base::test::TestFuture<int64_t> temp_future;
+  quota_manager_impl()->GetHostUsageForInternals(
+      "example.com", storage::mojom::StorageType::kTemporary,
+      temp_future.GetCallback());
+  int64_t temp_result = temp_future.Take();
+
+  EXPECT_EQ(400, temp_result);
+
+  base::test::TestFuture<int64_t> perm_future;
+  quota_manager_impl()->GetHostUsageForInternals(
+      "example.com", storage::mojom::StorageType::kPersistent,
+      perm_future.GetCallback());
+  int64_t perm_result = perm_future.Take();
+
+  EXPECT_EQ(2, perm_result);
+}
+
 TEST_F(QuotaManagerImplTest, NotifyAndLRUBucket) {
   static const ClientBucketData kData[] = {
       {"http://a.com/", kDefaultBucketName, kTemp, 0},
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index a946e60..b0d9b1f 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -30,6 +30,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 6
         },
@@ -93,6 +94,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "named_caches": [
             {
               "name": "cros_vm",
@@ -340,6 +342,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "named_caches": [
             {
               "name": "cros_vm",
@@ -1323,6 +1326,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 6
         },
@@ -1416,6 +1420,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 6
         },
@@ -1616,6 +1621,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 6
         },
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index a47dfd6a..a06e04f 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -1096,7 +1096,8 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android19.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android19.textpb",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_k.net_unittests.filter"
         ],
         "merge": {
           "args": [
@@ -2102,7 +2103,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2169,7 +2170,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2238,7 +2239,7 @@
           "--test-launcher-batch-limit=1",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2305,7 +2306,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2372,7 +2373,7 @@
         "args": [
           "angle_unittests",
           "-v",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2425,7 +2426,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2492,7 +2493,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2559,7 +2560,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2626,7 +2627,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2693,7 +2694,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2762,7 +2763,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2829,7 +2830,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2896,7 +2897,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -2964,7 +2965,7 @@
           "--gtest_filter=-*UsingRealWebcam*",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3031,7 +3032,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3098,7 +3099,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3165,7 +3166,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb",
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb",
           "--git-revision=${got_revision}"
         ],
         "isolate_profile_data": true,
@@ -3238,7 +3239,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3306,7 +3307,7 @@
           "--gtest_filter=-org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerTest.test*ExternalNavigationWithUserGesture*",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb",
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb",
           "--git-revision=${got_revision}"
         ],
         "isolate_profile_data": true,
@@ -3381,7 +3382,7 @@
           "--gtest_filter=org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerTest.test*ExternalNavigationWithUserGesture*",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb",
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb",
           "--git-revision=${got_revision}"
         ],
         "isolate_profile_data": true,
@@ -3455,7 +3456,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb",
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb",
           "--git-revision=${got_revision}"
         ],
         "isolate_profile_data": true,
@@ -3528,7 +3529,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3595,7 +3596,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3663,7 +3664,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3731,7 +3732,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3799,7 +3800,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3867,7 +3868,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -3934,7 +3935,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4001,7 +4002,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4068,7 +4069,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4135,7 +4136,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4202,7 +4203,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4269,7 +4270,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4336,7 +4337,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4404,7 +4405,7 @@
           "--use-cmd-decoder=validating",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4472,7 +4473,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4539,7 +4540,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4606,7 +4607,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4673,7 +4674,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4740,7 +4741,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4807,7 +4808,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4874,7 +4875,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -4941,7 +4942,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5008,7 +5009,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5075,7 +5076,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5142,7 +5143,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5209,7 +5210,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5276,7 +5277,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5343,7 +5344,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5410,7 +5411,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5478,7 +5479,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5545,7 +5546,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5612,7 +5613,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5679,7 +5680,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5746,7 +5747,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5813,7 +5814,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5880,7 +5881,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -5947,7 +5948,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6014,7 +6015,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6081,7 +6082,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6148,7 +6149,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6215,7 +6216,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6283,7 +6284,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6350,7 +6351,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6417,7 +6418,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6484,7 +6485,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6551,7 +6552,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6618,7 +6619,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6685,7 +6686,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6752,7 +6753,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6825,7 +6826,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6893,7 +6894,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -6960,7 +6961,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -7027,7 +7028,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "merge": {
@@ -7095,7 +7096,7 @@
       {
         "args": [
           "--gtest-benchmark-name=components_perftests",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_name": "components_perftests",
         "isolate_profile_data": true,
@@ -7149,7 +7150,7 @@
       {
         "args": [
           "--platform=android",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_name": "content_shell_crash_test",
         "isolate_profile_data": true,
@@ -7200,7 +7201,7 @@
       },
       {
         "args": [
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_name": "monochrome_public_apk_checker",
         "isolate_profile_data": true,
@@ -7258,7 +7259,7 @@
           "-v",
           "--passthrough",
           "--retry-limit=2",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_name": "telemetry_perf_unittests_android_chrome",
         "isolate_profile_data": true,
@@ -7314,7 +7315,7 @@
           "-v",
           "--passthrough",
           "--retry-limit=2",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_name": "telemetry_perf_unittests_android_monochrome",
         "isolate_profile_data": true,
@@ -7366,7 +7367,7 @@
       {
         "args": [
           "--extra-browser-args=--enable-crashpad",
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_name": "telemetry_perf_unittests_android_chrome",
         "isolate_profile_data": true,
@@ -7677,7 +7678,7 @@
     "scripts": [
       {
         "args": [
-          "--avd-config=../../tools/android/avd/proto/generic_android25.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android24.textpb"
         ],
         "isolate_profile_data": true,
         "name": "check_network_annotations",
@@ -7840,7 +7841,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8260,7 +8261,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8344,7 +8345,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8764,7 +8765,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 6367699..7cc5d4b 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -44881,7 +44881,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -45301,7 +45301,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -45385,7 +45385,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -45805,7 +45805,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -45893,7 +45893,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46313,7 +46313,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46397,7 +46397,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46817,7 +46817,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46972,7 +46972,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47392,7 +47392,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47476,7 +47476,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47896,7 +47896,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48051,7 +48051,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48471,7 +48471,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48555,7 +48555,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.46"
+              "revision": "version:100.0.4896.49"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48975,7 +48975,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.76"
+              "revision": "version:99.0.4844.80"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 5c90a77..b43bd71 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -234,6 +234,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "named_caches": [
             {
               "name": "cros_vm",
@@ -1313,6 +1314,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 6
         },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 4a0b311..0b48a86 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -15747,6 +15747,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "named_caches": [
             {
               "name": "cros_vm",
@@ -16987,6 +16988,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "named_caches": [
             {
               "name": "cros_vm",
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index bc5ba530..239f560 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -341,6 +341,7 @@
     "//testing/buildbot/filters/android.emulator_10.net_unittests.filter",
     "//testing/buildbot/filters/android.emulator_11.net_unittests.filter",
     "//testing/buildbot/filters/android.emulator_l.net_unittests.filter",
+    "//testing/buildbot/filters/android.emulator_k.net_unittests.filter",
     "//testing/buildbot/filters/fuchsia.lsan.net_unittests.filter",
     "//testing/buildbot/filters/fuchsia.net_unittests.filter",
   ]
diff --git a/testing/buildbot/filters/android.emulator_k.net_unittests.filter b/testing/buildbot/filters/android.emulator_k.net_unittests.filter
new file mode 100644
index 0000000..1a50ef31
--- /dev/null
+++ b/testing/buildbot/filters/android.emulator_k.net_unittests.filter
@@ -0,0 +1,13 @@
+# crbug.com/1301957
+-TransportClientSocketPoolTest.Tag
+-TransportClientSocketPoolTest.TagSSLDirect
+-TransportClientSocketPoolTest.TagSSLDirectTwoSockets
+-TransportClientSocketPoolTest.TagSSLDirectTwoSocketsFullPool
+-TCPClientSocketTest.TagAfterConnect
+-TCPClientSocketTest.Tag
+-TCPSocketTest.Tag
+-TCPSocketTest.TagAfterConnect
+-TrafficStatsAndroidTest.*
+-URLRequestTestHTTP.TestTagging
+-SocketTagTest.Apply
+-UDPSocketTest.Tag
diff --git a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter
index 11e0dbe..816a9e5 100644
--- a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter
+++ b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-combined-rel/unit_tests.filter
@@ -917,7 +917,6 @@
 -ChromeHintsManagerPushDisabledTest.PushManagerSetOnAndroid
 -ChromeHintsManagerPushEnabledTest.PushManagerSetOnAndroid
 -ChromeInfoMapTest.CheckPermissions
--ChromeKeySystemsProviderTest.IsKeySystemsUpdateNeeded
 -ChromeLocalPresentationManagerFactoryTest.CreateForRegularProfile
 -ChromeLoggingTest.EnvironmentAndFlagLogFileName
 -ChromeLoggingTest.EnvironmentLogFileName
diff --git a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter
index 11e0dbe..816a9e5 100644
--- a/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter
+++ b/testing/buildbot/filters/stable_test_filters/linux-stable-filter-rel/unit_tests.filter
@@ -917,7 +917,6 @@
 -ChromeHintsManagerPushDisabledTest.PushManagerSetOnAndroid
 -ChromeHintsManagerPushEnabledTest.PushManagerSetOnAndroid
 -ChromeInfoMapTest.CheckPermissions
--ChromeKeySystemsProviderTest.IsKeySystemsUpdateNeeded
 -ChromeLocalPresentationManagerFactoryTest.CreateForRegularProfile
 -ChromeLoggingTest.EnvironmentAndFlagLogFileName
 -ChromeLoggingTest.EnvironmentLogFileName
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index b5e23fd1..53f26db 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1687,7 +1687,6 @@
     "args": [
       "--replace-system-package",
       "org.chromium.webview_shell,apks/SystemWebViewShell.apk",
-      "--use-apk-under-test-flags-file",
     ],
     "label": "//android_webview/tools/system_webview_shell:system_webview_shell_layout_test_apk",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index cc28eda..a9b025e3 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -186,6 +186,7 @@
             }
           ],
           "idempotent": false,
+          "io_timeout": 3600,
           "named_caches": [
             {
               "name": "cros_vm",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 2a32b29..ac7de16 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -953,7 +953,7 @@
   'nougat-x86-emulator': {
     '$mixin_append': {
       'args': [
-        '--avd-config=../../tools/android/avd/proto/generic_android25.textpb',
+        '--avd-config=../../tools/android/avd/proto/generic_android24.textpb',
       ],
     },
     'swarming': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 77d1e39..46adbd7 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2324,6 +2324,11 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_11.net_unittests.filter',
         ],
       },
+      'android-cronet-x86-dbg-kitkat-tests': {
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_k.net_unittests.filter',
+        ],
+      },
       'android-cronet-x86-dbg-lollipop-tests': {
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_l.net_unittests.filter',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 5d0943d1..87329ab 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -612,6 +612,8 @@
       'chrome_all_tast_tests': {
         'swarming': {
           'idempotent': False,  # https://crbug.com/923426#c27
+          # Tast test doesn't always output. See crbug.com/1306300
+          'io_timeout': 3600,
           'shards': 6,
         },
         'resultdb': {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 00723155e..7c490b7a 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -459,7 +459,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.46',
+          'revision': 'version:100.0.4896.49',
         }
       ],
     },
@@ -483,7 +483,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.76',
+          'revision': 'version:99.0.4844.80',
         }
       ],
     },
@@ -603,7 +603,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.46',
+          'revision': 'version:100.0.4896.49',
         }
       ],
     },
@@ -627,7 +627,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.76',
+          'revision': 'version:99.0.4844.80',
         }
       ],
     },
@@ -747,7 +747,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.46',
+          'revision': 'version:100.0.4896.49',
         }
       ],
     },
@@ -771,7 +771,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.76',
+          'revision': 'version:99.0.4844.80',
         }
       ],
     },
diff --git a/testing/test.gni b/testing/test.gni
index a7682c6..b6c2bbf 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -675,7 +675,7 @@
 
     _generated_script = "$root_build_dir/bin/run_" + invoker.target_name
     if (is_skylab) {
-      generate_skylab_runner_script(_gen_runner_target) {
+      generate_skylab_deps(_gen_runner_target) {
         generated_script = _generated_script
         test_exe = invoker.target_name
       }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 64fc547c..46b36800 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -330,6 +330,37 @@
             ]
         }
     ],
+    "AndroidTNotifications": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "RationaleShownAtFirstRequest",
+                    "params": {
+                        "always_show_rationale_before_requesting_permission": "true",
+                        "notification_permission_dialog_text_variant_2": "false",
+                        "permission_request_interval_days": "7"
+                    },
+                    "enable_features": [
+                        "NotificationPermissionVariant"
+                    ]
+                },
+                {
+                    "name": "TextVariant2",
+                    "params": {
+                        "always_show_rationale_before_requesting_permission": "false",
+                        "notification_permission_dialog_text_variant_2": "true",
+                        "permission_request_interval_days": "7"
+                    },
+                    "enable_features": [
+                        "NotificationPermissionVariant"
+                    ]
+                }
+            ]
+        }
+    ],
     "AppListLaunchRecorder": [
         {
             "platforms": [
@@ -525,6 +556,32 @@
             ]
         }
     ],
+    "AssistantVoiceSearch": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "ModalV2_20220310",
+                    "params": {
+                        "colorful_mic": "false",
+                        "count": "2",
+                        "enable_multi_account_check": "false",
+                        "experiment_id": "48113782",
+                        "min_agsa_version": "12.37"
+                    },
+                    "enable_features": [
+                        "AssistantConsentModal",
+                        "AssistantConsentV2",
+                        "AssistantIntentExperimentId",
+                        "OmniboxAssistantVoiceSearch",
+                        "VoiceSearchAudioCapturePolicy"
+                    ]
+                }
+            ]
+        }
+    ],
     "AsyncFontAccess": [
         {
             "platforms": [
@@ -1348,31 +1405,6 @@
             ]
         }
     ],
-    "BaseAssistantIntegration": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "colorful_mic": "false",
-                        "count": "2",
-                        "enable_multi_account_check": "false",
-                        "experiment_id": "48101548",
-                        "min_agsa_version": "12.37"
-                    },
-                    "enable_features": [
-                        "AssistantConsentV2",
-                        "AssistantIntentExperimentId",
-                        "OmniboxAssistantVoiceSearch",
-                        "VoiceSearchAudioCapturePolicy"
-                    ]
-                }
-            ]
-        }
-    ],
     "BiometricAuthPwdFillAndroid": [
         {
             "platforms": [
@@ -3498,6 +3530,21 @@
             ]
         }
     ],
+    "HardwareToolbar": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ToolbarUseHardwareBitmapDraw"
+                    ]
+                }
+            ]
+        }
+    ],
     "HatsAudio": [
         {
             "platforms": [
@@ -4492,23 +4539,6 @@
             ]
         }
     ],
-    "NetworkServiceDedicatedThread": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Disabled",
-                    "disable_features": [
-                        "NetworkServiceDedicatedThread"
-                    ]
-                }
-            ]
-        }
-    ],
     "NetworkServiceUsesDisplayThreadPriority": [
         {
             "platforms": [
@@ -6509,7 +6539,6 @@
             "platforms": [
                 "chromeos",
                 "chromeos_lacros",
-                "linux",
                 "mac",
                 "windows"
             ],
@@ -6522,42 +6551,6 @@
                     "enable_features": [
                         "SubframeShutdownDelay"
                     ]
-                },
-                {
-                    "name": "EnabledConstantLong",
-                    "params": {
-                        "type": "constant-long"
-                    },
-                    "enable_features": [
-                        "SubframeShutdownDelay"
-                    ]
-                },
-                {
-                    "name": "EnabledHistoryBased",
-                    "params": {
-                        "type": "history-based"
-                    },
-                    "enable_features": [
-                        "SubframeShutdownDelay"
-                    ]
-                },
-                {
-                    "name": "EnabledHistoryBasedLong",
-                    "params": {
-                        "type": "history-based-long"
-                    },
-                    "enable_features": [
-                        "SubframeShutdownDelay"
-                    ]
-                },
-                {
-                    "name": "EnabledMemoryBased",
-                    "params": {
-                        "type": "memory-based"
-                    },
-                    "enable_features": [
-                        "SubframeShutdownDelay"
-                    ]
                 }
             ]
         }
diff --git a/third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml b/third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml
new file mode 100644
index 0000000..60bdbf5
--- /dev/null
+++ b/third_party/android_sdk/cipd/system_images/android-24/google_apis/x86.yaml
@@ -0,0 +1,9 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+package: chromium/third_party/android_sdk/public/system-images/android-24/google_apis/x86
+description: system_images;android-24;google_apis;x86
+root: ../../../../public/
+data:
+  - dir: system-images/android-24/google_apis/x86
diff --git a/third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml b/third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml
new file mode 100644
index 0000000..dae6d22
--- /dev/null
+++ b/third_party/android_sdk/cipd/system_images/android-24/google_apis_playstore/x86.yaml
@@ -0,0 +1,9 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+package: chromium/third_party/android_sdk/public/system-images/android-24/google_apis_playstore/x86
+description: system_images;android-24;google_apis_playstore;x86
+root: ../../../../public/
+data:
+  - dir: system-images/android-24/google_apis_playstore/x86
diff --git a/third_party/blink/common/custom_handlers/protocol_handler_utils.cc b/third_party/blink/common/custom_handlers/protocol_handler_utils.cc
index c9a75e1..bd72d33 100644
--- a/third_party/blink/common/custom_handlers/protocol_handler_utils.cc
+++ b/third_party/blink/common/custom_handlers/protocol_handler_utils.cc
@@ -7,13 +7,19 @@
 #include "base/containers/contains.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "third_party/blink/public/common/scheme_registry.h"
+#include "url/gurl.h"
 
 namespace blink {
 
 bool IsValidCustomHandlerScheme(const base::StringPiece scheme,
-                                bool allow_ext_prefix,
-                                bool& has_custom_scheme_prefix) {
-  has_custom_scheme_prefix = false;
+                                ProtocolHandlerSecurityLevel security_level,
+                                bool* has_custom_scheme_prefix) {
+  bool allow_scheme_prefix =
+      (security_level >= ProtocolHandlerSecurityLevel::kExtensionFeatures);
+  if (has_custom_scheme_prefix)
+    *has_custom_scheme_prefix = false;
 
   static constexpr const char kWebPrefix[] = "web+";
   static constexpr const char kExtPrefix[] = "ext+";
@@ -21,10 +27,11 @@
   static constexpr const size_t kPrefixLength = std::size(kWebPrefix) - 1;
   if (base::StartsWith(scheme, kWebPrefix,
                        base::CompareCase::INSENSITIVE_ASCII) ||
-      (allow_ext_prefix &&
+      (allow_scheme_prefix &&
        base::StartsWith(scheme, kExtPrefix,
                         base::CompareCase::INSENSITIVE_ASCII))) {
-    has_custom_scheme_prefix = true;
+    if (has_custom_scheme_prefix)
+      *has_custom_scheme_prefix = true;
     // HTML5 requires that schemes with the |web+| prefix contain one or more
     // ASCII alphas after that prefix.
     auto scheme_name = scheme.substr(kPrefixLength);
@@ -46,4 +53,13 @@
   return base::Contains(kProtocolSafelist, base::ToLowerASCII(scheme));
 }
 
+bool IsAllowedCustomHandlerURL(const GURL& url,
+                               ProtocolHandlerSecurityLevel security_level) {
+  bool has_valid_scheme =
+      url.SchemeIsHTTPOrHTTPS() ||
+      (security_level == ProtocolHandlerSecurityLevel::kExtensionFeatures &&
+       CommonSchemeRegistry::IsExtensionScheme(url.scheme()));
+  return has_valid_scheme && network::IsUrlPotentiallyTrustworthy(url);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index e8c1c486..f576c719 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -333,11 +333,6 @@
 // The feature is still used by virtual test suites exercising Plan B.
 const base::Feature kRTCUnifiedPlanByDefault{"RTCUnifiedPlanByDefault",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
-// When enabled, throw an exception when an RTCPeerConnection is constructed
-// with {sdpSemantics:"plan-b"} and the Deprecation Trial is not enabled.
-const base::Feature kRTCDisallowPlanBOutsideDeprecationTrial{
-    "RTCDisallowPlanBOutsideDeprecationTrial",
-    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Determines if the SDP attrbute extmap-allow-mixed should be offered by
 // default or not. The default value can be overridden by passing
@@ -1045,6 +1040,9 @@
 // requesting contexts.
 const base::FeatureParam<int> kBrowsingTopicsNumberOfEpochsToExpose{
     &kBrowsingTopics, "number_of_epochs_to_expose", 3};
+// The periodic topics calculation interval.
+const base::FeatureParam<base::TimeDelta> kBrowsingTopicsTimePeriodPerEpoch{
+    &kBrowsingTopics, "time_period_per_epoch", base::Days(7)};
 // The number of top topics to derive and to keep for each epoch (week).
 const base::FeatureParam<int> kBrowsingTopicsNumberOfTopTopicsPerEpoch{
     &kBrowsingTopics, "number_of_top_topics_per_epoch", 5};
@@ -1055,6 +1053,18 @@
 // topic instead of one of the top topics.
 const base::FeatureParam<int> kBrowsingTopicsUseRandomTopicProbabilityPercent{
     &kBrowsingTopics, "use_random_topic_probability_percent", 5};
+// How many epochs (weeks) of API usage data (i.e. topics observations) will be
+// based off for the filtering of topics for a calling context.
+const base::FeatureParam<int>
+    kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering{
+        &kBrowsingTopics,
+        "number_of_epochs_of_observation_data_to_use_for_filtering", 3};
+// The max number of observed-by context domains to keep for each top topic.
+// The intent is to cap the in-use memory.
+const base::FeatureParam<int>
+    kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic{
+        &kBrowsingTopics,
+        "max_number_of_api_usage_context_domains_to_keep_per_topic", 1000};
 // The max number of entries allowed to be retrieved from the
 // `BrowsingTopicsSiteDataStorage` database for each query for the API usage
 // contexts. The query will occur once per epoch (week) at topics calculation
@@ -1063,18 +1073,15 @@
     kBrowsingTopicsMaxNumberOfApiUsageContextEntriesToLoadPerEpoch{
         &kBrowsingTopics,
         "max_number_of_api_usage_context_entries_to_load_per_epoch", 100000};
-// Encodes the rest of the configuration parameters. Each version number should
-// only be mapped to one configuration set. In practice, this can be guaranteed
-// by always bumping up the version number whenever parameters are updated.
+// Encodes the configuration parameters above. Each version number should only
+// be mapped to one configuration set. In practice, this can be guaranteed by
+// always bumping up the version number whenever parameters are updated.
 const base::FeatureParam<int> kBrowsingTopicsConfigVersion{&kBrowsingTopics,
                                                            "config_version", 1};
-
-// Enable the ability to minimize processing in the WebRTC APM when all audio
-// tracks are disabled. If disabled, the APM in WebRTC will ignore attempts to
-// set it in a low-processing mode when all audio tracks are disabled.
-const base::Feature kMinimizeAudioProcessingForUnusedOutput{
-    "MinimizeAudioProcessingForUnusedOutput",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+// The taxonomy version. This only affects the topics classification that occurs
+// during this browser session, and doesn't affect the pre-existing epochs.
+const base::FeatureParam<int> kBrowsingTopicsTaxonomyVersion{
+    &kBrowsingTopics, "taxonomy_version", 1};
 
 // When <dialog>s are closed, this focuses the "previously focused" element
 // which had focus when the <dialog> was first opened.
diff --git a/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h b/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h
index b9889e3d..51f4892b 100644
--- a/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h
+++ b/third_party/blink/public/common/custom_handlers/protocol_handler_utils.h
@@ -7,6 +7,9 @@
 
 #include "base/strings/string_piece_forward.h"
 #include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/security/protocol_handler_security_level.h"
+
+class GURL;
 
 namespace blink {
 
@@ -24,8 +27,16 @@
 // insensitive match to the string "web+" (or alternatively "ext+" if allowed).
 bool BLINK_COMMON_EXPORT
 IsValidCustomHandlerScheme(const base::StringPiece scheme,
-                           bool allow_ext_prefix,
-                           bool& has_custom_scheme_prefix);
+                           ProtocolHandlerSecurityLevel security_level,
+                           bool* has_custom_scheme_prefix = nullptr);
+
+// This function returns whether the specified URL is allowed as a protocol
+// handler parameter, as described in steps 6 and 7 (except same origin) of the
+// HTML specification:
+// https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
+bool BLINK_COMMON_EXPORT
+IsAllowedCustomHandlerURL(const GURL& url,
+                          ProtocolHandlerSecurityLevel security_level);
 
 }  // namespace blink
 
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 16aa2ed..d203df37 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -153,8 +153,6 @@
     kPurgeRendererMemoryWhenBackgrounded;
 BLINK_COMMON_EXPORT extern const base::Feature kWindowOpenNewPopupBehavior;
 BLINK_COMMON_EXPORT extern const base::Feature kRTCUnifiedPlanByDefault;
-BLINK_COMMON_EXPORT extern const base::Feature
-    kRTCDisallowPlanBOutsideDeprecationTrial;
 BLINK_COMMON_EXPORT extern const base::Feature kRTCOfferExtmapAllowMixed;
 BLINK_COMMON_EXPORT extern const base::Feature kRTCGpuCodecSupportWaiter;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
@@ -467,6 +465,8 @@
 BLINK_COMMON_EXPORT bool IsAllowURNsInIframeEnabled();
 
 BLINK_COMMON_EXPORT extern const base::Feature kBrowsingTopics;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<base::TimeDelta>
+    kBrowsingTopicsTimePeriodPerEpoch;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
     kBrowsingTopicsNumberOfEpochsToExpose;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
@@ -474,14 +474,15 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
     kBrowsingTopicsUseRandomTopicProbabilityPercent;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
+    kBrowsingTopicsNumberOfEpochsOfObservationDataToUseForFiltering;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
+    kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
     kBrowsingTopicsMaxNumberOfApiUsageContextEntriesToLoadPerEpoch;
 BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
     kBrowsingTopicsConfigVersion;
-
-// Control switch for minimizing processing in the WebRTC APM when all audio
-// tracks are disabled.
-BLINK_COMMON_EXPORT extern const base::Feature
-    kMinimizeAudioProcessingForUnusedOutput;
+BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
+    kBrowsingTopicsTaxonomyVersion;
 
 // When <dialog>s are closed, this focuses the "previously focused" element
 // which had focus when the <dialog> was first opened.
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index f202340..8abe2c7 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -6940,6 +6940,7 @@
       ambient-light-sensor
       attribution-reporting
       autoplay
+      browsing-topics
       camera
       ch-dpr
       ch-device-memory
@@ -6980,6 +6981,7 @@
       gyroscope
       hid
       idle-detection
+      interest-cohort
       join-ad-interest-group
       keyboard-map
       magnetometer
diff --git a/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom b/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom
index 3f3d72e..8252702 100644
--- a/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom
+++ b/third_party/blink/public/mojom/loader/same_document_navigation_type.mojom
@@ -11,6 +11,6 @@
   kFragment = 0,
   // Navigating with window.history
   kHistoryApi = 1,
-  // Navigating with AppHistoryNavigateEvent's transitionWhile()
-  kAppHistoryTransitionWhile = 2,
+  // Navigating with the navigation API NavigateEvent's transitionWhile()
+  kNavigationApiTransitionWhile = 2,
 };
diff --git a/third_party/blink/public/mojom/navigation/navigation_params.mojom b/third_party/blink/public/mojom/navigation/navigation_params.mojom
index 926c92d..d8fe330e 100644
--- a/third_party/blink/public/mojom/navigation/navigation_params.mojom
+++ b/third_party/blink/public/mojom/navigation/navigation_params.mojom
@@ -482,4 +482,11 @@
 
   // When URL is about:srcdoc, this carries the srcdoc attribute's value.
   string srcdoc_value;
+
+  // Whether the navigation should be treated as a "loadDataWithBaseURL"
+  // navigation, where the "document URL" is set to the supplied base URL
+  // instead of the data URL set in CommonNavigationParams' `url`. If this
+  // returns false, the data: URL will still be loaded, but we won't try to use
+  // the supplied base URL and history URL.
+  bool is_load_data_with_base_url = false;
 };
diff --git a/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom b/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
index cf48d85..e552e65 100644
--- a/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
+++ b/third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom
@@ -208,6 +208,14 @@
   // cookies.
   kClientHintPartitionedCookies = 96,
 
+  // "browsing-topics" permissions policy that controls the use of Topics API.
+  // https://github.com/jkarlin/topics
+  kBrowsingTopics = 97,
+
+  // "interest-cohort" permissions policy that controls the use of Topics API.
+  // https://github.com/jkarlin/topics
+  kBrowsingTopicsBackwardCompatible = 98,
+
   // Don't change assigned numbers of any item, and don't reuse removed slots.
   // Add new features at the end of the enum.
   // Also, run update_permissions_policy_enum.py in
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 6bfe8c9..75cf522 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3116,7 +3116,7 @@
   kCSSAtRuleCounterStyle = 3809,
   kCanvasUseColorSpace = 3810,
   kSelectMenuElement = 3811,
-  kRTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial = 3812,
+  kOBSOLETE_RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial = 3812,
   kWebAppManifestCaptureLinks = 3813,
   kSanitizerAPICreated = 3814,
   kSanitizerAPIDefaultConfiguration = 3815,
@@ -3500,6 +3500,7 @@
   kV8FunctionPrototypeArguments = 4179,
   kV8FunctionPrototypeCaller = 4180,
   kBluetoothDeviceForget = 4181,
+  kTopicsAPI_BrowsingTopics_Method = 4182,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/DEPS b/third_party/blink/renderer/DEPS
index 80469c65..299ff841 100644
--- a/third_party/blink/renderer/DEPS
+++ b/third_party/blink/renderer/DEPS
@@ -68,6 +68,7 @@
     "+cc/paint",
     "+components/crash/core/common/crash_key.h",
     "+components/power_scheduler",
+    "+net/base/features.h",
     "+net/cookies",
     "+net/http/structured_headers.h",
     "+services/network/public/mojom",
diff --git a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc
index 035e906..a2509e5 100644
--- a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/platform/bindings/callback_function_base.h"
 #include "third_party/blink/renderer/platform/bindings/callback_interface_base.h"
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 
 namespace blink {
 
@@ -89,6 +90,12 @@
       callback_this_ =
           callback_this.V8Value(callback_->CallbackRelevantScriptState());
     }
+    if (auto* tracker =
+            ThreadScheduler::Current()->GetTaskAttributionTracker()) {
+      task_attribution_scope_ =
+          tracker->CreateTaskScope(callback_->CallbackRelevantScriptState(),
+                                   callback_->GetParentTaskId());
+    }
   }
 
   return true;
diff --git a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h
index d9c1dafe7..3872b1b 100644
--- a/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h
+++ b/third_party/blink/renderer/bindings/core/v8/callback_invoke_helper.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_value_or_script_wrappable_adapter.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -94,6 +95,8 @@
 
   ScriptState::Scope callback_relevant_context_scope_;
   v8::Context::BackupIncumbentScope backup_incumbent_scope_;
+  std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope>
+      task_attribution_scope_;
 };
 
 extern template class CORE_EXTERN_TEMPLATE_EXPORT
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index 857cf93..4974f81 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -1562,6 +1562,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_eventlisteneroptions.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_scrollintoviewoptions.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_scrollintoviewoptions.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_string_unrestricteddouble_null.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_boolean_string_unrestricteddouble_null.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_bytestringbytestringrecord_bytestringsequencesequence.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_bytestringbytestringrecord_bytestringsequencesequence.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_union_compositeoperationorauto_compositeoperationorautosequence.cc",
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 201fed4..7bdaadd6 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -203,6 +203,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_le_scan_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_manufacturer_data_filter_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_manufacturer_data_filter_init.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_browsing_topic.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_browsing_topic.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_authentication_data.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_authentication_data.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_registration_data.cc",
@@ -859,6 +861,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_save_file_picker_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_scheduler_post_task_options.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_script_wrappable_task_id.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_script_wrappable_task_id.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_sensor_error_event_init.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index e3e3ef61..d8548bc 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -74,6 +74,8 @@
           "//third_party/blink/renderer/modules/breakout_box/media_stream_track_processor_init.idl",
           "//third_party/blink/renderer/modules/breakout_box/video_track_generator.idl",
           "//third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.idl",
+          "//third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl",
+          "//third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl",
           "//third_party/blink/renderer/modules/buckets/navigator_storage_buckets.idl",
           "//third_party/blink/renderer/modules/buckets/storage_bucket.idl",
           "//third_party/blink/renderer/modules/buckets/storage_bucket_manager.idl",
@@ -637,6 +639,7 @@
           "//third_party/blink/renderer/modules/scheduler/scheduler.idl",
           "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl",
           "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl",
+          "//third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl",
           "//third_party/blink/renderer/modules/scheduler/task_controller.idl",
           "//third_party/blink/renderer/modules/scheduler/task_controller_init.idl",
           "//third_party/blink/renderer/modules/scheduler/task_priority_change_event.idl",
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 01736c7..5c2f8dc5 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1284,6 +1284,7 @@
     "frame/ad_tracker_test.cc",
     "frame/anchor_element_listener_test.cc",
     "frame/attribution_response_parsing_test.cc",
+    "frame/attribution_src_loader_test.cc",
     "frame/browser_controls_test.cc",
     "frame/child_frame_compositing_helper_test.cc",
     "frame/csp/content_security_policy_test.cc",
diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn
index 61bb910..dae77c3 100644
--- a/third_party/blink/renderer/core/animation/BUILD.gn
+++ b/third_party/blink/renderer/core/animation/BUILD.gn
@@ -56,8 +56,6 @@
     "css/css_animation_data.h",
     "css/css_animation_update.cc",
     "css/css_animation_update.h",
-    "css/css_animation_update_scope.cc",
-    "css/css_animation_update_scope.h",
     "css/css_animations.cc",
     "css/css_animations.h",
     "css/css_keyframe_effect_model.cc",
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index dab0ddb..14f074f2 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -40,7 +40,6 @@
 #include "third_party/blink/renderer/core/animation/compositor_animations.h"
 #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h"
 #include "third_party/blink/renderer/core/animation/css/css_animation.h"
-#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h"
 #include "third_party/blink/renderer/core/animation/css/css_keyframe_effect_model.h"
 #include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h"
 #include "third_party/blink/renderer/core/animation/css/css_transition.h"
@@ -62,6 +61,7 @@
 #include "third_party/blink/renderer/core/css/css_property_equality.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
+#include "third_party/blink/renderer/core/css/post_style_update_scope.h"
 #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
@@ -1495,7 +1495,7 @@
   const ComputedStyle* old_style = animating_element.GetComputedStyle();
 
   if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) {
-    if (auto* data = CSSAnimationUpdateScope::CurrentData())
+    if (auto* data = PostStyleUpdateScope::CurrentAnimationData())
       old_style = data->GetOldStyle(animating_element);
   }
 
diff --git a/third_party/blink/renderer/core/animation/document_animations.h b/third_party/blink/renderer/core/animation/document_animations.h
index 17c0980c..9490a6f 100644
--- a/third_party/blink/renderer/core/animation/document_animations.h
+++ b/third_party/blink/renderer/core/animation/document_animations.h
@@ -103,7 +103,7 @@
   // ApplyPendingElementUpdates.
   //
   // It's invalid to call this function if there is no current
-  // CSSAnimationUpdateScope.
+  // PostStyleUpdateScope.
   void AddElementWithPendingAnimationUpdate(Element&);
 
   // Apply pending updates for any elements previously added during AddElement-
diff --git a/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc b/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc
index 9e844ff..6e9d2441 100644
--- a/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc
+++ b/third_party/blink/renderer/core/css/affected_by_pseudo_test.cc
@@ -2516,4 +2516,135 @@
                 {kSiblingsAffectedByHas, false}});
 }
 
+TEST_F(AffectedByPseudoTest, AffectedBySelectorQuery) {
+  SetHtmlInnerHTML(R"HTML(
+    <div id=div1>
+      <div id=div2 class='a'>
+        <div id=div3></div>
+      </div>
+      <div id=div4 class='e'>
+        <div id=div5>
+          <div id=div6></div>
+        </div>
+      </div>
+      <div id=div7 class='b'>
+        <div id=div8 class='c'>
+          <div id=div9 class='d'>
+            <div id=div10></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  UpdateAllLifecyclePhasesForTest();
+  CheckAffectedByFlagsForHas(
+      "div1", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div2", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div3", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div4", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div5", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div6", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div7", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div8", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div9", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div10", {{kAffectedBySubjectHas, false},
+                {kAffectedByNonSubjectHas, false},
+                {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+                {kSiblingsAffectedByHas, false}});
+
+  StaticElementList* result =
+      GetDocument().QuerySelectorAll(".a:has(~ .b > .c > .d) ~ .e");
+  ASSERT_EQ(1U, result->length());
+  EXPECT_EQ(result->item(0)->GetIdAttribute(), "div4");
+
+  UpdateAllLifecyclePhasesForTest();
+  CheckAffectedByFlagsForHas(
+      "div1", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div2", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div3", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div4", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div5", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div6", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div7", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div8", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div9", {{kAffectedBySubjectHas, false},
+               {kAffectedByNonSubjectHas, false},
+               {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+               {kSiblingsAffectedByHas, false}});
+  CheckAffectedByFlagsForHas(
+      "div10", {{kAffectedBySubjectHas, false},
+                {kAffectedByNonSubjectHas, false},
+                {kAncestorsOrAncestorSiblingsAffectedByHas, false},
+                {kSiblingsAffectedByHas, false}});
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni
index ce29f22..e686570 100644
--- a/third_party/blink/renderer/core/css/build.gni
+++ b/third_party/blink/renderer/core/css/build.gni
@@ -500,6 +500,8 @@
   "part_names.cc",
   "part_names.h",
   "pending_sheet_type.h",
+  "post_style_update_scope.cc",
+  "post_style_update_scope.h",
   "properties/computed_style_utils.cc",
   "properties/computed_style_utils.h",
   "properties/css_bitset.h",
@@ -708,6 +710,7 @@
   "font_size_functions_test.cc",
   "font_update_invalidation_test.cc",
   "force_dark_test.cc",
+  "has_argument_match_context_test.cc",
   "invalidation/invalidation_set_test.cc",
   "invalidation/pending_invalidations_test.cc",
   "invalidation/style_invalidator_test.cc",
diff --git a/third_party/blink/renderer/core/css/container_query_test.cc b/third_party/blink/renderer/core/css/container_query_test.cc
index 276f19d..ed051d7 100644
--- a/third_party/blink/renderer/core/css/container_query_test.cc
+++ b/third_party/blink/renderer/core/css/container_query_test.cc
@@ -5,11 +5,11 @@
 #include "third_party/blink/renderer/core/css/container_query.h"
 
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h"
 #include "third_party/blink/renderer/core/animation/document_animations.h"
 #include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/css/css_container_rule.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
+#include "third_party/blink/renderer/core/css/post_style_update_scope.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -114,12 +114,12 @@
   }
 
   size_t GetOldStylesCount(String html) {
-    // Creating a CSSAnimationUpdateScope prevents old styles from being
-    // cleared until this function completes.
-    CSSAnimationUpdateScope animation_update_scope(GetDocument());
+    // Creating a PostStyleUpdateScope prevents old styles from being cleared
+    // until this function completes.
+    PostStyleUpdateScope post_style_update_scope(GetDocument());
     SetBodyInnerHTML(html);
-    DCHECK(CSSAnimationUpdateScope::CurrentData());
-    return CSSAnimationUpdateScope::CurrentData()->old_styles_.size();
+    DCHECK(PostStyleUpdateScope::CurrentAnimationData());
+    return PostStyleUpdateScope::CurrentAnimationData()->old_styles_.size();
   }
 };
 
@@ -575,7 +575,7 @@
 
   // Simulate a style and layout pass with multiple rounds of style recalc.
   {
-    CSSAnimationUpdateScope animation_update_scope(GetDocument());
+    PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // Should transition between [10px, 20px]. (Intermediate round).
     GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
@@ -596,7 +596,7 @@
     EXPECT_EQ(0u, GetAnimationsCount(target));
   }
 
-  // CSSAnimationUpdateScope going out of scope applies the update.
+  // PostStyleUpdateScope going out of scope applies the update.
   EXPECT_EQ(1u, GetAnimationsCount(target));
 
   // Verify that the newly-updated Animation produces the correct value.
@@ -648,7 +648,7 @@
 
   // Simulate a style and layout pass with multiple rounds of style recalc.
   {
-    CSSAnimationUpdateScope animation_update_scope(GetDocument());
+    PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // No transition property present. (Intermediate round).
     GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
@@ -669,7 +669,7 @@
     EXPECT_EQ(0u, GetAnimationsCount(target));
   }
 
-  // CSSAnimationUpdateScope going out of scope applies the update.
+  // PostStyleUpdateScope going out of scope applies the update.
   EXPECT_EQ(1u, GetAnimationsCount(target));
 
   // Verify that the newly-updated Animation produces the correct value.
@@ -721,7 +721,7 @@
 
   // Simulate a style and layout pass with multiple rounds of style recalc.
   {
-    CSSAnimationUpdateScope animation_update_scope(GetDocument());
+    PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // No transition property present yet. (Intermediate round).
     GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
@@ -742,7 +742,7 @@
     EXPECT_EQ(0u, GetAnimationsCount(target));
   }
 
-  // CSSAnimationUpdateScope going out of scope applies the update.
+  // PostStyleUpdateScope going out of scope applies the update.
   // We ultimately ended up with no transition, hence we should have no
   // Animations on the element.
   EXPECT_EQ(0u, GetAnimationsCount(target));
@@ -794,7 +794,7 @@
 
   // Simulate a style and layout pass with multiple rounds of style recalc.
   {
-    CSSAnimationUpdateScope animation_update_scope(GetDocument());
+    PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // Animation at 20%. (Intermediate round).
     GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
@@ -815,7 +815,7 @@
     EXPECT_EQ(0u, GetAnimationsCount(target));
   }
 
-  // CSSAnimationUpdateScope going out of scope applies the update.
+  // PostStyleUpdateScope going out of scope applies the update.
   EXPECT_EQ(1u, GetAnimationsCount(target));
 
   // Verify that the newly-updated Animation produces the correct value.
@@ -865,7 +865,7 @@
 
   // Simulate a style and layout pass with multiple rounds of style recalc.
   {
-    CSSAnimationUpdateScope animation_update_scope(GetDocument());
+    PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // Animation should appear to be canceled. (Intermediate round).
     GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
@@ -880,7 +880,7 @@
     EXPECT_EQ(1u, GetAnimationsCount(target));
   }
 
-  // CSSAnimationUpdateScope going out of scope applies the update.
+  // PostStyleUpdateScope going out of scope applies the update.
   // (Although since we didn't cancel, there is nothing to update).
   EXPECT_EQ(1u, GetAnimationsCount(target));
 
@@ -893,7 +893,7 @@
 
   // Change width such that container query matches, and cancel the animation
   // for real this time. Note that since we no longer have a
-  // CSSAnimationUpdateScope above us, the CSSAnimationUpdateScope within
+  // PostStyleUpdateScope above us, the PostStyleUpdateScope within
   // UpdateAllLifecyclePhasesForTest will apply the update.
   container->SetInlineStyleProperty(CSSPropertyID::kWidth, "130px");
   UpdateAllLifecyclePhasesForTest();
diff --git a/third_party/blink/renderer/core/css/has_argument_match_context.cc b/third_party/blink/renderer/core/css/has_argument_match_context.cc
index e20afe2..0462245 100644
--- a/third_party/blink/renderer/core/css/has_argument_match_context.cc
+++ b/third_party/blink/renderer/core/css/has_argument_match_context.cc
@@ -64,10 +64,9 @@
           sibling_combinator_between_child_or_descendant_combinator_ = true;
         }
         contains_child_or_descendant_combinator = true;
-        if (DepthFixed()) {
+        if (DepthFixed())
           depth_limit_++;
-          adjacent_distance_limit_ = 0;
-        }
+        adjacent_distance_limit_ = 0;
         break;
 
       case CSSSelector::kRelativeDirectAdjacent:
@@ -98,6 +97,7 @@
         return;
     }
   }
+  DCHECK_NE(leftmost_relation_, CSSSelector::kSubSelector);
 }
 
 HasArgumentSubtreeIterator::HasArgumentSubtreeIterator(
diff --git a/third_party/blink/renderer/core/css/has_argument_match_context.h b/third_party/blink/renderer/core/css/has_argument_match_context.h
index 5ec9e41..fe9ca585 100644
--- a/third_party/blink/renderer/core/css/has_argument_match_context.h
+++ b/third_party/blink/renderer/core/css/has_argument_match_context.h
@@ -10,7 +10,7 @@
 
 namespace blink {
 
-class HasArgumentMatchContext {
+class CORE_EXPORT HasArgumentMatchContext {
   STACK_ALLOCATED();
 
  public:
@@ -49,13 +49,13 @@
   // Case 1:  (kDescendant, 0, max)
   //   - Argument selector conditions
   //     - Starts with descendant combinator.
-  //   - E.g. ':has(.a)', ':has(.a)', ':has(.a ~ .b > .c)'
+  //   - E.g. ':has(.a)', ':has(.a ~ .b)', ':has(.a ~ .b > .c)'
   //   - Traverse all descendants of the :has scope element.
   // Case 2:  (kChild, 0, max)
   //   - Argument selector conditions
   //     - Starts with child combinator.
   //     - At least one descendant combinator.
-  //   - E.g. ':has(> .a .b)', ':has(> .a ~ .b .c)'
+  //   - E.g. ':has(> .a .b)', ':has(> .a ~ .b .c)', ':has(> .a + .b .c)'
   //   - Traverse all descendants of the :has scope element.
   // Case 3:  (kChild, 0, n)
   //   - Argument selector conditions
@@ -95,7 +95,7 @@
   //     - At least one subsequent-sibling combinator to the left of every
   //       descendant or child combinator.
   //     - At least 1 descendant combinator.
-  //   - E.g. ':has(+ .a ~ .b .c)', ':has(+ .a ~ .b > .c + .e .f)'
+  //   - E.g. ':has(+ .a ~ .b .c)', ':has(+ .a ~ .b > .c + .d .e)'
   //   - Traverse all the subsequent sibling subtrees of the :has scope element.
   //     (all subsequent siblings and it's descendants)
   // Case 8:  (kDirectAdjacent, max, 0)
@@ -114,7 +114,7 @@
   //     - No descendant combinator.
   //   - E.g.
   //     - ':has(+ .a ~ .b > .c)'            : (kDirectAdjacent, max, 1)
-  //     - ':has(+ .a ~ .b > .c + .e >.f)'   : (kDirectAdjacent, max, 2)
+  //     - ':has(+ .a ~ .b > .c + .d >.e)'   : (kDirectAdjacent, max, 2)
   //   - Traverse depth n elements of all subsequent sibling subtree of the
   //     :has scope element.
   // Case 10:  (kDirectAdjacent, n, max)
@@ -155,7 +155,7 @@
   //   - Traverse the depth m elements of the distance n sibling subtree of
   //     the :has scope element. (elements at depth m of the descendant subtree
   //     of the sibling element at distance n)
-  CSSSelector::RelationType leftmost_relation_;
+  CSSSelector::RelationType leftmost_relation_{CSSSelector::kSubSelector};
   int adjacent_distance_limit_;
   int depth_limit_;
 
diff --git a/third_party/blink/renderer/core/css/has_argument_match_context_test.cc b/third_party/blink/renderer/core/css/has_argument_match_context_test.cc
new file mode 100644
index 0000000..7476cd8
--- /dev/null
+++ b/third_party/blink/renderer/core/css/has_argument_match_context_test.cc
@@ -0,0 +1,124 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/has_argument_match_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_test_helpers.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+
+namespace blink {
+
+namespace {
+
+const int kMax = std::numeric_limits<int>::max();
+
+void RunTest(const String& selector_text,
+             CSSSelector::RelationType expected_leftmost_relation,
+             int expected_adjacent_distance_limit,
+             int expected_depth_limit) {
+  CSSSelectorList selector_list =
+      css_test_helpers::ParseSelectorList(selector_text);
+  HasArgumentMatchContext context(
+      selector_list.First()->SelectorList()->First());
+
+  EXPECT_EQ(expected_leftmost_relation, context.LeftmostRelation())
+      << "Failed : " << selector_text;
+  EXPECT_EQ(expected_adjacent_distance_limit, context.AdjacentDistanceLimit())
+      << "Failed : " << selector_text;
+  EXPECT_EQ(expected_depth_limit, context.DepthLimit())
+      << "Failed : " << selector_text;
+}
+
+}  // namespace
+
+TEST(HasArgumentMatchContextTest, TestArgumentMatchContext) {
+  RunTest(":has(.a)", CSSSelector::kRelativeDescendant,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(.a ~ .b)", CSSSelector::kRelativeDescendant,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(.a ~ .b > .c)", CSSSelector::kRelativeDescendant,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(> .a .b)", CSSSelector::kRelativeChild,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(> .a ~ .b .c)", CSSSelector::kRelativeChild,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(> .a + .b .c)", CSSSelector::kRelativeChild,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(> .a)", CSSSelector::kRelativeChild,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ 1);
+  RunTest(":has(> .a ~ .b > .c)", CSSSelector::kRelativeChild,
+          /* expected_adjacent_distance_limit */ 0,
+          /* expected_depth_limit */ 2);
+  RunTest(":has(~ .a .b)", CSSSelector::kRelativeIndirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(~ .a + .b > .c ~ .d .e)",
+          CSSSelector::kRelativeIndirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(~ .a)", CSSSelector::kRelativeIndirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 0);
+  RunTest(":has(~ .a + .b ~ .c)", CSSSelector::kRelativeIndirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 0);
+  RunTest(":has(~ .a > .b)", CSSSelector::kRelativeIndirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 1);
+  RunTest(":has(~ .a + .b > .c ~ .d > .e)",
+          CSSSelector::kRelativeIndirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 2);
+  RunTest(":has(+ .a ~ .b .c)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(+ .a ~ .b > .c + .d .e)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(+ .a ~ .b)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 0);
+  RunTest(":has(+ .a + .b ~ .c)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 0);
+  RunTest(":has(+ .a ~ .b > .c)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 1);
+  RunTest(":has(+ .a ~ .b > .c + .d > .e)",
+          CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ kMax,
+          /* expected_depth_limit */ 2);
+  RunTest(":has(+ .a .b)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ 1,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(+ .a > .b + .c .d)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ 1,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(+ .a + .b > .c .d)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ 2,
+          /* expected_depth_limit */ kMax);
+  RunTest(":has(+ .a)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ 1,
+          /* expected_depth_limit */ 0);
+  RunTest(":has(+ .a + .b + .c)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ 3,
+          /* expected_depth_limit */ 0);
+  RunTest(":has(+ .a > .b)", CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ 1,
+          /* expected_depth_limit */ 1);
+  RunTest(":has(+ .a + .b > .c ~ .d > .e)",
+          CSSSelector::kRelativeDirectAdjacent,
+          /* expected_adjacent_distance_limit */ 2,
+          /* expected_depth_limit */ 2);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.cc b/third_party/blink/renderer/core/css/post_style_update_scope.cc
similarity index 70%
rename from third_party/blink/renderer/core/animation/css/css_animation_update_scope.cc
rename to third_party/blink/renderer/core/css/post_style_update_scope.cc
index af6e843..5a437826 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.cc
+++ b/third_party/blink/renderer/core/css/post_style_update_scope.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h"
+#include "third_party/blink/renderer/core/css/post_style_update_scope.h"
 
 #include "third_party/blink/renderer/core/animation/css/css_animations.h"
 #include "third_party/blink/renderer/core/animation/document_animations.h"
@@ -15,34 +15,36 @@
 
 namespace blink {
 
-CSSAnimationUpdateScope* CSSAnimationUpdateScope::current_ = nullptr;
+PostStyleUpdateScope* PostStyleUpdateScope::current_ = nullptr;
 
-CSSAnimationUpdateScope::Data* CSSAnimationUpdateScope::CurrentData() {
+PostStyleUpdateScope::AnimationData*
+PostStyleUpdateScope::CurrentAnimationData() {
   if (!RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled())
     return nullptr;
-  return current_ ? &current_->data_ : nullptr;
+  return current_ ? &current_->animation_data_ : nullptr;
 }
 
-CSSAnimationUpdateScope::CSSAnimationUpdateScope(Document& document)
+PostStyleUpdateScope::PostStyleUpdateScope(Document& document)
     : document_(document) {
   if (!current_)
     current_ = this;
 }
 
-CSSAnimationUpdateScope::~CSSAnimationUpdateScope() {
+PostStyleUpdateScope::~PostStyleUpdateScope() {
   if (current_ == this) {
     if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled())
       Apply();
+    document_.ClearFocusedElementIfNeeded();
     current_ = nullptr;
   }
 }
 
-void CSSAnimationUpdateScope::Apply() {
+void PostStyleUpdateScope::Apply() {
   StyleEngine::InApplyAnimationUpdateScope in_apply_animation_update_scope(
       document_.GetStyleEngine());
 
   HeapHashSet<Member<Element>> pending;
-  std::swap(pending, data_.elements_with_pending_updates_);
+  std::swap(pending, animation_data_.elements_with_pending_updates_);
 
   for (auto& element : pending) {
     ElementAnimations* element_animations = element->GetElementAnimations();
@@ -51,23 +53,24 @@
     element_animations->CssAnimations().MaybeApplyPendingUpdate(element.Get());
   }
 
-  DCHECK(data_.elements_with_pending_updates_.IsEmpty())
+  DCHECK(animation_data_.elements_with_pending_updates_.IsEmpty())
       << "MaybeApplyPendingUpdate must not set further pending updates";
 }
 
-void CSSAnimationUpdateScope::Data::SetPendingUpdate(
+void PostStyleUpdateScope::AnimationData::SetPendingUpdate(
     Element& element,
     const CSSAnimationUpdate& update) {
   element.EnsureElementAnimations().CssAnimations().SetPendingUpdate(update);
   elements_with_pending_updates_.insert(&element);
 }
 
-void CSSAnimationUpdateScope::Data::StoreOldStyleIfNeeded(Element& element) {
+void PostStyleUpdateScope::AnimationData::StoreOldStyleIfNeeded(
+    Element& element) {
   old_styles_.insert(
       &element, scoped_refptr<const ComputedStyle>(element.GetComputedStyle()));
 }
 
-const ComputedStyle* CSSAnimationUpdateScope::Data::GetOldStyle(
+const ComputedStyle* PostStyleUpdateScope::AnimationData::GetOldStyle(
     Element& element) const {
   auto iter = old_styles_.find(&element);
   if (iter == old_styles_.end())
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.h b/third_party/blink/renderer/core/css/post_style_update_scope.h
similarity index 68%
rename from third_party/blink/renderer/core/animation/css/css_animation_update_scope.h
rename to third_party/blink/renderer/core/css/post_style_update_scope.h
index 557a4154..b088640 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_update_scope.h
+++ b/third_party/blink/renderer/core/css/post_style_update_scope.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_ANIMATION_UPDATE_SCOPE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_ANIMATION_UPDATE_SCOPE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_POST_STYLE_UPDATE_SCOPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_POST_STYLE_UPDATE_SCOPE_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
@@ -18,24 +18,25 @@
 class ComputedStyle;
 class CSSAnimationUpdate;
 
-// CSSAnimationUpdateScope applies pending animation on destruction, if it
-// is the *current* scope. A CSSAnimationUpdateScope becomes the current
-// scope upon construction if there isn't one already.
-class CORE_EXPORT CSSAnimationUpdateScope {
+// PostStyleUpdateScope applies pending animation, and initiates clearing of the
+// focused element, on destruction, if it is the *current* scope. A
+// PostStyleUpdateScope becomes the current scope upon construction if there
+// isn't one already.
+class CORE_EXPORT PostStyleUpdateScope {
   STACK_ALLOCATED();
 
  public:
-  explicit CSSAnimationUpdateScope(Document&);
-  ~CSSAnimationUpdateScope();
+  explicit PostStyleUpdateScope(Document&);
+  ~PostStyleUpdateScope();
 
-  class Data {
+  class AnimationData {
     STACK_ALLOCATED();
 
    public:
     // Set a pending CSSAnimationUpdate for a given Element.
     //
     // The update will be automatically applied when the owning
-    // CSSAnimationUpdateScope object goes out of scope.
+    // PostStyleUpdateScope object goes out of scope.
     void SetPendingUpdate(Element&, const CSSAnimationUpdate&);
 
     // When calculating transition updates, we need the old style of the element
@@ -49,8 +50,8 @@
     // it as the old style. If an old style was already stored for this Element,
     // this function does nothing.
     //
-    // The old styles remain until the CSSAnimationUpdateScope/Data object goes
-    // out of scope.
+    // The old styles remain until the PostStyleUpdateScope object goes out of
+    // scope.
     void StoreOldStyleIfNeeded(Element&);
 
     // If an old-style was previously stored using StoreOldStyleIfNeeded,
@@ -59,7 +60,7 @@
     const ComputedStyle* GetOldStyle(Element&) const;
 
    private:
-    friend class CSSAnimationUpdateScope;
+    friend class PostStyleUpdateScope;
     friend class ContainerQueryTest;
 
     HeapHashSet<Member<Element>> elements_with_pending_updates_;
@@ -67,19 +68,19 @@
         old_styles_;
   };
 
-  static Data* CurrentData();
+  static AnimationData* CurrentAnimationData();
 
  private:
   Document& document_;
-  // Note that |data_| is only used if the CSSAnimationUpdateScope is the
+  // Note that |animation_data_| is only used if the PostStyleUpdateScope is the
   // current scope. Otherwise it will remain empty.
-  Data data_;
+  AnimationData animation_data_;
 
   void Apply();
 
-  static CSSAnimationUpdateScope* current_;
+  static PostStyleUpdateScope* current_;
 };
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_ANIMATION_UPDATE_SCOPE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_POST_STYLE_UPDATE_SCOPE_H_
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 7ca965f6..656807dd 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -32,7 +32,6 @@
 
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h"
-#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h"
 #include "third_party/blink/renderer/core/animation/css/css_animations.h"
 #include "third_party/blink/renderer/core/animation/document_animations.h"
 #include "third_party/blink/renderer/core/animation/element_animations.h"
@@ -55,6 +54,7 @@
 #include "third_party/blink/renderer/core/css/font_face.h"
 #include "third_party/blink/renderer/core/css/page_rule_collector.h"
 #include "third_party/blink/renderer/core/css/part_names.h"
+#include "third_party/blink/renderer/core/css/post_style_update_scope.h"
 #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
@@ -148,7 +148,7 @@
                                 StyleResolverState& state,
                                 Element& element) {
   if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) {
-    if (auto* data = CSSAnimationUpdateScope::CurrentData()) {
+    if (auto* data = PostStyleUpdateScope::CurrentAnimationData()) {
       if (ShouldStoreOldStyle(style_recalc_context, state))
         data->StoreOldStyleIfNeeded(element);
     }
@@ -161,7 +161,7 @@
     return;
 
   if (RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled()) {
-    if (auto* data = CSSAnimationUpdateScope::CurrentData())
+    if (auto* data = PostStyleUpdateScope::CurrentAnimationData())
       data->SetPendingUpdate(element, state.AnimationUpdate());
   } else {
     element.EnsureElementAnimations().CssAnimations().SetPendingUpdate(
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index 3f28d54..ad7fe57c 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -748,33 +748,36 @@
       auto cache_result =
           map.insert(has_scope_element, false);  // Mark as checked
       if (!cache_result.is_new_entry) {        // Was already marked as checked
-
-        // The SiblingsAffectedByHas flag is set only when an element is a
-        // sibling of the :has() scope element during the subselector matching.
-        // But during the matching, the matching status of some elements are
-        // cached and those element cannot be a sibling of the :has() scope
-        // element in case that the subselector starts with adjacent combinator.
-        // 1. MatchSelector() gives some possibly matched elements, and those
-        //    are cached as matched.
-        // 2. If an element is not matched, then it is cached as not matched.
-        // We need to set missing SiblingAffectedByHas flags for the cached
-        // elements before returning early.
-        switch (leftmost_relation) {
-          case CSSSelector::kRelativeDirectAdjacent:
-            if (Element* next_sibling =
-                    ElementTraversal::NextSibling(*has_scope_element))
-              next_sibling->SetSiblingsAffectedByHas();
-            break;
-          case CSSSelector::kRelativeIndirectAdjacent:
-            for (Element* next_sibling =
-                     ElementTraversal::NextSibling(*has_scope_element);
-                 next_sibling && !next_sibling->SiblingsAffectedByHas();
-                 next_sibling = ElementTraversal::NextSibling(*next_sibling)) {
-              next_sibling->SetSiblingsAffectedByHas();
-            }
-            break;
-          default:
-            break;
+        if (mode_ == kResolvingStyle) {
+          // The SiblingsAffectedByHas flag is set only when an element is a
+          // sibling of the :has() scope element during the subselector
+          // matching. But during the matching, the matching status of some
+          // elements are cached and those element cannot be a sibling of the
+          // :has() scope element in case that the subselector starts with
+          // adjacent combinator.
+          // 1. MatchSelector() gives some possibly matched elements, and those
+          //    are cached as matched.
+          // 2. If an element is not matched, then it is cached as not matched.
+          // We need to set missing SiblingAffectedByHas flags for the cached
+          // elements before returning early.
+          switch (leftmost_relation) {
+            case CSSSelector::kRelativeDirectAdjacent:
+              if (Element* next_sibling =
+                      ElementTraversal::NextSibling(*has_scope_element))
+                next_sibling->SetSiblingsAffectedByHas();
+              break;
+            case CSSSelector::kRelativeIndirectAdjacent:
+              for (Element* next_sibling =
+                       ElementTraversal::NextSibling(*has_scope_element);
+                   next_sibling && !next_sibling->SiblingsAffectedByHas();
+                   next_sibling =
+                       ElementTraversal::NextSibling(*next_sibling)) {
+                next_sibling->SetSiblingsAffectedByHas();
+              }
+              break;
+            default:
+              break;
+          }
         }
 
         if (cache_result.stored_value->value)  // Was already marked as matched
@@ -784,64 +787,13 @@
     }
 
     sub_context.selector = selector;
-
-    bool depth_fixed = has_argument_match_context.DepthFixed();
-
-    // To prevent incorrect 'NotChecked' status while matching ':has' pseudo
-    // class, change the argument matching context scope when the ':has'
-    // argument matching traversal cannot be fixed with a certain depth and
-    // adjacent distance.
-    //
-    // For example, When we tries to match '.a:has(.b .c) .d' on below DOM,
-    // <div id=d1 class="a">
-    //  <div id=d2 class="b">
-    //   <div id=d3 class="a">
-    //    <div id=d4 class="c">
-    //      <div id=d5 class="d"></div>
-    //    </div>
-    //   </div>
-    //  </div>
-    // </div>
-    // the ':has(.b .c)' selector will be checked on the #d3 element first
-    // because the selector '.a:has(.b .c) .d' will be matched upward from
-    // the #d5 element.
-    //  1) '.d' will be matched first on #d5
-    //  2) move to the #d3 until the '.a' matched
-    //  3) match the ':has(.b .c)' on the #d3
-    //    3.1) match the argument selector '.b .c' on the descendants of #d3
-    //  4) move to the #d1 until the '.a' matched
-    //  5) match the ':has(.b .c)' on the #d1
-    //    5.1) match the argument selector '.b .c' on the descendants of #d1
-    //
-    // The argument selector '.b .c' will not be matched on the #d4 at this
-    // step if the argument matching scope is limited to #d3. But the '.b .c'
-    // can be matched on the #d4 if the argument matching scope is #d1.
-    // To prevent duplicated argument matching operation, the #d1 should be
-    // marked as 'Matched' at the step 3.
-    //
-    // TODO(blee@igalia.com) Need to clarify the :scope dependency in relative
-    // selector definition.
-    // - spec : https://www.w3.org/TR/selectors-4/#relative
-    // - csswg issue : https://github.com/w3c/csswg-drafts/issues/6399
-    if (!depth_fixed) {
-      sub_context.relative_leftmost_element =
-          &has_scope_element->GetTreeScope().RootNode();
-    } else if (has_argument_match_context.AdjacentDistanceFixed()) {
-      if (ContainerNode* parent_node = has_scope_element->parentNode()) {
-        sub_context.relative_leftmost_element =
-            Traversal<Element>::FirstChild(*parent_node);
-      } else {
-        sub_context.relative_leftmost_element = has_scope_element;
-      }
-    } else {
-      sub_context.relative_leftmost_element = has_scope_element;
-    }
+    sub_context.relative_leftmost_element = has_scope_element;
 
     bool selector_matched = false;
     for (HasArgumentSubtreeIterator iterator(*has_scope_element,
                                              has_argument_match_context);
          !iterator.AtEnd(); ++iterator) {
-      if (depth_fixed && !iterator.AtFixedDepth()) {
+      if (has_argument_match_context.DepthFixed() && !iterator.AtFixedDepth()) {
         // We can skip subselector matching on some elements at a certain depth
         // when the subselector doesn't have descendant combinator. (e.g. For
         // the style rule '.a:has(> .b > .c) {}', we don't need to match the
@@ -851,7 +803,7 @@
         // flags of the skipped elements will not set.
         // To prevent this, marks the flags of skipped elements if those are
         // under the depth limit.
-        if (iterator.UnderDepthLimit()) {
+        if (mode_ == kResolvingStyle && iterator.UnderDepthLimit()) {
           SetAffectedByHasFlag(iterator.CurrentElement(),
                                iterator.AtSiblingOfHasScope());
         }
@@ -896,36 +848,45 @@
       }
 
       if (selector_matched) {
-        // Need to walk up to set 'AncestorsOrAncestorSiblingsAffectedByHas'
-        // or 'SiblingsAffectedByHas' flag so that the StyleEngine can walk up
-        // to find the elements affected by subject or non-subject :has().
-        //
-        // StyleEngine tries to find elements affected by :has() by walking up
-        // siblings or ancestors a mutated element only when an element has the
-        // flags set. If an element doesn't have those flags set, then the
-        // StyleEngine will stop the upward tree walk at the element.
-        //
-        // HasArgumentSubtreeIterator traverses the sub-tree in the reversed
-        // DOM tree walk order for preventing O(n^2) matching problem of
-        // multiple elements affected by :has(). Due to this traversal order,
-        // this early returning can break the upward tree walk.
-        //
-        // To prevent the problem, walks up until reach to the scope element
-        // and marks elements as 'AncestorsOrAncestorSiblingsAffectedByHas' or
-        // 'SiblingsAffectedByHas' before returning.
-        //
-        // Similar to the DynamicRestyleFlags in the ContainerNode, these flags
-        // will never be reset.
-        for (AffectedByHasIterator affected_by_has_iterator(iterator);
-             !affected_by_has_iterator.AtEnd(); ++affected_by_has_iterator) {
-          SetAffectedByHasFlag(affected_by_has_iterator.CurrentElement(),
-                               affected_by_has_iterator.AtSiblingOfHasScope());
+        if (mode_ == kResolvingStyle) {
+          // Need to traverse to ancestors or siblings to set the affected-by-
+          // has flags ('AncestorsOrAncestorSiblingsAffectedByHas' or
+          // 'SiblingsAffectedByHas') so that the StyleEngine can traverse to
+          // ancestors or siblings to find the elements affected by subject or
+          // non-subject :has().
+          //
+          // StyleEngine tries to find elements affected by :has() by traversing
+          // siblings or ancestors of a mutated element only when an element has
+          // the flags set. If an element doesn't have those flags set, then the
+          // StyleEngine will stop the traversal at the element.
+          //
+          // HasArgumentSubtreeIterator traverses the subtree in the reversed
+          // DOM tree walk order to prevent duplicated subtree traversal caused
+          // by the multiple elements affected by :has(). Due to this traversal
+          // order, this early returning can break the traversal to ancestors or
+          // siblings.
+          //
+          // To prevent the problem, traverse to ancestors or siblings until
+          // reach to the scope element and marks elements as
+          // 'AncestorsOrAncestorSiblingsAffectedByHas' or
+          // 'SiblingsAffectedByHas' before returning.
+          //
+          // Similar to the DynamicRestyleFlags in the ContainerNode, these
+          // flags will never be reset.
+          for (AffectedByHasIterator affected_by_has_iterator(iterator);
+               !affected_by_has_iterator.AtEnd(); ++affected_by_has_iterator) {
+            SetAffectedByHasFlag(
+                affected_by_has_iterator.CurrentElement(),
+                affected_by_has_iterator.AtSiblingOfHasScope());
+          }
         }
         return true;
       }
 
-      SetAffectedByHasFlag(iterator.CurrentElement(),
-                           iterator.AtSiblingOfHasScope());
+      if (mode_ == kResolvingStyle) {
+        SetAffectedByHasFlag(iterator.CurrentElement(),
+                             iterator.AtSiblingOfHasScope());
+      }
     }
   }
   return false;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 0db584e5..9ea03f4 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -84,7 +84,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
 #include "third_party/blink/renderer/core/accessibility/ax_context.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
-#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h"
 #include "third_party/blink/renderer/core/animation/document_animations.h"
 #include "third_party/blink/renderer/core/animation/document_timeline.h"
 #include "third_party/blink/renderer/core/animation/pending_animations.h"
@@ -102,6 +101,7 @@
 #include "third_party/blink/renderer/core/css/media_query_matcher.h"
 #include "third_party/blink/renderer/core/css/media_values.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
+#include "third_party/blink/renderer/core/css/post_style_update_scope.h"
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 #include "third_party/blink/renderer/core/css/property_registry.h"
 #include "third_party/blink/renderer/core/css/resolver/font_builder.h"
@@ -1003,15 +1003,18 @@
     if (!element)
       element = MakeGarbageCollected<SVGUnknownElement>(qname, *this);
     saw_elements_in_known_namespaces_ = true;
-  } else if (RuntimeEnabledFeatures::MathMLCoreEnabled() &&
-             qname.NamespaceURI() == mathml_names::kNamespaceURI) {
-    element = MathMLElementFactory::Create(qname.LocalName(), *this, flags);
-    // An unknown MathML element is treated like an <mrow> element.
-    // TODO(crbug.com/1021837): Determine if we need to introduce a
-    // MathMLUnknownElement IDL.
-    if (!element)
-      element = MakeGarbageCollected<MathMLRowElement>(qname, *this);
-    saw_elements_in_known_namespaces_ = true;
+  } else if (qname.NamespaceURI() == mathml_names::kNamespaceURI) {
+    if (RuntimeEnabledFeatures::MathMLCoreEnabled()) {
+      element = MathMLElementFactory::Create(qname.LocalName(), *this, flags);
+      // An unknown MathML element is treated like an <mrow> element.
+      // TODO(crbug.com/1021837): Determine if we need to introduce a
+      // MathMLUnknownElement IDL.
+      if (!element)
+        element = MakeGarbageCollected<MathMLRowElement>(qname, *this);
+      saw_elements_in_known_namespaces_ = true;
+    } else {
+      element = MakeGarbageCollected<MathMLElement>(qname, *this);
+    }
   } else {
     element = MakeGarbageCollected<Element>(qname, this);
   }
@@ -2029,7 +2032,7 @@
     owner->GetDocument().UpdateStyleAndLayoutTree(parent_upgrade);
   }
 
-  CSSAnimationUpdateScope animation_update_scope(*this);
+  PostStyleUpdateScope post_style_update_scope(*this);
 
   // This call has to happen even if UpdateStyleAndLayout below will be called.
   // This is because the subsequent call to ShouldUpgrade may depend on the
@@ -2142,21 +2145,6 @@
     View()->MarkOrthogonalWritingModeRootsForLayout();
   }
 
-  // TODO(crbug.com/1298921): If style is layout-dependent, we have to
-  // delay this until after layout.
-  if (focused_element_) {
-    bool focusable = false;
-    if (RuntimeEnabledFeatures::CSSContainerQueriesEnabled() &&
-        GetStyleEngine().StyleMayRequireLayout()) {
-      const ComputedStyle* style = focused_element_->GetComputedStyle();
-      focusable = style && style->IsFocusable();
-    } else {
-      focusable = focused_element_->IsFocusable();
-    }
-    if (!focusable)
-      ClearFocusedElementSoon();
-  }
-
   GetLayoutView()->ClearHitTestCache();
 
   DCHECK(!document_animations_->NeedsAnimationTimingUpdate());
@@ -2585,9 +2573,11 @@
                                                                  GetFrame());
 }
 
-void Document::ClearFocusedElementSoon() {
-  if (!clear_focused_element_timer_.IsActive())
+void Document::ClearFocusedElementIfNeeded() {
+  if (!clear_focused_element_timer_.IsActive() && focused_element_ &&
+      !focused_element_->IsFocusable()) {
     clear_focused_element_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
+  }
 }
 
 void Document::ClearFocusedElementTimerFired(TimerBase*) {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 45e3053..c9718f6 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -949,6 +949,7 @@
   bool SetFocusedElement(Element*, const FocusParams&);
   void ClearFocusedElement();
   Element* FocusedElement() const { return focused_element_.Get(); }
+  void ClearFocusedElementIfNeeded();
   UserActionElementSet& UserActionElements() { return user_action_elements_; }
   const UserActionElementSet& UserActionElements() const {
     return user_action_elements_;
@@ -1971,7 +1972,6 @@
 
   void DidAssociateFormControlsTimerFired(TimerBase*);
 
-  void ClearFocusedElementSoon();
   void ClearFocusedElementTimerFired(TimerBase*);
 
   bool HaveScriptBlockingStylesheetsLoaded() const;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 87de615..ca8d042ba 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2886,7 +2886,7 @@
   }
 
   if (ElementAnimations* element_animations = GetElementAnimations()) {
-    // See also CSSAnimationUpdateScope.
+    // See also PostStyleUpdateScope.
     if (!RuntimeEnabledFeatures::CSSDelayedAnimationUpdatesEnabled())
       element_animations->CssAnimations().MaybeApplyPendingUpdate(this);
   }
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index eab967e..a32ab68 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -1696,22 +1696,6 @@
   }
 }
 
-// StyledElements allow inline style (style="border: 1px"), presentational
-// attributes (ex. color), class names (ex. class="foo bar") and other non-basic
-// styling features. They also control if this element can participate in style
-// sharing.
-//
-// FIXME: The only things that ever go through StyleResolver that aren't
-// StyledElements are PseudoElements and VTTElements. It's possible we can just
-// eliminate all the checks since those elements will never have class names,
-// inline style, or other things that this apparently guards against.
-bool Node::IsStyledElement() const {
-  auto* this_element = DynamicTo<Element>(this);
-  return IsHTMLElement() || IsSVGElement() || IsMathMLElement() ||
-         (!RuntimeEnabledFeatures::MathMLCoreEnabled() && this_element &&
-          this_element->namespaceURI() == mathml_names::kNamespaceURI);
-}
-
 bool Node::IsActiveSlot() const {
   return ToHTMLSlotElementIfSupportsAssignmentOrNull(*this);
 }
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index f961b121..eb26a46 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -364,7 +364,20 @@
   // either a MediaControlElement or MediaControls.
   bool HasMediaControlAncestor() const;
 
-  bool IsStyledElement() const;
+  // StyledElements allow inline style (style="border: 1px"), presentational
+  // attributes (ex. color), class names (ex. class="foo bar") and other
+  // non-basic styling features. They also control if this element can
+  // participate in style sharing.
+  //
+  // TODO(crbug.com/1305488): The only things that ever go through StyleResolver
+  // that aren't StyledElements are PseudoElements and VTTElements. It's
+  // possible we can just remove this function entirely, and replace it at the
+  // callsites with a DCHECK, since those elements will never have class
+  // names, inline style, or other things that this apparently guards
+  // against.
+  bool IsStyledElement() const {
+    return IsHTMLElement() || IsSVGElement() || IsMathMLElement();
+  }
 
   bool IsDocumentNode() const;
   bool IsTreeScope() const;
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
index 45d45e2..7327187 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -774,12 +774,14 @@
             text_node_to_trim, start_offset,
             downstream_end_.ComputeOffsetInContainerNode() - start_offset);
       } else {
+        RelocatablePosition relocatable_downstream_end(downstream_end_);
         RemoveChildrenInRange(start_node, start_offset,
                               downstream_end_.ComputeEditingOffset(),
                               editing_state);
         if (editing_state->IsAborted())
           return;
         ending_position_ = upstream_start_;
+        downstream_end_ = relocatable_downstream_end.GetPosition();
       }
       // We should update layout to associate |start_node| to layout object.
       GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc
index 1bec958..7ea14ed2 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc
@@ -149,4 +149,19 @@
   EXPECT_EQ("|x", GetSelectionTextFromBody());
 }
 
+// This is a regression test for https://crbug.com/1307391
+TEST_F(DeleteSelectionCommandTest, FloatingInputsWithTrailingSpace) {
+  GetDocument().setDesignMode("on");
+  InsertStyleElement("input { float: left; }");
+  Selection().SetSelection(SetSelectionTextToBody("<input>^<input><input>| "),
+                           SetSelectionOptions());
+
+  DeleteSelectionCommand& command =
+      *MakeGarbageCollected<DeleteSelectionCommand>(
+          GetDocument(), DeleteSelectionOptions::NormalDelete());
+  // Should not crash.
+  EXPECT_TRUE(command.Apply());
+  EXPECT_EQ("<input>| ", GetSelectionTextFromBody());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 21495e2..0373942 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -46,6 +47,15 @@
 
 namespace {
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class AttributionSrcRequestStatus {
+  kRequested = 0,
+  kReceived = 1,
+  kFailed = 2,
+  kMaxValue = kFailed,
+};
+
 bool ContainsTriggerHeaders(const HTTPHeaderMap& headers) {
   return headers.Contains(
              http_names::kAttributionReportingRegisterEventTrigger) ||
@@ -56,6 +66,11 @@
               http_names::kAttributionReportingRegisterAggregatableValues));
 }
 
+void RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus status) {
+  base::UmaHistogramEnumeration("Conversions.AttributionSrcRequestStatus",
+                                status);
+}
+
 }  // namespace
 
 class AttributionSrcLoader::ResourceClient
@@ -243,6 +258,9 @@
       this, src_type, associated_with_navigation);
   resource_clients_.insert(client);
   RawResource::Fetch(params, local_frame_->DomWindow()->Fetcher(), client);
+
+  RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus::kRequested);
+
   return client;
 }
 
@@ -374,6 +392,12 @@
 
   DCHECK(loader_->resource_clients_.Contains(this));
   loader_->resource_clients_.erase(this);
+
+  if (resource->ErrorOccurred()) {
+    RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus::kFailed);
+  } else {
+    RecordAttributionSrcRequestStatus(AttributionSrcRequestStatus::kReceived);
+  }
 }
 
 void AttributionSrcLoader::ResourceClient::HandleResponseHeaders(
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc b/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc
new file mode 100644
index 0000000..57de7413
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader_test.cc
@@ -0,0 +1,82 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/frame/attribution_src_loader.h"
+
+#include <memory>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/execution_context/security_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+namespace {
+
+using blink::url_test_helpers::RegisterMockedErrorURLLoad;
+using blink::url_test_helpers::RegisterMockedURLLoad;
+using blink::url_test_helpers::ToKURL;
+
+}  // namespace
+
+class AttributionSrcLoaderTest : public testing::Test {
+ public:
+  void SetUp() override {
+    dummy_page_holder_ = std::make_unique<DummyPageHolder>();
+
+    SecurityContext& security_context = dummy_page_holder_->GetDocument()
+                                            .GetFrame()
+                                            ->DomWindow()
+                                            ->GetSecurityContext();
+    security_context.SetSecurityOriginForTesting(nullptr);
+    security_context.SetSecurityOrigin(
+        SecurityOrigin::CreateFromString("https://example.com"));
+
+    attribution_src_loader_ = MakeGarbageCollected<AttributionSrcLoader>(
+        dummy_page_holder_->GetDocument().GetFrame());
+  }
+
+ protected:
+  Persistent<AttributionSrcLoader> attribution_src_loader_;
+  std::unique_ptr<DummyPageHolder> dummy_page_holder_;
+};
+
+TEST_F(AttributionSrcLoaderTest, AttributionSrcRequestStatusHistogram) {
+  base::HistogramTester histograms;
+
+  KURL url1 = ToKURL("https://example1.com/foo.html");
+  RegisterMockedURLLoad(url1, test::CoreTestDataPath("foo.html"));
+
+  attribution_src_loader_->Register(url1, /*element=*/nullptr);
+
+  KURL url2 = ToKURL("https://example2.com/foo.html");
+  RegisterMockedErrorURLLoad(url2);
+
+  attribution_src_loader_->Register(url2, /*element=*/nullptr);
+
+  // kRequested = 0.
+  histograms.ExpectUniqueSample("Conversions.AttributionSrcRequestStatus", 0,
+                                2);
+
+  url_test_helpers::ServeAsynchronousRequests();
+
+  // kReceived = 1.
+  histograms.ExpectBucketCount("Conversions.AttributionSrcRequestStatus", 1, 1);
+
+  // kFailed = 2.
+  histograms.ExpectBucketCount("Conversions.AttributionSrcRequestStatus", 2, 1);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
index 184a68a..81990b5 100644
--- a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
@@ -557,27 +557,13 @@
           "RTCPeerConnectionSdpSemanticsPlanB", kM93,
           "Plan B SDP semantics, which is used when constructing an "
           "RTCPeerConnection with {sdpSemantics:\"plan-b\"}, is a legacy "
-          "version of the Session Description Protocol that has severe "
-          "compatibility issues on modern browsers. The standardized SDP "
-          "format, \"unified-plan\", has been used by default since M72 "
-          "(January, 2019). Dropping support for Plan B is targeted for M93, "
-          "but it's possible to register for a Deprecation Trial in order to "
-          "extend the Plan B deprecation deadline for a limited amount of "
-          "time.",
+          "non-standard version of the Session Description Protocol that has "
+          "been permanently deleted from the Web Platform. It is still "
+          "available when building with IS_FUCHSIA, but we intend to delete it "
+          "as soon as possible. Stop depending on it. See "
+          "https://crbug.com/1302249 for status.",
           "5823036655665152");
 
-    case WebFeature::kRTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial:
-      return DeprecationInfo::WithDetails(
-          "RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial", kM96,
-          "Plan B SDP semantics, which is used when constructing an "
-          "RTCPeerConnection with {sdpSemantics:\"plan-b\"}, is a legacy "
-          "version of the Session Description Protocol that has severe "
-          "compatibility issues on modern browsers. The standardized SDP "
-          "format, \"unified-plan\", has been used by default since M72 "
-          "(January, 2019). Dropping support for Plan B is targeted for M93, "
-          "but this page may extend the deadline until the End Date of the "
-          "'RTCPeerConnection Plan B SDP Semantics' deprecation trial.");
-
     case WebFeature::kAddressSpacePublicNonSecureContextEmbeddedPrivate:
     case WebFeature::kAddressSpacePublicNonSecureContextEmbeddedLocal:
     case WebFeature::kAddressSpacePrivateNonSecureContextEmbeddedLocal:
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index f0789f8..e77d640 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -51,9 +51,9 @@
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h"
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
-#include "third_party/blink/renderer/core/animation/css/css_animation_update_scope.h"
 #include "third_party/blink/renderer/core/animation/document_animations.h"
 #include "third_party/blink/renderer/core/css/font_face_set_document.h"
+#include "third_party/blink/renderer/core/css/post_style_update_scope.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
 #include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h"
@@ -291,8 +291,7 @@
           // it's not useful to generate mobile friendliness metrics for
           // devtools.
           //
-          GetFrame().Client()->IsLocalFrameClientImpl() &&
-                  GetFrame().Client()->IsLocalFrameClientImpl()
+          GetFrame().Client()->IsLocalFrameClientImpl()
               ? MakeGarbageCollected<MobileFriendlinessChecker>(*this)
               : nullptr)
 #if DCHECK_IS_ON()
@@ -3249,7 +3248,7 @@
 }
 
 bool LocalFrameView::UpdateStyleAndLayoutInternal() {
-  CSSAnimationUpdateScope animation_update_scope(*frame_->GetDocument());
+  PostStyleUpdateScope post_style_update_scope(*frame_->GetDocument());
 
   {
     frame_->GetDocument()->UpdateStyleAndLayoutTreeForThisDocument();
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 800eddc..bfa1cbff 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1080,6 +1080,65 @@
                                           "'plaintext-only', or 'inherit'.");
 }
 
+V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull* HTMLElement::hidden() const {
+  const AtomicString& attribute = FastGetAttribute(html_names::kHiddenAttr);
+
+  if (!RuntimeEnabledFeatures::BeforeMatchEventEnabled(GetExecutionContext())) {
+    return MakeGarbageCollected<
+        V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>(attribute !=
+                                                          g_null_atom);
+  }
+
+  if (attribute == g_null_atom) {
+    return MakeGarbageCollected<
+        V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>(false);
+  }
+  if (attribute == "until-found") {
+    return MakeGarbageCollected<
+        V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>(
+        String("until-found"));
+  }
+  return MakeGarbageCollected<V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull>(
+      true);
+}
+
+void HTMLElement::setHidden(
+    const V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull* value) {
+  switch (value->GetContentType()) {
+    case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType::
+        kBoolean:
+      if (value->GetAsBoolean()) {
+        setAttribute(html_names::kHiddenAttr, "");
+      } else {
+        removeAttribute(html_names::kHiddenAttr);
+      }
+      break;
+    case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType::kString:
+      if (RuntimeEnabledFeatures::BeforeMatchEventEnabled(
+              GetExecutionContext()) &&
+          EqualIgnoringASCIICase(value->GetAsString(), "until-found")) {
+        setAttribute(html_names::kHiddenAttr, "until-found");
+      } else if (value->GetAsString() == "") {
+        removeAttribute(html_names::kHiddenAttr);
+      } else {
+        setAttribute(html_names::kHiddenAttr, "");
+      }
+      break;
+    case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType::kNull:
+      removeAttribute(html_names::kHiddenAttr);
+      break;
+    case V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull::ContentType::
+        kUnrestrictedDouble:
+      double double_value = value->GetAsUnrestrictedDouble();
+      if (double_value && !std::isnan(double_value)) {
+        setAttribute(html_names::kHiddenAttr, "");
+      } else {
+        removeAttribute(html_names::kHiddenAttr);
+      }
+      break;
+  }
+}
+
 const AtomicString& HTMLElement::autocapitalize() const {
   DEFINE_STATIC_LOCAL(const AtomicString, kOff, ("off"));
   DEFINE_STATIC_LOCAL(const AtomicString, kNone, ("none"));
diff --git a/third_party/blink/renderer/core/html/html_element.h b/third_party/blink/renderer/core/html/html_element.h
index 0daa4216..8d81cfd9 100644
--- a/third_party/blink/renderer/core/html/html_element.h
+++ b/third_party/blink/renderer/core/html/html_element.h
@@ -24,6 +24,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_ELEMENT_H_
 
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_union_boolean_string_unrestricteddouble_null.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h"
@@ -173,6 +174,9 @@
   void AdjustDirectionalityIfNeededAfterShadowRootChanged();
   void BeginParsingChildren() override;
 
+  V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull* hidden() const;
+  void setHidden(const V8UnionBooleanOrStringOrUnrestrictedDoubleOrNull*);
+
  protected:
   enum AllowPercentage { kDontAllowPercentageValues, kAllowPercentageValues };
   enum AllowZero { kDontAllowZeroValues, kAllowZeroValues };
diff --git a/third_party/blink/renderer/core/html/html_element.idl b/third_party/blink/renderer/core/html/html_element.idl
index 243bb1f..8ae18ce 100644
--- a/third_party/blink/renderer/core/html/html_element.idl
+++ b/third_party/blink/renderer/core/html/html_element.idl
@@ -30,7 +30,8 @@
     [CEReactions] attribute DOMString dir;
 
     // user interaction
-    [CEReactions, Reflect] attribute boolean hidden;
+    [CEReactions] attribute (boolean or unrestricted double or DOMString?) hidden;
+
     [RuntimeCallStatsCounter=HTMLElementClick] void click();
     [CEReactions, RuntimeEnabled=InertAttribute, Reflect] attribute boolean inert;
     [CEReactions, Reflect] attribute DOMString accessKey;
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css
index 9f62d3a..dc34c8c 100644
--- a/third_party/blink/renderer/core/html/resources/html.css
+++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -664,7 +664,7 @@
     border: 2px outset ButtonBorder;
     box-sizing: border-box;
     background-color: -internal-light-dark(#efefef, #3B3B3B);
-    color: -internal-light-dark(black, white);
+    color: ButtonText;
 }
 
 input[type="checkbox" i]:disabled,
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.h b/third_party/blink/renderer/core/layout/hit_test_result.h
index 3a610c6..5105121 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.h
+++ b/third_party/blink/renderer/core/layout/hit_test_result.h
@@ -191,6 +191,10 @@
 
   void Append(const HitTestResult&);
 
+  bool HasListBasedResult() const {
+    return GetHitTestRequest().ListBased() && InnerNode();
+  }
+
   // If m_listBasedTestResult is 0 then set it to a new NodeSet. Return
   // *m_listBasedTestResult. Lazy allocation makes sense because the NodeSet is
   // seldom necessary, and it's somewhat expensive to allocate and initialize.
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index 7e4f3253..694f2763 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -624,7 +624,7 @@
     case CSSValueID::kButtonshadow:
       return 0xFF888888;
     case CSSValueID::kButtontext:
-      return color_scheme == mojom::blink::ColorScheme::kDark ? 0xFFAAAAAA
+      return color_scheme == mojom::blink::ColorScheme::kDark ? 0xFFFFFFFF
                                                               : 0xFF000000;
     case CSSValueID::kCaptiontext:
       return color_scheme == mojom::blink::ColorScheme::kDark ? 0xFFFFFFFF
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
index e621bc79..59677476 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
@@ -28,7 +28,6 @@
   }
 
   HeapVector<NGFlexLine> flex_lines;
-  // |row_break_between| is only used in the case of row flex containers.
   Vector<EBreakBetween> row_break_between;
   LayoutUnit intrinsic_block_size;
   // |broke_before_row| is only used in the case of row flex containers. If this
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index 6b5a281..3adfce4 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -815,7 +815,7 @@
       // break at that location.
       DCHECK(result->GetEarlyBreak());
       return RelayoutAndBreakEarlier<NGFlexLayoutAlgorithm>(
-          *result->GetEarlyBreak());
+          *result->GetEarlyBreak(), &column_early_breaks_);
     case NGLayoutResult::kNeedsRelayoutWithNoChildScrollbarChanges:
       return RelayoutIgnoringChildScrollbarChanges();
     case NGLayoutResult::kDisableFragmentation:
@@ -946,9 +946,11 @@
   }
 
   // For rows, the break-before of the first row and the break-after of the
-  // last row are propagated to the container.
-  if (is_horizontal_flow_ &&
-      ConstraintSpace().ShouldPropagateChildBreakValues()) {
+  // last row are propagated to the container. For columns, treat the set
+  // of columns as a single row and propagate the combined break-before rules
+  // for the first items in each column and break-after rules for last items in
+  // each column.
+  if (ConstraintSpace().ShouldPropagateChildBreakValues()) {
     DCHECK(!row_break_between_outputs.IsEmpty());
     container_builder_.SetInitialBreakBefore(row_break_between_outputs.front());
     container_builder_.SetPreviousBreakAfter(row_break_between_outputs.back());
@@ -1145,14 +1147,21 @@
   }
 
   bool should_propagate_row_break_values =
-      is_horizontal_flow_ &&
       ConstraintSpace().ShouldPropagateChildBreakValues();
   if (should_propagate_row_break_values) {
     DCHECK(row_break_between_outputs);
     // The last row break between will store the final break-after to be
     // propagated to the container.
-    *row_break_between_outputs = Vector<EBreakBetween>(
-        flex_line_outputs->size() + 1, EBreakBetween::kAuto);
+    if (is_horizontal_flow_) {
+      *row_break_between_outputs = Vector<EBreakBetween>(
+          flex_line_outputs->size() + 1, EBreakBetween::kAuto);
+    } else {
+      // For flex columns, we only need to store two values - one for
+      // the break-before value of all combined columns, and the second for
+      // for the break-after values for all combined columns.
+      *row_break_between_outputs =
+          Vector<EBreakBetween>(2, EBreakBetween::kAuto);
+    }
   }
 
   absl::optional<LayoutUnit> fallback_baseline;
@@ -1185,12 +1194,6 @@
       flex_item.has_descendant_that_depends_on_percentage_block_size =
           layout_result->HasDescendantThatDependsOnPercentageBlockSize();
 
-      // The break-before and break-after values of flex items in a flex row are
-      // propagated to the row itself. Accumulate the BreakBetween values for
-      // each row ahead of time so that they can be stored on the break token
-      // for future use.
-      //
-      // https://drafts.csswg.org/css-flexbox-1/#pagination
       if (should_propagate_row_break_values) {
         const auto& item_style = flex_item.Style();
         auto item_break_before = JoinFragmentainerBreakValues(
@@ -1198,13 +1201,34 @@
         auto item_break_after = JoinFragmentainerBreakValues(
             item_style.BreakAfter(), layout_result->FinalBreakAfter());
 
-        (*row_break_between_outputs)[flex_line_idx] =
-            JoinFragmentainerBreakValues(
-                (*row_break_between_outputs)[flex_line_idx], item_break_before);
-        (*row_break_between_outputs)[flex_line_idx + 1] =
-            JoinFragmentainerBreakValues(
-                (*row_break_between_outputs)[flex_line_idx + 1],
-                item_break_after);
+        // The break-before and break-after values of flex items in a flex row
+        // are propagated to the row itself. Accumulate the BreakBetween values
+        // for each row ahead of time so that they can be stored on the break
+        // token for future use.
+        //
+        // https://drafts.csswg.org/css-flexbox-1/#pagination
+        if (is_horizontal_flow_) {
+          (*row_break_between_outputs)[flex_line_idx] =
+              JoinFragmentainerBreakValues(
+                  (*row_break_between_outputs)[flex_line_idx],
+                  item_break_before);
+          (*row_break_between_outputs)[flex_line_idx + 1] =
+              JoinFragmentainerBreakValues(
+                  (*row_break_between_outputs)[flex_line_idx + 1],
+                  item_break_after);
+        } else {
+          // Treat all columns as a "row" of columns, and accumulate the initial
+          // and final break values for all columns, which will be propagated to
+          // the container.
+          if (flex_item_idx == 0) {
+            (*row_break_between_outputs)[0] = JoinFragmentainerBreakValues(
+                (*row_break_between_outputs)[0], item_break_before);
+          }
+          if (flex_item_idx == line_output.line_items.size() - 1) {
+            (*row_break_between_outputs)[1] = JoinFragmentainerBreakValues(
+                (*row_break_between_outputs)[1], item_break_after);
+          }
+        }
       }
 
       const auto& physical_fragment =
@@ -1267,6 +1291,13 @@
                                    is_horizontal_flow_);
   Vector<bool> has_inflow_child_break_inside_line(flex_line_outputs->size(),
                                                   false);
+  bool needs_earlier_break_in_column = false;
+
+  HeapVector<NGFlexColumnBreakInfo> column_break_info;
+  if (!is_horizontal_flow_) {
+    column_break_info =
+        HeapVector<NGFlexColumnBreakInfo>(flex_line_outputs->size());
+  }
 
   for (auto entry = item_iterator.NextItem(*broke_before_row);
        NGFlexItem* flex_item = entry.flex_item;
@@ -1285,6 +1316,16 @@
           has_inflow_child_break_inside_line[flex_line_idx - 1])
         break;
     } else {
+      // If we are relaying out as a result of an early break, and we have early
+      // breaks for more than one column, they will be stored in
+      // |additional_early_breaks_|. Keep |early_break_| consistent with that of
+      // the current column.
+      if (additional_early_breaks_ &&
+          flex_line_idx < additional_early_breaks_->size())
+        early_break_ = (*additional_early_breaks_)[flex_line_idx];
+      else if (early_break_ && flex_line_idx != 0)
+        early_break_ = nullptr;
+
       if (has_inflow_child_break_inside_line[flex_line_idx]) {
         if (!last_item_in_line)
           item_iterator.NextLine();
@@ -1348,10 +1389,17 @@
           *broke_before_row = true;
         ConsumeRemainingFragmentainerSpace(previously_consumed_block_size,
                                            &line_output);
+        // For column flex containers, continue to the next column. For rows,
+        // continue until we've processed all items in the current row.
         has_inflow_child_break_inside_line[flex_line_idx] = true;
-        if (!last_item_in_line && !is_horizontal_flow_)
-          item_iterator.NextLine();
-        return NGLayoutResult::kSuccess;
+        if (!is_horizontal_flow_) {
+          if (!last_item_in_line)
+            item_iterator.NextLine();
+        } else if (last_item_in_line) {
+          return NGLayoutResult::kSuccess;
+        }
+        last_line_idx_to_process_first_child_ = flex_line_idx;
+        continue;
       } else {
         early_break_in_child =
             EnterEarlyBreakInChild(flex_item->ng_input_node, *early_break_);
@@ -1375,6 +1423,7 @@
         child_space, item_break_token, early_break_in_child);
 
     NGBreakStatus break_status = NGBreakStatus::kContinue;
+    NGFlexColumnBreakInfo* current_column_break_info = nullptr;
     if (!early_break_ && ConstraintSpace().HasBlockFragmentation()) {
       bool has_container_separation = false;
       if (is_horizontal_flow_) {
@@ -1403,13 +1452,42 @@
         }
       } else {
         has_container_separation =
-            last_line_idx_to_process_first_child_ == flex_line_idx ||
+            (last_line_idx_to_process_first_child_ != kNotFound &&
+             last_line_idx_to_process_first_child_ >= flex_line_idx) ||
             (!item_break_token && offset.block_offset > LayoutUnit());
+
+        // We may switch back and forth between columns, so we need to make sure
+        // to use the break-after for the current column.
+        if (flex_line_outputs->size() > 1) {
+          current_column_break_info = &column_break_info[flex_line_idx];
+          container_builder_.SetPreviousBreakAfter(
+              current_column_break_info->break_after);
+        }
       }
       break_status = BreakBeforeChildIfNeeded(
           ConstraintSpace(), flex_item->ng_input_node, *layout_result,
           ConstraintSpace().FragmentainerOffsetAtBfc() + offset.block_offset,
-          has_container_separation, &container_builder_, is_horizontal_flow_);
+          has_container_separation, &container_builder_, is_horizontal_flow_,
+          current_column_break_info);
+    }
+
+    if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
+      if (current_column_break_info) {
+        DCHECK(!is_horizontal_flow_);
+        DCHECK(current_column_break_info->early_break);
+        if (!needs_earlier_break_in_column) {
+          needs_earlier_break_in_column = true;
+          container_builder_.SetEarlyBreak(
+              current_column_break_info->early_break);
+        }
+        // Keep track of the early breaks for each column.
+        AddColumnEarlyBreak(current_column_break_info->early_break,
+                            flex_line_idx);
+        if (!last_item_in_line)
+          item_iterator.NextLine();
+        continue;
+      }
+      return NGLayoutResult::kNeedsEarlierBreak;
     }
 
     if (break_status == NGBreakStatus::kBrokeBefore) {
@@ -1427,9 +1505,6 @@
       last_line_idx_to_process_first_child_ = flex_line_idx;
       continue;
     }
-    if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
-      return NGLayoutResult::kNeedsEarlierBreak;
-    }
 
     const auto& physical_fragment =
         To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment());
@@ -1461,13 +1536,25 @@
       }
     }
 
+    if (!is_horizontal_flow_) {
+      line_output.line_intrinsic_block_size =
+          line_output.line_intrinsic_block_size.ClampIndefiniteToZero();
+      line_output.line_intrinsic_block_size +=
+          offset.block_offset + fragment.BlockSize();
+    }
+
     // TODO(almaher): What to do in the case where the line extends past
     // the last item? Should that be included when fragmenting?
     intrinsic_block_size_ +=
         (offset.block_offset + fragment.BlockSize() - intrinsic_block_size_)
             .ClampNegativeToZero();
 
-    container_builder_.AddResult(*layout_result, offset);
+    container_builder_.AddResult(*layout_result, offset,
+                                 /* relative_offset */ absl::nullopt,
+                                 /* inline_container */ nullptr,
+                                 current_column_break_info
+                                     ? &current_column_break_info->break_after
+                                     : nullptr);
 
     // Only propagate baselines from children on the first flex-line.
     if (&line_output == flex_line_outputs->begin()) {
@@ -1499,6 +1586,9 @@
     last_line_idx_to_process_first_child_ = flex_line_idx;
   }
 
+  if (needs_earlier_break_in_column)
+    return NGLayoutResult::kNeedsEarlierBreak;
+
   if (!container_builder_.HasInflowChildBreakInside() &&
       !item_iterator.NextItem(*broke_before_row).flex_item) {
     container_builder_.SetHasSeenAllChildren();
@@ -1988,8 +2078,15 @@
     // This will be further adjusted by the total consumed block size once we
     // handle the break before in the next fragmentainer. This ensures that the
     // expansion is properly handled in the column balancing pass.
+    LayoutUnit intrinsic_block_size = intrinsic_block_size_;
+    if (flex_line->line_intrinsic_block_size != kIndefiniteSize) {
+      DCHECK(!is_horizontal_flow_);
+      intrinsic_block_size = (flex_line->line_intrinsic_block_size -
+                              previously_consumed_block_size)
+                                 .ClampNegativeToZero();
+    }
     flex_line->item_offset_adjustment -=
-        intrinsic_block_size_ + previously_consumed_block_size;
+        intrinsic_block_size + previously_consumed_block_size;
   }
 
   if (!ConstraintSpace().HasKnownFragmentainerBlockSize())
@@ -2096,6 +2193,14 @@
   return true;
 }
 
+void NGFlexLayoutAlgorithm::AddColumnEarlyBreak(NGEarlyBreak* breakpoint,
+                                                wtf_size_t index) {
+  DCHECK(!is_horizontal_flow_);
+  while (column_early_breaks_.size() <= index)
+    column_early_breaks_.push_back(nullptr);
+  column_early_breaks_[index] = breakpoint;
+}
+
 #if DCHECK_IS_ON()
 void NGFlexLayoutAlgorithm::CheckFlexLines(
     const HeapVector<NGFlexLine>& flex_line_outputs) const {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
index d421a30..205c294 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -150,6 +150,9 @@
                              LayoutUnit row_block_size,
                              wtf_size_t row_index);
 
+  // Add an early break for the column at the provided |index|.
+  void AddColumnEarlyBreak(NGEarlyBreak* breakpoint, wtf_size_t index);
+
 #if DCHECK_IS_ON()
   void CheckFlexLines(const HeapVector<NGFlexLine>& flex_line_outputs) const;
 #endif
@@ -184,6 +187,11 @@
   // fragmenting, |total_intrinsic_block_size| and |intrinsic_block_size_| will
   // be equivalent.
   LayoutUnit total_intrinsic_block_size_;
+
+  // Only one early break is supported per container. However, we may need to
+  // return to an early break within multiple flex columns. This stores the
+  // early breaks per column to be used when aborting layout.
+  HeapVector<Member<NGEarlyBreak>> column_early_breaks_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
index b4840861..5fc0923 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
@@ -43,6 +43,8 @@
   LayoutUnit line_cross_size;
   LayoutUnit cross_axis_offset;
   LayoutUnit item_offset_adjustment;
+  // This is only used for columns during fragmentation.
+  LayoutUnit line_intrinsic_block_size = kIndefiniteSize;
   bool has_seen_all_children = false;
   HeapVector<NGFlexItem> line_items;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index c83e3e0..88f1fe0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -85,7 +85,8 @@
     const NGLayoutResult& child_layout_result,
     const LogicalOffset offset,
     absl::optional<LogicalOffset> relative_offset,
-    const NGInlineContainer<LogicalOffset>* inline_container) {
+    const NGInlineContainer<LogicalOffset>* inline_container,
+    EBreakBetween* flex_column_break_after) {
   const auto& fragment = child_layout_result.PhysicalFragment();
 
   // We'll normally propagate info from child_layout_result here, but if that's
@@ -129,7 +130,7 @@
     PropagateBreakInfo(*result_for_propagation, offset);
   if (UNLIKELY(ConstraintSpace() &&
                ConstraintSpace()->ShouldPropagateChildBreakValues()))
-    PropagateChildBreakValues(*result_for_propagation);
+    PropagateChildBreakValues(*result_for_propagation, flex_column_break_after);
 }
 
 void NGBoxFragmentBuilder::AddChild(
@@ -444,7 +445,8 @@
 }
 
 void NGBoxFragmentBuilder::PropagateChildBreakValues(
-    const NGLayoutResult& child_layout_result) {
+    const NGLayoutResult& child_layout_result,
+    EBreakBetween* flex_column_break_after) {
   if (child_layout_result.Status() != NGLayoutResult::kSuccess)
     return;
 
@@ -474,6 +476,8 @@
   EBreakBetween break_after = JoinFragmentainerBreakValues(
       child_layout_result.FinalBreakAfter(), child_style.BreakAfter());
   SetPreviousBreakAfter(break_after);
+  if (flex_column_break_after)
+    *flex_column_break_after = break_after;
 }
 
 const NGLayoutResult* NGBoxFragmentBuilder::ToBoxFragment(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index 0c93eb4..1a3fd79 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -197,12 +197,15 @@
   // computed ahead of time. If so, a |relative_offset| will be passed
   // in. Otherwise, the relative offset will be calculated as normal.
   // |inline_container| is passed when adding an OOF that is contained by a
-  // non-atomic inline.
+  // non-atomic inline. If |flex_column_break_after| is supplied, we are running
+  // layout for a column flex container, in which case, we need to update the
+  // break-after value for the column itself.
   void AddResult(
       const NGLayoutResult&,
       const LogicalOffset,
       absl::optional<LogicalOffset> relative_offset = absl::nullopt,
-      const NGInlineContainer<LogicalOffset>* inline_container = nullptr);
+      const NGInlineContainer<LogicalOffset>* inline_container = nullptr,
+      EBreakBetween* flex_column_break_after = nullptr);
 
   // Add a child fragment and propagate info from it. Called by AddResult().
   // Other callers should call AddResult() instead of this when possible, since
@@ -669,7 +672,10 @@
   }
 
   // Propagate the break-before/break-after of the child (if applicable).
-  void PropagateChildBreakValues(const NGLayoutResult& child_layout_result);
+  // Update the break-after value for |flex_column_break_after|, if supplied.
+  void PropagateChildBreakValues(
+      const NGLayoutResult& child_layout_result,
+      EBreakBetween* flex_column_break_after = nullptr);
 
  private:
   // Propagate fragmentation details. This includes checking whether we have
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
index 13149c3f..3222e7a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -593,13 +593,15 @@
   return NGBreakStatus::kContinue;
 }
 
-NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace& space,
-                                       NGLayoutInputNode child,
-                                       const NGLayoutResult& layout_result,
-                                       LayoutUnit fragmentainer_block_offset,
-                                       bool has_container_separation,
-                                       NGBoxFragmentBuilder* builder,
-                                       bool is_row_item) {
+NGBreakStatus BreakBeforeChildIfNeeded(
+    const NGConstraintSpace& space,
+    NGLayoutInputNode child,
+    const NGLayoutResult& layout_result,
+    LayoutUnit fragmentainer_block_offset,
+    bool has_container_separation,
+    NGBoxFragmentBuilder* builder,
+    bool is_row_item,
+    NGFlexColumnBreakInfo* flex_column_break_info) {
   DCHECK(space.HasBlockFragmentation());
 
   // Break-before and break-after are handled at the row level.
@@ -608,8 +610,11 @@
         CalculateBreakBetweenValue(child, layout_result, *builder);
     if (IsForcedBreakValue(space, break_between)) {
       BreakBeforeChild(space, child, &layout_result, fragmentainer_block_offset,
-                       kBreakAppealPerfect, /* is_forced_break */ true,
-                       builder);
+                       kBreakAppealPerfect, /* is_forced_break */ true, builder,
+                       /* block_size_override */ absl::nullopt,
+                       flex_column_break_info
+                           ? &flex_column_break_info->break_after
+                           : nullptr);
       return NGBreakStatus::kBrokeBefore;
     }
   }
@@ -622,14 +627,16 @@
   // the appeal of breaking there, even if we didn't.
   if (MovePastBreakpoint(space, child, layout_result,
                          fragmentainer_block_offset, appeal_before, builder,
-                         is_row_item))
+                         is_row_item, flex_column_break_info))
     return NGBreakStatus::kContinue;
 
   // Breaking inside the child isn't appealing, and we're out of space. Figure
   // out where to insert a soft break. It will either be before this child, or
   // before an earlier sibling, if there's a more appealing breakpoint there.
   if (!AttemptSoftBreak(space, child, &layout_result,
-                        fragmentainer_block_offset, appeal_before, builder))
+                        fragmentainer_block_offset, appeal_before, builder,
+                        /* block_size_override */ absl::nullopt,
+                        flex_column_break_info))
     return NGBreakStatus::kNeedsEarlierBreak;
 
   return NGBreakStatus::kBrokeBefore;
@@ -642,7 +649,8 @@
                       absl::optional<NGBreakAppeal> appeal,
                       bool is_forced_break,
                       NGBoxFragmentBuilder* builder,
-                      absl::optional<LayoutUnit> block_size_override) {
+                      absl::optional<LayoutUnit> block_size_override,
+                      EBreakBetween* flex_column_break_after) {
 #if DCHECK_IS_ON()
   DCHECK(layout_result || block_size_override);
   if (layout_result && layout_result->Status() == NGLayoutResult::kSuccess) {
@@ -668,7 +676,7 @@
 
   if (layout_result && space.ShouldPropagateChildBreakValues() &&
       !is_forced_break)
-    builder->PropagateChildBreakValues(*layout_result);
+    builder->PropagateChildBreakValues(*layout_result, flex_column_break_after);
 
   // We'll drop the fragment (if any) on the floor and retry at the start of the
   // next fragmentainer.
@@ -723,7 +731,8 @@
                         LayoutUnit fragmentainer_block_offset,
                         NGBreakAppeal appeal_before,
                         NGBoxFragmentBuilder* builder,
-                        bool is_row_item) {
+                        bool is_row_item,
+                        NGFlexColumnBreakInfo* flex_column_break_info) {
   if (layout_result.Status() != NGLayoutResult::kSuccess) {
     // Layout aborted - no fragment was produced. There's nothing to move
     // past. We need to break before.
@@ -819,10 +828,17 @@
     // breaking before is impossible, break inside regardless of appeal.
     if (refuse_break_before)
       return true;
-    if (appeal_inside >= appeal_before &&
-        (!builder || !builder->HasEarlyBreak() ||
-         appeal_inside >= builder->EarlyBreak().BreakAppeal()))
-      return true;
+    if (appeal_inside >= appeal_before) {
+      if (flex_column_break_info &&
+          (!flex_column_break_info->early_break ||
+           appeal_inside >=
+               flex_column_break_info->early_break->BreakAppeal())) {
+        return true;
+      } else if (!builder || !builder->HasEarlyBreak() ||
+                 appeal_inside >= builder->EarlyBreak().BreakAppeal()) {
+        return true;
+      }
+    }
   } else {
     bool move_past = refuse_break_before;
     if (!move_past) {
@@ -849,7 +865,8 @@
         // orphans and widows, if at all possible. We also only do this for
         // non-row items since items in a row will be parallel to one another.)
         UpdateEarlyBreakAtBlockChild(space, To<NGBlockNode>(child),
-                                     layout_result, appeal_before, builder);
+                                     layout_result, appeal_before, builder,
+                                     flex_column_break_info);
       }
       return true;
     }
@@ -859,11 +876,13 @@
   return false;
 }
 
-void UpdateEarlyBreakAtBlockChild(const NGConstraintSpace& space,
-                                  NGBlockNode child,
-                                  const NGLayoutResult& layout_result,
-                                  NGBreakAppeal appeal_before,
-                                  NGBoxFragmentBuilder* builder) {
+void UpdateEarlyBreakAtBlockChild(
+    const NGConstraintSpace& space,
+    NGBlockNode child,
+    const NGLayoutResult& layout_result,
+    NGBreakAppeal appeal_before,
+    NGBoxFragmentBuilder* builder,
+    NGFlexColumnBreakInfo* flex_column_break_info) {
   // If the child already broke, it's a little too late to look for breakpoints.
   DCHECK(!layout_result.PhysicalFragment().BreakToken());
 
@@ -872,8 +891,19 @@
   if (const NGEarlyBreak* breakpoint = layout_result.GetEarlyBreak()) {
     appeal_inside = CalculateBreakAppealInside(space, layout_result,
                                                breakpoint->BreakAppeal());
-    if (!builder->HasEarlyBreak() ||
-        builder->EarlyBreak().BreakAppeal() <= breakpoint->BreakAppeal()) {
+    if (flex_column_break_info) {
+      if (!flex_column_break_info->early_break ||
+          flex_column_break_info->early_break->BreakAppeal() <=
+              breakpoint->BreakAppeal()) {
+        // Found a good breakpoint inside the child. Add the child to the early
+        // break chain for the current column.
+        auto* parent_break = MakeGarbageCollected<NGEarlyBreak>(
+            child, appeal_inside, breakpoint);
+        flex_column_break_info->early_break = parent_break;
+      }
+    } else if (!builder->HasEarlyBreak() ||
+               builder->EarlyBreak().BreakAppeal() <=
+                   breakpoint->BreakAppeal()) {
       // Found a good breakpoint inside the child. Add the child to the early
       // break container chain, and store it.
       auto* parent_break =
@@ -889,6 +919,15 @@
   if (appeal_before <= appeal_inside)
     return;
 
+  if (flex_column_break_info) {
+    if (flex_column_break_info->early_break &&
+        flex_column_break_info->early_break->BreakAppeal() > appeal_before)
+      return;
+    flex_column_break_info->early_break =
+        MakeGarbageCollected<NGEarlyBreak>(child, appeal_before);
+    return;
+  }
+
   if (builder->HasEarlyBreak() &&
       builder->EarlyBreak().BreakAppeal() > appeal_before)
     return;
@@ -903,12 +942,21 @@
                       LayoutUnit fragmentainer_block_offset,
                       NGBreakAppeal appeal_before,
                       NGBoxFragmentBuilder* builder,
-                      absl::optional<LayoutUnit> block_size_override) {
+                      absl::optional<LayoutUnit> block_size_override,
+                      NGFlexColumnBreakInfo* flex_column_break_info) {
   DCHECK(layout_result || block_size_override);
   // If there's a breakpoint with higher appeal among earlier siblings, we need
   // to abort and re-layout to that breakpoint.
-  if (builder->HasEarlyBreak() &&
-      builder->EarlyBreak().BreakAppeal() > appeal_before) {
+  bool found_earlier_break = false;
+  if (flex_column_break_info) {
+    found_earlier_break =
+        flex_column_break_info->early_break &&
+        flex_column_break_info->early_break->BreakAppeal() > appeal_before;
+  } else {
+    found_earlier_break = builder->HasEarlyBreak() &&
+                          builder->EarlyBreak().BreakAppeal() > appeal_before;
+  }
+  if (found_earlier_break) {
     // Found a better place to break. Before aborting, calculate and report
     // space shortage from where we'd actually break.
     PropagateSpaceShortage(space, layout_result, fragmentainer_block_offset,
@@ -919,9 +967,10 @@
   // Break before the child. Note that there may be a better break further up
   // with higher appeal (but it's too early to tell), in which case this
   // breakpoint will be replaced.
-  BreakBeforeChild(space, child, layout_result, fragmentainer_block_offset,
-                   appeal_before, /* is_forced_break */ false, builder,
-                   block_size_override);
+  BreakBeforeChild(
+      space, child, layout_result, fragmentainer_block_offset, appeal_before,
+      /* is_forced_break */ false, builder, block_size_override,
+      flex_column_break_info ? &flex_column_break_info->break_after : nullptr);
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
index b49e4e9..aae9d6b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
@@ -22,6 +22,19 @@
 class NGEarlyBreak;
 class NGLayoutResult;
 
+// Each column in a flex container is fragmented independently, so we need to
+// track early break and break-after info for each column separately.
+struct NGFlexColumnBreakInfo {
+  DISALLOW_NEW();
+
+  NGFlexColumnBreakInfo() = default;
+
+  void Trace(Visitor* visitor) const { visitor->Trace(early_break); }
+
+  Member<NGEarlyBreak> early_break = nullptr;
+  EBreakBetween break_after = EBreakBetween::kAuto;
+};
+
 // Join two adjacent break values specified on break-before and/or break-
 // after. avoid* values win over auto values, and forced break values win over
 // avoid* values. |first_value| is specified on an element earlier in the flow
@@ -268,17 +281,24 @@
 // class C breakpoints. Those occur if there's a positive gap between the
 // block-start content edge of the container and the block-start margin edge of
 // the first in-flow child. https://www.w3.org/TR/css-break-3/#possible-breaks
-NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace&,
-                                       NGLayoutInputNode child,
-                                       const NGLayoutResult&,
-                                       LayoutUnit fragmentainer_block_offset,
-                                       bool has_container_separation,
-                                       NGBoxFragmentBuilder*,
-                                       bool is_row_item = false);
+// If |flex_column_break_info| is supplied, we are running layout for a column
+// flex container, in which case, we may be tracking certain break behavior at
+// the column level.
+NGBreakStatus BreakBeforeChildIfNeeded(
+    const NGConstraintSpace&,
+    NGLayoutInputNode child,
+    const NGLayoutResult&,
+    LayoutUnit fragmentainer_block_offset,
+    bool has_container_separation,
+    NGBoxFragmentBuilder*,
+    bool is_row_item = false,
+    NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
 
 // Insert a break before the child, and propagate space shortage if needed.
 // |block_size_override| should only be supplied when you wish to propagate a
-// different block-size than that of the provided layout result.
+// different block-size than that of the provided layout result. If
+// |flex_column_break_after| is supplied, then we need to update the
+// break-after value for the column, as well.
 void BreakBeforeChild(
     const NGConstraintSpace&,
     NGLayoutInputNode child,
@@ -287,7 +307,8 @@
     absl::optional<NGBreakAppeal> appeal,
     bool is_forced_break,
     NGBoxFragmentBuilder*,
-    absl::optional<LayoutUnit> block_size_override = absl::nullopt);
+    absl::optional<LayoutUnit> block_size_override = absl::nullopt,
+    EBreakBetween* flex_column_break_after = nullptr);
 
 // Propagate the block-size of unbreakable content. This is used to inflate the
 // initial minimal column block-size when balancing columns, before we calculate
@@ -321,29 +342,36 @@
 // Move past the breakpoint before the child, if possible, and return true. Also
 // update the appeal of breaking before or inside the child (if we're not going
 // to break before it). If false is returned, it means that we need to break
-// before the child (or even earlier).
-bool MovePastBreakpoint(const NGConstraintSpace& space,
-                        NGLayoutInputNode child,
-                        const NGLayoutResult& layout_result,
-                        LayoutUnit fragmentainer_block_offset,
-                        NGBreakAppeal appeal_before,
-                        NGBoxFragmentBuilder* builder,
-                        bool is_row_item = false);
+// before the child (or even earlier). See BreakBeforeChildIfNeeded() for
+// details on |flex_column_break_info|.
+bool MovePastBreakpoint(
+    const NGConstraintSpace& space,
+    NGLayoutInputNode child,
+    const NGLayoutResult& layout_result,
+    LayoutUnit fragmentainer_block_offset,
+    NGBreakAppeal appeal_before,
+    NGBoxFragmentBuilder* builder,
+    bool is_row_item = false,
+    NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
 
 // If the appeal of breaking before or inside the child is the same or higher
 // than any previous breakpoint we've found, set a new breakpoint in the
-// builder, and update appeal accordingly.
-void UpdateEarlyBreakAtBlockChild(const NGConstraintSpace&,
-                                  NGBlockNode child,
-                                  const NGLayoutResult&,
-                                  NGBreakAppeal appeal_before,
-                                  NGBoxFragmentBuilder*);
+// builder, and update appeal accordingly. See BreakBeforeChildIfNeeded() for
+// details on |flex_column_break_info|.
+void UpdateEarlyBreakAtBlockChild(
+    const NGConstraintSpace&,
+    NGBlockNode child,
+    const NGLayoutResult&,
+    NGBreakAppeal appeal_before,
+    NGBoxFragmentBuilder*,
+    NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
 
 // Attempt to insert a soft break before the child, and return true if we did.
 // If false is returned, it means that the desired breakpoint is earlier in the
 // container, and that we need to abort and re-layout to that breakpoint.
 // |block_size_override| should only be supplied when you wish to propagate a
-// different block-size than that of the provided layout result.
+// different block-size than that of the provided layout result. See
+// BreakBeforeChildIfNeeded() for details on |flex_column_break_info|.
 bool AttemptSoftBreak(
     const NGConstraintSpace&,
     NGLayoutInputNode child,
@@ -351,7 +379,8 @@
     LayoutUnit fragmentainer_block_offset,
     NGBreakAppeal appeal_before,
     NGBoxFragmentBuilder*,
-    absl::optional<LayoutUnit> block_size_override = absl::nullopt);
+    absl::optional<LayoutUnit> block_size_override = absl::nullopt,
+    NGFlexColumnBreakInfo* flex_column_break_info = nullptr);
 
 // If we have an previously found break point, and we're entering an ancestor of
 // the node we're going to break before, return the early break inside. This can
@@ -431,4 +460,6 @@
 
 }  // namespace blink
 
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::NGFlexColumnBreakInfo)
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENTATION_UTILS_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h
index 4adffc7..7af8c85 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h
@@ -40,16 +40,19 @@
   STACK_ALLOCATED();
 
  public:
-  NGLayoutAlgorithmParams(NGBlockNode node,
-                          const NGFragmentGeometry& fragment_geometry,
-                          const NGConstraintSpace& space,
-                          const NGBlockBreakToken* break_token = nullptr,
-                          const NGEarlyBreak* early_break = nullptr)
+  NGLayoutAlgorithmParams(
+      NGBlockNode node,
+      const NGFragmentGeometry& fragment_geometry,
+      const NGConstraintSpace& space,
+      const NGBlockBreakToken* break_token = nullptr,
+      const NGEarlyBreak* early_break = nullptr,
+      const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks = nullptr)
       : node(node),
         fragment_geometry(fragment_geometry),
         space(space),
         break_token(break_token),
-        early_break(early_break) {}
+        early_break(early_break),
+        additional_early_breaks(additional_early_breaks) {}
 
   NGBlockNode node;
   const NGFragmentGeometry& fragment_geometry;
@@ -57,6 +60,7 @@
   const NGBlockBreakToken* break_token;
   const NGEarlyBreak* early_break;
   const NGLayoutResult* previous_result = nullptr;
+  const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks;
 };
 
 // Base class for all LayoutNG algorithms.
@@ -88,7 +92,8 @@
             params.node,
             &params.node.Style(),
             &params.space,
-            {params.space.GetWritingMode(), params.space.Direction()}) {
+            {params.space.GetWritingMode(), params.space.Direction()}),
+        additional_early_breaks_(params.additional_early_breaks) {
     container_builder_.SetIsNewFormattingContext(
         params.space.IsNewFormattingContext());
     container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
@@ -138,13 +143,16 @@
   // such as orphans, widows, break-before:avoid or break-after:avoid.
   template <typename Algorithm>
   const NGLayoutResult* RelayoutAndBreakEarlier(
-      const NGEarlyBreak& breakpoint) {
+      const NGEarlyBreak& breakpoint,
+      const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks =
+          nullptr) {
     // Not allowed to recurse!
     DCHECK(!early_break_);
+    DCHECK(!additional_early_breaks_ || additional_early_breaks_->IsEmpty());
 
     NGLayoutAlgorithmParams params(
         Node(), container_builder_.InitialFragmentGeometry(), ConstraintSpace(),
-        BreakToken(), &breakpoint);
+        BreakToken(), &breakpoint, additional_early_breaks);
     Algorithm algorithm_with_break(params);
     auto& new_builder = algorithm_with_break.container_builder_;
     new_builder.SetBoxType(container_builder_.BoxType());
@@ -191,6 +199,11 @@
   const NGBreakTokenType* break_token_;
 
   NGBoxFragmentBuilderType container_builder_;
+
+  // There are cases where we may need more than one early break per fragment.
+  // For example, there may be an early break within multiple flex columns. This
+  // can be used to pass additional early breaks to the next layout pass.
+  const HeapVector<Member<NGEarlyBreak>>* additional_early_breaks_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index 0899a90c..fdd51ac 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -240,27 +240,33 @@
   column_locations->resize(column_constraints.data.size());
   if (column_locations->IsEmpty())
     return;
+  bool is_first_non_collpased_column = true;
   LayoutUnit column_offset = inline_border_spacing;
   for (wtf_size_t i = 0; i < column_constraints.data.size(); ++i) {
     auto& column_location = (*column_locations)[i];
     auto& column_constraint = column_constraints.data[i];
-    *has_collapsed_columns =
-        *has_collapsed_columns || column_constraint.is_collapsed;
-    column_location.offset = column_offset;
+    *has_collapsed_columns |= column_constraint.is_collapsed;
     if (column_constraints.data[i].is_mergeable &&
         (column_sizes[i] == kIndefiniteSize ||
          column_sizes[i] == LayoutUnit())) {
       // Empty mergeable columns are treated as collapsed.
+      column_location.offset = column_offset;
       column_location.size = LayoutUnit();
       column_location.is_collapsed = true;
     } else if (shrink_collapsed && column_constraint.is_collapsed) {
-      column_location.is_collapsed = true;
+      column_location.offset = column_offset;
       column_location.size = LayoutUnit();
+      column_location.is_collapsed = true;
     } else {
-      column_location.is_collapsed = false;
+      if (is_first_non_collpased_column)
+        is_first_non_collpased_column = false;
+      else
+        column_offset += inline_border_spacing;
+      column_location.offset = column_offset;
       column_location.size =
           column_sizes[i] != kIndefiniteSize ? column_sizes[i] : LayoutUnit();
-      column_offset += column_location.size + inline_border_spacing;
+      column_location.is_collapsed = false;
+      column_offset += column_location.size;
     }
   }
 }
@@ -1068,7 +1074,7 @@
                                    grid_converter.ToPhysical(table_grid_rect),
                                    border_spacing, column_block_size);
 
-  if (Node().GetDOMNode() &&
+  if (RuntimeEnabledFeatures::MathMLCoreEnabled() && Node().GetDOMNode() &&
       Node().GetDOMNode()->HasTagName(mathml_names::kMtableTag))
     table_baseline = MathTableBaseline(Style(), child_block_offset);
   if (table_baseline)
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index 90887f1..b37e75d 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -171,37 +171,6 @@
       row.Style().GetWritingDirection();
   const bool has_collapsed_borders = table_borders.IsCollapsed();
 
-  auto CreateCellConstraintSpace = [&column_locations, &table_writing_direction,
-                                    &is_table_block_size_specified,
-                                    &has_collapsed_borders,
-                                    &cell_percentage_inline_size](
-                                       const NGBlockNode& cell,
-                                       wtf_size_t start_column_index,
-                                       const NGBoxStrut& cell_borders) {
-    const wtf_size_t start_column = start_column_index;
-    DCHECK_LT(start_column, column_locations.size());
-    const wtf_size_t end_column =
-        std::min(start_column + cell.TableCellColspan() - 1,
-                 column_locations.size() - 1);
-    const LayoutUnit cell_inline_size = column_locations[end_column].offset +
-                                        column_locations[end_column].size -
-                                        column_locations[start_column].offset;
-
-    // Typically we want these values to match the "layout" pass as close as
-    // possible. The one exception is "is_hidden_for_paint". This is set to
-    // true if a cell should be hidden within a collapsed column. If this is
-    // the case, the size is almost certainly different causing a second layout.
-    return NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
-               table_writing_direction, cell, cell_borders,
-               {cell_inline_size, kIndefiniteSize}, cell_percentage_inline_size,
-               /* alignment_baseline */ absl::nullopt, start_column,
-               /* is_initial_block_size_indefinite */ true,
-               is_table_block_size_specified,
-               /* is_hidden_for_paint */ false, has_collapsed_borders,
-               NGCacheSlot::kMeasure)
-        .ToConstraintSpace();
-  };
-
   // TODO(layout-ng) Scrollbars should be frozen when computing row sizes.
   // This cannot be done today, because fragments with frozen scrollbars
   // will be cached. Needs to be fixed in NG framework.
@@ -221,9 +190,20 @@
     const NGBoxStrut cell_borders = table_borders.CellBorder(
         cell, row_index, colspan_cell_tabulator->CurrentColumn(), section_index,
         table_writing_direction);
-    const NGConstraintSpace cell_constraint_space = CreateCellConstraintSpace(
-        cell, colspan_cell_tabulator->CurrentColumn(), cell_borders);
-    const NGLayoutResult* layout_result = cell.Layout(cell_constraint_space);
+
+    // We want these values to match the "layout" pass as close as possible.
+    const auto cell_space =
+        NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
+            table_writing_direction, cell, cell_borders, column_locations,
+            /* cell_block_size */ kIndefiniteSize, cell_percentage_inline_size,
+            /* alignment_baseline */ absl::nullopt,
+            colspan_cell_tabulator->CurrentColumn(),
+            /* is_initial_block_size_indefinite */ true,
+            is_table_block_size_specified, has_collapsed_borders,
+            NGCacheSlot::kMeasure)
+            .ToConstraintSpace();
+    const NGLayoutResult* layout_result = cell.Layout(cell_space);
+
     const NGBoxFragment fragment(
         table_writing_direction,
         To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment()));
@@ -255,8 +235,7 @@
       cell_css_percent = cell_specified_block_length.Percent();
     } else if (cell_specified_block_length.IsFixed()) {
       // NOTE: Ignore min/max-height for determining the |cell_css_block_size|.
-      NGBoxStrut cell_padding =
-          ComputePadding(cell_constraint_space, cell_style);
+      NGBoxStrut cell_padding = ComputePadding(cell_space, cell_style);
       NGBoxStrut border_padding = cell_borders + cell_padding;
       // https://quirks.spec.whatwg.org/#the-table-cell-height-box-sizing-quirk
       if (cell.GetDocument().InQuirksMode() ||
@@ -455,17 +434,32 @@
     const WritingDirectionMode table_writing_direction,
     const NGBlockNode cell,
     const NGBoxStrut& cell_borders,
-    LogicalSize cell_size,
+    const Vector<NGTableColumnLocation>& column_locations,
+    LayoutUnit cell_block_size,
     LayoutUnit percentage_inline_size,
     absl::optional<LayoutUnit> alignment_baseline,
-    wtf_size_t column_index,
+    wtf_size_t start_column,
     bool is_initial_block_size_indefinite,
     bool is_table_block_size_specified,
-    bool is_hidden_for_paint,
     bool has_collapsed_borders,
     NGCacheSlot cache_slot) {
   const auto& cell_style = cell.Style();
   const auto table_writing_mode = table_writing_direction.GetWritingMode();
+  const wtf_size_t end_column = std::min(
+      start_column + cell.TableCellColspan() - 1, column_locations.size() - 1);
+  const LayoutUnit cell_inline_size = column_locations[end_column].offset +
+                                      column_locations[end_column].size -
+                                      column_locations[start_column].offset;
+
+  // A table-cell is hidden if all the columns it spans are collapsed.
+  const bool is_hidden_for_paint = [&]() -> bool {
+    for (wtf_size_t column = start_column; column <= end_column; ++column) {
+      if (!column_locations[column].is_collapsed)
+        return false;
+    }
+    return true;
+  }();
+
   NGConstraintSpaceBuilder builder(table_writing_mode,
                                    cell_style.GetWritingDirection(),
                                    /* is_new_fc */ true);
@@ -478,21 +472,20 @@
                                                : icb_size.width);
   }
 
-  builder.SetAvailableSize(cell_size);
+  builder.SetAvailableSize({cell_inline_size, cell_block_size});
   builder.SetIsFixedInlineSize(true);
-  if (cell_size.block_size != kIndefiniteSize)
+  if (cell_block_size != kIndefiniteSize)
     builder.SetIsFixedBlockSize(true);
   builder.SetIsInitialBlockSizeIndefinite(is_initial_block_size_indefinite);
 
-  // Standard:
-  // https://www.w3.org/TR/css-tables-3/#computing-the-table-height "the
-  // computed height (if definite, percentages being considered 0px)"
+  // https://www.w3.org/TR/css-tables-3/#computing-the-table-height
+  // "the computed height (if definite, percentages being considered 0px)"
   builder.SetPercentageResolutionSize(
       {percentage_inline_size, kIndefiniteSize});
 
   builder.SetTableCellBorders(cell_borders);
   builder.SetTableCellAlignmentBaseline(alignment_baseline);
-  builder.SetTableCellColumnIndex(column_index);
+  builder.SetTableCellColumnIndex(start_column);
   builder.SetIsRestrictedBlockSizeTableCell(
       is_table_block_size_specified || cell_style.LogicalHeight().IsFixed());
   builder.SetIsTableCellHiddenForPaint(is_hidden_for_paint);
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
index d09a7ee3..0337dc7c 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
@@ -16,7 +16,6 @@
 class NGConstraintSpaceBuilder;
 class NGTableBorders;
 enum class NGCacheSlot;
-struct LogicalSize;
 
 // Table size distribution algorithms.
 class NGTableAlgorithmUtils {
@@ -38,13 +37,13 @@
       const WritingDirectionMode table_writing_direction,
       const NGBlockNode cell,
       const NGBoxStrut& cell_borders,
-      LogicalSize cell_size,
+      const Vector<NGTableColumnLocation>& column_locations,
+      LayoutUnit cell_block_size,
       LayoutUnit percentage_inline_size,
       absl::optional<LayoutUnit> alignment_baseline,
-      wtf_size_t column_index,
+      wtf_size_t start_column,
       bool is_initial_block_size_indefinite,
       bool is_restricted_block_size_table,
-      bool is_hidden_for_paint,
       bool has_collapsed_borders,
       NGCacheSlot);
 
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
index 2567d68..bc6d162 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
@@ -29,40 +29,7 @@
                                        absl::optional<LayoutUnit> row_baseline,
                                        LayoutUnit* cell_inline_offset = nullptr,
                                        bool use_block_fragmentation = false) {
-    const wtf_size_t start_column = table_data.cells[cell_index].start_column;
-    const wtf_size_t end_column =
-        std::min(start_column + cell.TableCellColspan() - 1,
-                 table_data.column_locations.size() - 1);
-
-    // When columns spanned by the cell are collapsed, the cell geometry is
-    // defined by:
-    // - The start edge of the first non-collapsed column.
-    // - The end edge of the last non-collapsed column.
-    // - If all columns are collapsed, the |cell_inline_size| is defined by the
-    //   edges of the last column. Picking last column is arbitrary, any
-    //   spanned column would work, as all spanned columns define the same
-    //   geometry: same location, zero width.
-    wtf_size_t cell_location_start_column = start_column;
-    while (
-        table_data.column_locations[cell_location_start_column].is_collapsed &&
-        cell_location_start_column < end_column)
-      cell_location_start_column++;
-    wtf_size_t cell_location_end_column = end_column;
-    while (table_data.column_locations[cell_location_end_column].is_collapsed &&
-           cell_location_end_column > cell_location_start_column)
-      cell_location_end_column--;
-
-    if (cell_inline_offset) {
-      *cell_inline_offset =
-          table_data.column_locations[cell_location_start_column].offset;
-    }
-
-    const NGTableConstraintSpaceData::Cell& cell_data =
-        table_data.cells[cell_index];
-    const LayoutUnit cell_inline_size =
-        table_data.column_locations[cell_location_end_column].offset +
-        table_data.column_locations[cell_location_end_column].size -
-        table_data.column_locations[cell_location_start_column].offset;
+    const auto& cell_data = table_data.cells[cell_index];
     const LayoutUnit cell_block_size =
         cell_data.rowspan_block_size != kIndefiniteSize
             ? cell_data.rowspan_block_size
@@ -74,17 +41,17 @@
         cell_data.is_constrained ||
         (cell_data.has_grown && table_data.is_table_block_size_specified);
 
-    const bool is_hidden_for_paint =
-        table_data.column_locations[cell_location_start_column].is_collapsed &&
-        cell_location_start_column == cell_location_end_column;
+    const wtf_size_t start_column = cell_data.start_column;
+    if (cell_inline_offset)
+      *cell_inline_offset = table_data.column_locations[start_column].offset;
 
     NGConstraintSpaceBuilder builder =
         NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
             table_data.table_writing_direction, cell, cell_data.borders,
-            {cell_inline_size, cell_block_size},
+            table_data.column_locations, cell_block_size,
             container_builder_.InlineSize(), row_baseline, start_column,
             !is_initial_block_size_definite,
-            table_data.is_table_block_size_specified, is_hidden_for_paint,
+            table_data.is_table_block_size_specified,
             table_data.has_collapsed_borders, NGCacheSlot::kLayout);
 
     if (use_block_fragmentation) {
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 4e9296a..35d0d465 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -700,8 +700,9 @@
       // WebFrameLoadType::kBackForward.
       DCHECK(frame_load_type != WebFrameLoadType::kBackForward);
       return kSPANavTypeHistoryPushStateOrReplaceState;
-    case mojom::blink::SameDocumentNavigationType::kAppHistoryTransitionWhile:
-      return kSPANavTypeAppHistoryTransitionWhile;
+    case mojom::blink::SameDocumentNavigationType::
+        kNavigationApiTransitionWhile:
+      return kSPANavTypeNavigationApiTransitionWhile;
   }
   NOTREACHED();
   return kSPANavTypeSameDocumentBackwardOrForward;
@@ -818,9 +819,9 @@
   // NavigateEvent, the navigation will finish asynchronously, so
   // don't immediately call DidStopLoading() in that case.
   bool should_send_stop_notification =
-      !was_loading &&
-      same_document_navigation_type !=
-          mojom::blink::SameDocumentNavigationType::kAppHistoryTransitionWhile;
+      !was_loading && same_document_navigation_type !=
+                          mojom::blink::SameDocumentNavigationType::
+                              kNavigationApiTransitionWhile;
   if (should_send_stop_notification)
     GetFrameLoader().Progress().ProgressCompleted();
 
diff --git a/third_party/blink/renderer/core/loader/frame_loader_types.h b/third_party/blink/renderer/core/loader/frame_loader_types.h
index 0f62d75..0f05ece 100644
--- a/third_party/blink/renderer/core/loader/frame_loader_types.h
+++ b/third_party/blink/renderer/core/loader/frame_loader_types.h
@@ -53,7 +53,7 @@
   kSPANavTypeHistoryPushStateOrReplaceState = 0,
   kSPANavTypeSameDocumentBackwardOrForward = 1,
   kSPANavTypeOtherFragmentNavigation = 2,
-  kSPANavTypeAppHistoryTransitionWhile = 3,
+  kSPANavTypeNavigationApiTransitionWhile = 3,
   kSPANavTypeCount
 };
 
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.cc b/third_party/blink/renderer/core/mathml/mathml_element.cc
index 4f011986..bed7025 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_element.cc
@@ -38,6 +38,9 @@
 }
 
 bool MathMLElement::IsPresentationAttribute(const QualifiedName& name) const {
+  if (!RuntimeEnabledFeatures::MathMLCoreEnabled())
+    return Element::IsPresentationAttribute(name);
+
   if (name == html_names::kDirAttr || name == mathml_names::kMathsizeAttr ||
       name == mathml_names::kMathcolorAttr ||
       name == mathml_names::kMathbackgroundAttr ||
@@ -76,6 +79,11 @@
     const QualifiedName& name,
     const AtomicString& value,
     MutableCSSPropertyValueSet* style) {
+  if (!RuntimeEnabledFeatures::MathMLCoreEnabled()) {
+    Element::CollectStyleForPresentationAttribute(name, value, style);
+    return;
+  }
+
   if (name == html_names::kDirAttr) {
     if (IsValidDirAttribute(value)) {
       AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kDirection,
@@ -138,6 +146,11 @@
 }
 
 void MathMLElement::ParseAttribute(const AttributeModificationParams& param) {
+  if (!RuntimeEnabledFeatures::MathMLCoreEnabled()) {
+    Element::ParseAttribute(param);
+    return;
+  }
+
   const AtomicString& event_name =
       HTMLElement::EventNameForAttributeName(param.name);
   if (!event_name.IsNull()) {
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.h b/third_party/blink/renderer/core/mathml/mathml_element.h
index b927336..8c88493 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.h
+++ b/third_party/blink/renderer/core/mathml/mathml_element.h
@@ -32,6 +32,8 @@
 
   bool IsMathMLElement() const =
       delete;  // This will catch anyone doing an unnecessary check.
+  bool IsStyledElement() const =
+      delete;  // This will catch anyone doing an unnecessary check.
 
   virtual bool IsGroupingElement() const { return false; }
 
diff --git a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
index 440103b6..7a6503d 100644
--- a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
+++ b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
@@ -39,16 +39,14 @@
 // prevent the user from scaling the page as if user-scalable=no was set.
 static constexpr double kMaximumScalePreventsZoomingThreshold = 1.2;
 
-// Finding bad tap targets may takes too time for big page and should abort if
-// it takes more than 5ms.
+// Finding bad tap targets may costs too long time for big page and should abort
+// if it takes more than 5ms.
 static constexpr base::TimeDelta kTimeBudgetForBadTapTarget =
     base::Milliseconds(5);
 // Extracting tap targets phase is the major part of finding bad tap targets.
+// This phase will abort when it consumes more than 4ms.
 static constexpr base::TimeDelta kTimeBudgetForTapTargetExtraction =
     base::Milliseconds(4);
-// Checking clock itself is heavy on excessive call, skip checking by this
-// stride.
-constexpr int kTimeBudgetCheckStride = 32;
 static constexpr base::TimeDelta kEvaluationDelay = base::Milliseconds(3000);
 static constexpr base::TimeDelta kEvaluationInterval = base::Minutes(1);
 
@@ -77,8 +75,8 @@
 
 namespace {
 
-bool IsTimeBudgetExpired(const base::Time& from) {
-  return base::Time::Now() - from > kTimeBudgetForBadTapTarget;
+bool IsTimeBudgetExpired(const base::TimeTicks& from) {
+  return base::TimeTicks::Now() - from > kTimeBudgetForBadTapTarget;
 }
 
 // Fenwick tree is a data structure which can efficiently update elements and
@@ -219,21 +217,18 @@
     const LocalFrameView& frame_view,
     int finger_radius,
     Vector<int>& x_positions,
-    const base::Time& started,
+    const base::TimeTicks& started,
     Vector<std::pair<int, EdgeOrCenter>>& vertices) {
   LayoutObject* const root =
       frame_view.GetFrame().GetDocument()->GetLayoutView();
   WTF::HashSet<Member<const LayoutObject>> tap_targets;
 
-  int object_count = 0;
   // Simultaneously iterate front-to-back and back-to-front to consider
   // both page headers and footers using the same time budget.
   for (const LayoutObject *forward = root, *backward = root;
        forward && backward;) {
-    if ((++object_count % kTimeBudgetCheckStride) == 0 &&
-        base::Time::Now() - started > kTimeBudgetForTapTargetExtraction) {
+    if (base::TimeTicks::Now() - started > kTimeBudgetForTapTargetExtraction)
       return static_cast<int>(tap_targets.size());
-    }
 
     blink::GetRootNodeOptions options;
     if (forward->GetNode() != nullptr &&
@@ -309,7 +304,7 @@
 // Returns kTimeBudgetExceeded if time limit exceeded.
 int CountBadTapTargets(wtf_size_t rightmost_position,
                        const Vector<std::pair<int, EdgeOrCenter>>& vertices,
-                       const base::Time& started) {
+                       const base::TimeTicks& started) {
   FenwickTree tree(rightmost_position);
   int bad_tap_targets = 0;
   for (const auto& it : vertices) {
@@ -348,7 +343,7 @@
 // go/bad-tap-target-ukm
 int MobileFriendlinessChecker::ComputeBadTapTargetsRatio() {
   DCHECK(frame_view_->GetFrame().IsLocalRoot());
-  base::Time started = base::Time::Now();
+  base::TimeTicks started = base::TimeTicks::Now();
   constexpr float kOneDipInMm = 0.15875;
   double initial_scale = frame_view_->GetPage()
                              ->GetPageScaleConstraintsSet()
@@ -381,7 +376,7 @@
 
     all_tap_targets += got_tap_targets;
 
-    if (base::Time::Now() - started > kTimeBudgetForTapTargetExtraction)
+    if (base::TimeTicks::Now() - started > kTimeBudgetForTapTargetExtraction)
       break;
   }
   if (all_tap_targets == 0)
diff --git a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc
index 0ff8fa1..72bd024 100644
--- a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc
+++ b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker_test.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.h"
 
-#include "base/time/time_override.h"
+#include "base/time/time.h"
 #include "third_party/blink/public/common/mobile_metrics/mobile_friendliness.h"
 #include "third_party/blink/public/mojom/mobile_metrics/mobile_friendliness.mojom-shared.h"
 #include "third_party/blink/public/web/web_settings.h"
@@ -44,11 +44,40 @@
     "png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4/"
     "/8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
 
+class ScopedTimeTicksOverride {
+ public:
+  explicit ScopedTimeTicksOverride(bool fixed) {
+    if (fixed) {
+      time_clock_overrides_ =
+          std::make_unique<base::subtle::ScopedTimeClockOverrides>(
+              nullptr, &ScopedTimeTicksOverride::FixedTicks, nullptr);
+    } else {
+      time_clock_overrides_ =
+          std::make_unique<base::subtle::ScopedTimeClockOverrides>(
+              nullptr, &ScopedTimeTicksOverride::BoostedTicks, nullptr);
+    }
+  }
+
+  static base::TimeTicks FixedTicks() {
+    static base::TimeTicks now = base::subtle::TimeTicksNowIgnoringOverride();
+    return now;
+  }
+  static base::TimeTicks BoostedTicks() {
+    static base::TimeTicks now = base::subtle::TimeTicksNowIgnoringOverride();
+    now += base::Microseconds(100);
+    return now;
+  }
+
+ private:
+  std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_clock_overrides_;
+};
+
 class MobileFriendlinessCheckerTest : public testing::Test {
   static void EvalMobileFriendliness(LocalFrameView* view,
-                                     int scroll_y_offset) {
+                                     int scroll_y_offset,
+                                     bool fixed_clock) {
     DCHECK(view->GetFrame().IsLocalRoot());
-
+    ScopedTimeTicksOverride clock(fixed_clock);
     for (const Frame* frame = &view->GetFrame(); frame;
          frame = frame->Tree().TraverseNext()) {
       if (const auto* local_frame = DynamicTo<LocalFrame>(frame)) {
@@ -98,7 +127,8 @@
 
   MobileFriendliness CalculateMetricsForHTMLString(const std::string& html,
                                                    float device_scale = 1.0,
-                                                   int scroll_y_offset = 0) {
+                                                   int scroll_y_offset = 0,
+                                                   bool fixed_clock = true) {
     MFTestWebFrameClient web_frame_client;
     {
       std::unique_ptr<frame_test_helpers::WebViewHelper> helper(
@@ -108,14 +138,15 @@
           url_test_helpers::ToKURL("about:blank"));
       EvalMobileFriendliness(
           helper->GetWebView()->MainFrameImpl()->GetFrameView(),
-          scroll_y_offset);
+          scroll_y_offset, fixed_clock);
     }
     return web_frame_client.GetMobileFriendliness();
   }
 
   MobileFriendliness CalculateMetricsForFile(const std::string& path,
                                              float device_scale = 1.0,
-                                             int scroll_y_offset = 0) {
+                                             int scroll_y_offset = 0,
+                                             bool fixed_clock = true) {
     MFTestWebFrameClient web_frame_client;
     {
       std::unique_ptr<frame_test_helpers::WebViewHelper> helper(
@@ -127,30 +158,12 @@
                                     kBaseUrl + path);
       EvalMobileFriendliness(
           helper->GetWebView()->MainFrameImpl()->GetFrameView(),
-          scroll_y_offset);
+          scroll_y_offset, fixed_clock);
     }
     return web_frame_client.GetMobileFriendliness();
   }
 };
 
-class ClockFixedMobileFriendlinessCheckerTest
-    : public MobileFriendlinessCheckerTest {
- public:
-  void SetUp() override {
-    clock_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>(
-        []() {
-          // Returns fixed mock time to avoid BadTapTargetRatio hits
-          // timeout.
-          static base::Time start = base::subtle::TimeNowIgnoringOverride();
-          return start;
-        },
-        nullptr, nullptr);
-  }
-
- protected:
-  std::unique_ptr<base::subtle::ScopedTimeClockOverrides> clock_override_;
-};
-
 TEST_F(MobileFriendlinessCheckerTest, NoViewportSetting) {
   MobileFriendliness actual_mf =
       CalculateMetricsForHTMLString("<body>bar</body>");
@@ -230,7 +243,7 @@
   EXPECT_EQ(actual_mf.small_text_ratio, 100);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, NoText) {
+TEST_F(MobileFriendlinessCheckerTest, NoText) {
   MobileFriendliness actual_mf =
       CalculateMetricsForHTMLString(R"(<body></body>)");
   EXPECT_EQ(actual_mf.viewport_device_width, false);
@@ -857,7 +870,7 @@
   EXPECT_EQ(actual_mf.text_content_outside_viewport_percentage, 0.0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, SingleTapTarget) {
+TEST_F(MobileFriendlinessCheckerTest, SingleTapTarget) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -873,7 +886,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, TwoImageTapTargetsClose) {
+TEST_F(MobileFriendlinessCheckerTest, TwoImageTapTargetsClose) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(
       base::StringPrintf(R"(
 <html>
@@ -891,7 +904,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, TwoImageTapTargetsFar) {
+TEST_F(MobileFriendlinessCheckerTest, TwoImageTapTargetsFar) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(
       base::StringPrintf(R"(
 <html>
@@ -910,7 +923,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, NoBadTapTarget) {
+TEST_F(MobileFriendlinessCheckerTest, NoBadTapTarget) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -929,8 +942,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest,
-       NoBadTapTargetWithDeviceScaleFactor) {
+TEST_F(MobileFriendlinessCheckerTest, NoBadTapTargetWithDeviceScaleFactor) {
   MobileFriendliness actual_mf =
       CalculateMetricsForHTMLString(R"(
 <html>
@@ -951,8 +963,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest,
-       BadTapTargetWithDeviceScaleFactor) {
+TEST_F(MobileFriendlinessCheckerTest, BadTapTargetWithDeviceScaleFactor) {
   MobileFriendliness actual_mf =
       CalculateMetricsForHTMLString(R"(
 <html>
@@ -973,7 +984,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, BadTapTargetWithAutoZoomOut) {
+TEST_F(MobileFriendlinessCheckerTest, BadTapTargetWithAutoZoomOut) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <body style="font-size: 18px">
@@ -990,7 +1001,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, TooCloseTapTargetsVertical) {
+TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsVertical) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1013,8 +1024,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest,
-       TooCloseTapTargetsVerticalEventListener) {
+TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsVerticalEventListener) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1034,8 +1044,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest,
-       TooCloseTapTargetsVerticalSamePoint) {
+TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsVerticalSamePoint) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1063,7 +1072,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 34);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, TooCloseTapTargetsHorizontal) {
+TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsHorizontal) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1086,8 +1095,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest,
-       TooCloseTapTargetsHorizontalSamePoint) {
+TEST_F(MobileFriendlinessCheckerTest, TooCloseTapTargetsHorizontalSamePoint) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1115,7 +1123,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 34);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, GridGoodTargets3X3) {
+TEST_F(MobileFriendlinessCheckerTest, GridGoodTargets3X3) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1179,7 +1187,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, GridBadTargets3X3) {
+TEST_F(MobileFriendlinessCheckerTest, GridBadTargets3X3) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1243,7 +1251,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, FormTapTargets) {
+TEST_F(MobileFriendlinessCheckerTest, FormTapTargets) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1260,8 +1268,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 50);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest,
-       InvisibleTapTargetWillBeIgnored) {
+TEST_F(MobileFriendlinessCheckerTest, InvisibleTapTargetWillBeIgnored) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1280,8 +1287,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest,
-       BadTapTargetWithPositionAbsolute) {
+TEST_F(MobileFriendlinessCheckerTest, BadTapTargetWithPositionAbsolute) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1300,17 +1306,9 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, TapTargetTimeout) {
-  clock_override_.reset();
-  clock_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>(
-      []() {
-        // Time::Now() progress 1 ms stride for every check to force timeout.
-        static base::Time now = base::subtle::TimeNowIgnoringOverride();
-        now += base::Milliseconds(1);
-        return now;
-      },
-      nullptr, nullptr);
-  MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
+TEST_F(MobileFriendlinessCheckerTest, TapTargetTimeout) {
+  MobileFriendliness actual_mf =
+      CalculateMetricsForHTMLString(R"(
 <html>
   <head>
     <meta name="viewport" content="width=480, initial-scale=1">
@@ -1336,11 +1334,14 @@
     </button>
   </body>
 </html>
-)");
+)",
+                                    /*device_scale=*/1.0,
+                                    /*scroll_y_offset=*/0,
+                                    /*fixed_clock=*/false);
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, -2);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, TapTargetPositionFixed) {
+TEST_F(MobileFriendlinessCheckerTest, TapTargetPositionFixed) {
   MobileFriendliness actual_mf = CalculateMetricsForHTMLString(R"(
 <html>
   <head>
@@ -1355,7 +1356,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 100);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameTest) {
+TEST_F(MobileFriendlinessCheckerTest, IFrameTest) {
   url_test_helpers::RegisterMockedURLLoadFromBase(
       WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(),
       WebString::FromUTF8("visible_iframe.html"));
@@ -1366,7 +1367,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameVieportDeviceWidth) {
+TEST_F(MobileFriendlinessCheckerTest, IFrameVieportDeviceWidth) {
   url_test_helpers::RegisterMockedURLLoadFromBase(
       WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(),
       WebString::FromUTF8("viewport/viewport-1.html"));
@@ -1378,7 +1379,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameSmallTextRatio) {
+TEST_F(MobileFriendlinessCheckerTest, IFrameSmallTextRatio) {
   url_test_helpers::RegisterMockedURLLoadFromBase(
       WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(),
       WebString::FromUTF8("small_text_iframe.html"));
@@ -1390,7 +1391,7 @@
   EXPECT_EQ(actual_mf.bad_tap_targets_ratio, 0);
 }
 
-TEST_F(ClockFixedMobileFriendlinessCheckerTest, IFrameBadTapTargetsRatio) {
+TEST_F(MobileFriendlinessCheckerTest, IFrameBadTapTargetsRatio) {
   url_test_helpers::RegisterMockedURLLoadFromBase(
       WebString::FromUTF8(kBaseUrl), blink::test::CoreTestDataPath(),
       WebString::FromUTF8("bad_tap_targets_iframe.html"));
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
index f00b509..d6050ca 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -470,7 +470,7 @@
     if (options->hasState()) {
       ExceptionState exception_state(
           script_state->GetIsolate(),
-          ExceptionContext::Context::kOperationInvoke, "AppHistory",
+          ExceptionContext::Context::kOperationInvoke, "Navigation",
           "navigate");
       serialized_state = SerializeState(options->state(), exception_state);
       if (exception_state.HadException()) {
@@ -501,7 +501,7 @@
     if (options->hasState()) {
       ExceptionState exception_state(
           script_state->GetIsolate(),
-          ExceptionContext::Context::kOperationInvoke, "AppHistory", "reload");
+          ExceptionContext::Context::kOperationInvoke, "Navigation", "reload");
       serialized_state = SerializeState(options->state(), exception_state);
       if (exception_state.HadException()) {
         NavigationResult* result =
@@ -559,7 +559,7 @@
                                             const String& key,
                                             NavigationOptions* options) {
   if (DOMException* maybe_ex =
-          PerformSharedNavigationChecks("goTo()/back()/forward()")) {
+          PerformSharedNavigationChecks("traverseTo()/back()/forward()")) {
     return EarlyErrorResult(script_state, maybe_ex);
   }
 
@@ -778,7 +778,7 @@
     // steps. Instead it does stuff like the loading spinner and use counters.
     GetSupplementable()->document()->Loader()->RunURLAndHistoryUpdateSteps(
         url, destination_item,
-        mojom::blink::SameDocumentNavigationType::kAppHistoryTransitionWhile,
+        mojom::blink::SameDocumentNavigationType::kNavigationApiTransitionWhile,
         state_object, type, is_browser_initiated, is_synchronously_committed);
   }
 
diff --git a/third_party/blink/renderer/core/page/focusgroup_controller_test.cc b/third_party/blink/renderer/core/page/focusgroup_controller_test.cc
index 2d1a7a1c..d5771768 100644
--- a/third_party/blink/renderer/core/page/focusgroup_controller_test.cc
+++ b/third_party/blink/renderer/core/page/focusgroup_controller_test.cc
@@ -42,53 +42,6 @@
         event);
   }
 
-  void AssertForwardDoesntMoveFocusWhenOutOfFocusgroup(int key);
-  void AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot(int key);
-  void AssertForwardDoesntMoveWhenOnNonFocusgroupItem(int key);
-  void AssertForwardMovesToNextItem(int key);
-  void AssertForwardDoesntMoveWhenOnlyOneItem(int key);
-  void AssertForwardDoesntMoveWhenOnlyOneItemAndWraps(int key);
-  void AssertForwardSkipsNonFocusableItems(int key);
-  void AssertForwardMovesInExtendingFocusgroup(int key);
-  void AssertForwardExitsExtendingFocusgroup(int key);
-  void AssertForwardMovesToNextElementWithinDescendants(int key);
-  void AssertForwardDoesntMoveFocusWhenAxisNotSupported(int key);
-  void AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key);
-  void AssertForwardSkipsExtendingFocusgroup(int key);
-  void AssertForwardDoesntWrapWhenNotSupported(int key);
-  void AssertForwardDoesntWrapEvenWhenOtherAxisSupported(int key);
-  void AssertForwardDoesntWrapInFocusgroupWithoutItems(int key);
-  void AssertForwardWrapsSuccessfully(int key);
-  void AssertForwardWrapsToParentFocusgroup(int key);
-  void AssertForwardWrapsInInnerFocusgroupOnly(int key);
-  void AssertForwardWrapsInExpectedScope(int key);
-  void AssertForwardWrapsAndGoesInInnerFocusgroup(int key);
-  void AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(int key);
-
-  void AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup(int key);
-  void AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(int key);
-  void AssertBackwardDoesntMoveWhenOnNonFocusgroupItem(int key);
-  void AssertBackwardMovesFocusToPreviousItem(int key);
-  void AssertBackwardSkipsNonFocusableItems(int key);
-  void AssertBackwardDoesntMoveWhenOnlyOneItem(int key);
-  void AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps(int key);
-  void AssertBackwardDoesntMoveFocusAxisNotSupported(int key);
-  void AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key);
-  void AssertBackwardDescendIntoExtendingFocusgroup(int key);
-  void AssertBackwardSkipsNonFocusgroupSubtree(int key);
-  void AssertBackwardSkipsOrthogonalFocusgroup(int key);
-  void AssertBackwardSkipsRootFocusgroup(int key);
-  void AssertBackwardSkipsEmptyWrappingFocusgroup(int key);
-  void AssertBackwardSkipsRootFocusgroupComplexCase(int key);
-  void AssertBackwardSkipsOrthogonalFocusgroupComplexCase(int key);
-  void AssertBackwardAscendsToParentFocusgroup(int key);
-  void AssertBackwardDoesntWrapWhenNotSupported(int key);
-  void AssertBackwardWrapsSuccessfully(int key);
-  void AssertBackwardWrapsSuccessfullyInAxis(int key);
-  void AssertBackwardDoesntWrapInOrthogonalAxis(int key);
-  void AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(int key);
-  void AssertBackwardWrapsSuccessfullyInComplexCase(int key);
-
  private:
   void SetUp() override { PageTestBase::SetUp(gfx::Size()); }
 
@@ -671,11 +624,20 @@
 // FORWARD NAVIGATION - DOWN ARROW & RIGHT ARROW
 // *****************************************************************************
 
+class FocusgroupControllerForwardNavigationTest
+    : public FocusgroupControllerTest,
+      public ::testing::WithParamInterface<int> {};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         FocusgroupControllerForwardNavigationTest,
+                         testing::Values(ui::DomKey::ARROW_DOWN,
+                                         ui::DomKey::ARROW_RIGHT));
+
 // When the focus is set on an element outside of the focusgroup, an arrow key
 // press shouldn't move the focus at all.
-void FocusgroupControllerTest::AssertForwardDoesntMoveFocusWhenOutOfFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       DoesntMoveFocusWhenOutOfFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <span id=out tabindex=-1></span>
     <div focusgroup>
@@ -695,19 +657,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), out);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveFocusWhenOutOfFocusgroup) {
-  AssertForwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveFocusWhenOutOfFocusgroup) {
-  AssertForwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the root of a focusgroup element, an arrow key press
 // shouldn't move the focus at all.
-void FocusgroupControllerTest::AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       DoesntMoveFocusWhenOnFocusgroupRoot) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root tabindex=-1 focusgroup>
       <span id=item1 tabindex=0></span>
@@ -726,20 +680,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), root);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveFocusWhenOnFocusgroupRoot) {
-  AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowRightDoesntMoveFocusWhenOnFocusgroupRoot) {
-  AssertForwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on a focusable element that isn't a focusgroup item, an
 // arrow key press shouldn't move the focus at all.
-void FocusgroupControllerTest::AssertForwardDoesntMoveWhenOnNonFocusgroupItem(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       DoesntMoveWhenOnNonFocusgroupItem) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div tabindex=-1 focusgroup>
       <div>
@@ -761,18 +706,10 @@
   ASSERT_EQ(GetDocument().FocusedElement(), nonitem1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveWhenOnNonFocusgroupItem) {
-  AssertForwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveWhenOnNonFocusgroupItem) {
-  AssertForwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on a focusgroup item, an arrow key press should move
 // the focus to the next item.
-void FocusgroupControllerTest::AssertForwardMovesToNextItem(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, MovesToNextItem) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
       <span id=item1 tabindex=0></span>
@@ -793,17 +730,10 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item2);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownMovesToNextItem) {
-  AssertForwardMovesToNextItem(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightMovesToNextItem) {
-  AssertForwardMovesToNextItem(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the only focusgroup item, the focus shouldn't move
 // and we shouldn't get stuck in an infinite loop.
-void FocusgroupControllerTest::AssertForwardDoesntMoveWhenOnlyOneItem(int key) {
+TEST_P(FocusgroupControllerForwardNavigationTest, DoesntMoveWhenOnlyOneItem) {
+  int key = GetParam();
   ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
@@ -822,20 +752,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveWhenOnlyOneItem) {
-  AssertForwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveWhenOnlyOneItem) {
-  AssertForwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the only focusgroup item and the focusgroup wraps in
 // the axis of the arrow key pressed, the focus shouldn't move and we shouldn't
 // get stuck in an infinite loop.
-void FocusgroupControllerTest::AssertForwardDoesntMoveWhenOnlyOneItemAndWraps(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       DoesntMoveWhenOnlyOneItemAndWraps) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -853,18 +775,10 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveWhenOnlyOneItemAndWraps) {
-  AssertForwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightDoesntMoveWhenOnlyOneItemAndWraps) {
-  AssertForwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on a focusgroup item, an arrow key press should move
 // the focus to the next item and skip non-focusable items.
-void FocusgroupControllerTest::AssertForwardSkipsNonFocusableItems(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, SkipsNonFocusableItems) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
       <span id=item1 tabindex=0></span>
@@ -886,20 +800,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownSkipsNonFocusableItems) {
-  AssertForwardSkipsNonFocusableItems(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightSkipsNonFocusableItems) {
-  AssertForwardSkipsNonFocusableItems(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on a focusgroup item which happens to also be an
 // extending focusgroup, an arrow key press should move the focus to the next
 // item within the extending focusgroup and skip non-focusable items.
-void FocusgroupControllerTest::AssertForwardMovesInExtendingFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, MovesInExtendingFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
       <div id=item1 tabindex=0 focusgroup=extend>
@@ -924,22 +829,14 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownMovesInExtendingFocusgroup) {
-  AssertForwardMovesInExtendingFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightMovesInExtendingFocusgroup) {
-  AssertForwardMovesInExtendingFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on a focusgroup item which happens to also be an
 // extending focusgroup, an arrow key press should move the focus to the next
 // item within the extending focusgroup and skip non-focusable items. If no
 // valid candidate is found within that extending focusgroup, the next element
 // (in pre-order traversal) should be considered. In this case, |item4| is the
 // valid next candidate.
-void FocusgroupControllerTest::AssertForwardExitsExtendingFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, ExitsExtendingFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
       <div id=item1 tabindex=0 focusgroup=extend>
@@ -963,20 +860,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item4);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownExitsExtendingFocusgroup) {
-  AssertForwardExitsExtendingFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightExitsExtendingFocusgroup) {
-  AssertForwardExitsExtendingFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on a focusgroup item that is an ancestor to an
 // extending focusgroup, the focus should move to the next element inside that
 // extending focusgroup even if it's not a direct child.
-void FocusgroupControllerTest::AssertForwardMovesToNextElementWithinDescendants(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       MovesToNextElementWithinDescendants) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
       <div id=item1 tabindex=0>
@@ -1002,20 +891,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item2);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownMovesToNextElementWithinDescendants) {
-  AssertForwardMovesToNextElementWithinDescendants(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowRightMovesToNextElementWithinDescendants) {
-  AssertForwardMovesToNextElementWithinDescendants(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on an item of a focusgroup that only supports the
 // orthogonal axis to the arrow key pressed, the arrow pressed shouldn't move
 // the focus.
-void FocusgroupControllerTest::AssertForwardDoesntMoveFocusWhenAxisNotSupported(
-    int key) {
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       DoesntMoveFocusWhenAxisNotSupported) {
+  int key = GetParam();
   ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
   if (key == ui::DomKey::ARROW_DOWN) {
     // Arrow in the vertical axis, set the test to support only horizontal.
@@ -1046,20 +927,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntMoveFocusWhenAxisNotSupported) {
-  AssertForwardDoesntMoveFocusWhenAxisNotSupported(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowRightDoesntMoveFocusWhenAxisNotSupported) {
-  AssertForwardDoesntMoveFocusWhenAxisNotSupported(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on an item of a focusgroup that only supports the
 // axis of the arrow key pressed the focus should move.
-void FocusgroupControllerTest::
-    AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       MovesFocusWhenInArrowAxisOnlyFocusgroup) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_DOWN) {
     // The arrow is in the vertical axis, so the focusgroup should support only
     // the vertical axis.
@@ -1093,21 +965,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item2);
 }
 
-TEST_F(FocusgroupControllerTest,
-       ArrowDownMovesFocusWhenInArrowAxisOnlyFocusgroup) {
-  AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowRightMovesFocusWhenInArrowAxisOnlyFocusgroup) {
-  AssertForwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on an extending focusgroup element but that focusgroup
 // doesn't support the axis of the arrow key pressed, skip that subtree
 // altogether.
-void FocusgroupControllerTest::AssertForwardSkipsExtendingFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, SkipsExtendingFocusgroup) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_DOWN) {
     // The arrow is in the vertical axis, so the extending focusgroup should
     // support only the horizontal axis.
@@ -1146,19 +1008,10 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownSkipsExtendingFocusgroup) {
-  AssertForwardSkipsExtendingFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightSkipsExtendingFocusgroup) {
-  AssertForwardSkipsExtendingFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of a focusgroup that doesn't support
 // wrapping in the axis of the arrow key pressed, the focus shouldn't move.
-void FocusgroupControllerTest::AssertForwardDoesntWrapWhenNotSupported(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, DoesntWrapWhenNotSupported) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
       <span id=item1 tabindex=0></span>
@@ -1177,20 +1030,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item2);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntWrapWhenNotSupported) {
-  AssertForwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightDoesntWrapWhenNotSupported) {
-  AssertForwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of a focusgroup that doesn't support
 // wrapping in the axis of the arrow key pressed but supports wrapping in the
 // orthogonal axis, the focus shouldn't move.
-void FocusgroupControllerTest::
-    AssertForwardDoesntWrapEvenWhenOtherAxisSupported(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       DoesntWrapEvenWhenOtherAxisSupported) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_DOWN) {
     // The arrow is in the vertical axis, so the focusgroup that wraps should be
     // in the horizontal axis only.
@@ -1231,23 +1076,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item4);
 }
 
-TEST_F(FocusgroupControllerTest,
-       ArrowDownDoesntWrapEvenWhenOtherAxisSupported) {
-  AssertForwardDoesntWrapEvenWhenOtherAxisSupported(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowRightDoesntWrapEvenWhenOtherAxisSupported) {
-  AssertForwardDoesntWrapEvenWhenOtherAxisSupported(ui::DomKey::ARROW_RIGHT);
-}
-
 // This test validates that we don't get stuck in an infinite loop searching for
 // a focusable element in the extending focusgroup that wraps that doesn't
 // contain one. Wrapping should only be allowed in the focusgroup that contains
 // the focusable element we started on or in one of its ancestors.
-void FocusgroupControllerTest::AssertForwardDoesntWrapInFocusgroupWithoutItems(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       DoesntWrapInFocusgroupWithoutItems) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup>
       <span id=item1 tabindex=0></span>
@@ -1274,19 +1109,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item4);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownDoesntWrapInFocusgroupWithoutItems) {
-  AssertForwardDoesntWrapInFocusgroupWithoutItems(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightDoesntWrapInFocusgroupWithoutItems) {
-  AssertForwardDoesntWrapInFocusgroupWithoutItems(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of a focusgroup that supports wrapping
 // in the axis of the arrow key pressed, the focus should move back to the first
 // item.
-void FocusgroupControllerTest::AssertForwardWrapsSuccessfully(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, WrapsSuccessfully) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -1307,19 +1134,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownWrapsSuccessfully) {
-  AssertForwardWrapsSuccessfully(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightWrapsSuccessfully) {
-  AssertForwardWrapsSuccessfully(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of an inner focusgroup that supports
 // wrapping while its parent focusgroup also does, the focus should move to the
 // first item of the parent focusgroup.
-void FocusgroupControllerTest::AssertForwardWrapsToParentFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, WrapsToParentFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -1343,20 +1162,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownWrapsToParentFocusgroup) {
-  AssertForwardWrapsToParentFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightWrapsToParentFocusgroup) {
-  AssertForwardWrapsToParentFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of an inner focusgroup that supports
 // wrapping while its parent focusgroup doesn't (in the axis of the arrow key
 // pressed), the focus should move to the first item of the inner focusgroup.
-void FocusgroupControllerTest::AssertForwardWrapsInInnerFocusgroupOnly(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, WrapsInInnerFocusgroupOnly) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_DOWN) {
     // The arrow key is in the vertical axis, so the outer focusgroup should
     // only support the horizontal axis.
@@ -1400,21 +1210,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownWrapsInInnerFocusgroupOnly) {
-  AssertForwardWrapsInInnerFocusgroupOnly(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightWrapsInInnerFocusgroupOnly) {
-  AssertForwardWrapsInInnerFocusgroupOnly(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of an inner focusgroup that supports
 // wrapping while its parent focusgroup doesn't (in the axis of the arrow key
 // pressed), the focus should move to the first item of the inner focusgroup
 // even if there's another focusgroup supporting wrapping in the same axis as
 // the arrow key pressed in the hierarchy.
-void FocusgroupControllerTest::AssertForwardWrapsInExpectedScope(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest, WrapsInExpectedScope) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_DOWN) {
     // The arrow key supports the vertical axis, so the outer focusgroup should
     // only support horizontal wrapping.
@@ -1462,21 +1264,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownWrapsInExpectedScope) {
-  AssertForwardWrapsInExpectedScope(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightWrapsInExpectedScope) {
-  AssertForwardWrapsInExpectedScope(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of a focusgroup that supports
 // wrapping in the axis of the arrow key pressed and the first item is in an
 // inner focusgroup that supports it too, the focus moves to that item in the
 // inner focusgroup.
-void FocusgroupControllerTest::AssertForwardWrapsAndGoesInInnerFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       WrapsAndGoesInInnerFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root focusgroup=wrap>
       <div focusgroup=extend>
@@ -1500,21 +1294,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowDownWrapsAndGoesInInnerFocusgroup) {
-  AssertForwardWrapsAndGoesInInnerFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowRightWrapsAndGoesInInnerFocusgroup) {
-  AssertForwardWrapsAndGoesInInnerFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // When the focus is set on the last item of a focusgroup that supports
 // wrapping in the axis of the arrow key pressed and the first item is in an
 // inner focusgroup that doesn't support wrapping in the same axis, the focus
 // moves to the next item out of that inner focusgroup.
-void FocusgroupControllerTest::
-    AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_DOWN || key == ui::DomKey::ARROW_RIGHT);
+TEST_P(FocusgroupControllerForwardNavigationTest,
+       WrapsAndSkipsOrthogonalInnerFocusgroup) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_DOWN) {
     // The arrow key is in the vertical axis, so the inner focusgroup should
     // only support the horizontal one.
@@ -1557,25 +1343,24 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest,
-       ArrowDownWrapsAndSkipsOrthogonalInnerFocusgroup) {
-  AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(ui::DomKey::ARROW_DOWN);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowRightWrapsAndSkipsOrthogonalInnerFocusgroup) {
-  AssertForwardWrapsAndSkipsOrthogonalInnerFocusgroup(ui::DomKey::ARROW_RIGHT);
-}
-
 // *****************************************************************************
-// FORWARD NAVIGATION - UP ARROW & LEFT ARROW
+// BACKWARD NAVIGATION - UP ARROW & LEFT ARROW
 // *****************************************************************************
 
+class FocusgroupControllerBackwardNavigationTest
+    : public FocusgroupControllerTest,
+      public ::testing::WithParamInterface<int> {};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         FocusgroupControllerBackwardNavigationTest,
+                         testing::Values(ui::DomKey::ARROW_UP,
+                                         ui::DomKey::ARROW_LEFT));
+
 // When the focus is set on an element outside of the focusgroup, an arrow key
 // press shouldn't move the focus at all.
-void FocusgroupControllerTest::AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntMoveFocusWhenOutOfFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <span id=out tabindex=-1></span>
     <div focusgroup>
@@ -1595,19 +1380,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), out);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveFocusWhenOutOfFocusgroup) {
-  AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveFocusWhenOutOfFocusgroup) {
-  AssertBackwardDoesntMoveFocusWhenOutOfFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the root of a focusgroup element, an arrow key press
 // shouldn't move the focus at all.
-void FocusgroupControllerTest::
-    AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntMoveFocusWhenOnFocusgroupRoot) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div id=root tabindex=-1 focusgroup>
       <span id=item1 tabindex=0></span>
@@ -1626,19 +1403,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), root);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveFocusWhenOnFocusgroupRoot) {
-  AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveFocusWhenOnFocusgroupRoot) {
-  AssertBackwardDoesntMoveFocusWhenOnFocusgroupRoot(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on a focusable element that isn't a focusgroup item, an
 // arrow key press shouldn't move the focus at all.
-void FocusgroupControllerTest::AssertBackwardDoesntMoveWhenOnNonFocusgroupItem(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntMoveWhenOnNonFocusgroupItem) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div tabindex=-1 focusgroup>
       <div>
@@ -1660,18 +1429,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), nonitem1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveWhenOnNonFocusgroupItem) {
-  AssertBackwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveWhenOnNonFocusgroupItem) {
-  AssertBackwardDoesntMoveWhenOnNonFocusgroupItem(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last element of a focusgroup, a backward key
 // press should move the focus to the previous item.
-void FocusgroupControllerTest::AssertBackwardMovesFocusToPreviousItem(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       MovesFocusToPreviousItem) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup>
       <span id=item1 tabindex=0></span>
@@ -1692,19 +1454,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpMovesFocusToPreviousItem) {
-  AssertBackwardMovesFocusToPreviousItem(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftMovesFocusToPreviousItem) {
-  AssertBackwardMovesFocusToPreviousItem(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last element of a focusgroup, a backward key
 // press should move the focus to the previous item, skipping any non-focusable
 // element.
-void FocusgroupControllerTest::AssertBackwardSkipsNonFocusableItems(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       SkipsNonFocusableItems) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup>
       <span id=item1 tabindex=0></span>
@@ -1727,20 +1482,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpSkipsNonFocusableItems) {
-  AssertBackwardSkipsNonFocusableItems(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftSkipsNonFocusableItems) {
-  AssertBackwardSkipsNonFocusableItems(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the only element of a focusgroup that doesn't wrap,
 // a backward key press shouldn't move the focus and we shouldn't get stuck in
 // an infinite loop.
-void FocusgroupControllerTest::AssertBackwardDoesntMoveWhenOnlyOneItem(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntMoveWhenOnlyOneItem) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup>
       <span id=item1 tabindex=0></span>
@@ -1758,20 +1505,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveWhenOnlyOneItem) {
-  AssertBackwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveWhenOnlyOneItem) {
-  AssertBackwardDoesntMoveWhenOnlyOneItem(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the only element of a focusgroup that wraps, a
 // backward key press shouldn't move the focus and we shouldn't get stuck in an
 // infinite loop.
-void FocusgroupControllerTest::AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntMoveWhenOnlyOneItemAndWraps) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -1789,19 +1528,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveWhenOnlyOneItemAndWraps) {
-  AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveWhenOnlyOneItemAndWraps) {
-  AssertBackwardDoesntMoveWhenOnlyOneItemAndWraps(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last element of a focusgroup that only supports
 // the orthogonal axis of the arrow key pressed, the focus shouldn't move.
-void FocusgroupControllerTest::AssertBackwardDoesntMoveFocusAxisNotSupported(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntMoveFocusAxisNotSupported) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_UP) {
     // The arrow is in the vertical axis, so the focusgroup should only
     // support the horizontal axis.
@@ -1833,20 +1564,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item2);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntMoveFocusAxisNotSupported) {
-  AssertBackwardDoesntMoveFocusAxisNotSupported(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntMoveFocusAxisNotSupported) {
-  AssertBackwardDoesntMoveFocusAxisNotSupported(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last element of a focusgroup that only supports
 // the axis of the arrow key pressed, the focus should move to the previous
 // item.
-void FocusgroupControllerTest::
-    AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       MovesFocusWhenInArrowAxisOnlyFocusgroup) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_UP) {
     // The arrow is in the vertical axis, so the focusgroup should only
     // support the vertical axis.
@@ -1880,22 +1603,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest,
-       ArrowUpMovesFocusWhenInArrowAxisOnlyFocusgroup) {
-  AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowLeftMovesFocusWhenInArrowAxisOnlyFocusgroup) {
-  AssertBackwardMovesFocusWhenInArrowAxisOnlyFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last item of a focusgroup and the previous item
 // is a descendant of a subtree, a backward arrow key press should move the
 // focus to that previous item within the subtree.
-void FocusgroupControllerTest::AssertBackwardDescendIntoExtendingFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DescendIntoExtendingFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -1923,20 +1636,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDescendIntoExtendingFocusgroup) {
-  AssertBackwardDescendIntoExtendingFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDescendIntoExtendingFocusgroup) {
-  AssertBackwardDescendIntoExtendingFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last item of a focusgroup and the previous item
 // is located past a non-focusgroup subtree, a backward arrow key press should
 // move the focus to that previous item without getting stuck in the subtree.
-void FocusgroupControllerTest::AssertBackwardSkipsNonFocusgroupSubtree(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       SkipsNonFocusgroupSubtree) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -1961,22 +1666,14 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpSkipsNonFocusgroupSubtree) {
-  AssertBackwardSkipsNonFocusgroupSubtree(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftSkipsNonFocusgroupSubtree) {
-  AssertBackwardSkipsNonFocusgroupSubtree(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last item of a focusgroup and the previous item
 // is a descendant of a subtree, a backward arrow key press should move the
 // focus to that previous item within the subtree. However, if that subtree is
 // an extending focusgroup that supports only the orthogonal axis, it should be
 // skipped.
-void FocusgroupControllerTest::AssertBackwardSkipsOrthogonalFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       SkipsOrthogonalFocusgroup) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_UP) {
     // The arrow is in the vertical axis, so the inner focusgroup should support
     // only the horizontal axis.
@@ -2023,20 +1720,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpSkipsOrthogonalFocusgroup) {
-  AssertBackwardSkipsOrthogonalFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftSkipsOrthogonalFocusgroup) {
-  AssertBackwardSkipsOrthogonalFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last item of a focusgroup and the previous item
 // is located past an other (non-extending) focusgroup subtree, a backward arrow
 // key press should move the focus to that previous item without getting stuck
 // in the other focusgroup.
-void FocusgroupControllerTest::AssertBackwardSkipsRootFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest, SkipsRootFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -2064,21 +1753,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpSkipsRootFocusgroup) {
-  AssertBackwardSkipsRootFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftSkipsRootFocusgroup) {
-  AssertBackwardSkipsRootFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last item of a focusgroup and the previous item
 // is located past an extending focusgroup that wraps but has no item in it, a
 // backward arrow key press should move the focus to that previous item without
 // getting stuck in the inner focusgroup.
-void FocusgroupControllerTest::AssertBackwardSkipsEmptyWrappingFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       SkipsEmptyWrappingFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -2106,22 +1787,14 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpSkipsEmptyWrappingFocusgroup) {
-  AssertBackwardSkipsEmptyWrappingFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftSkipsEmptyWrappingFocusgroup) {
-  AssertBackwardSkipsEmptyWrappingFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last item of a focusgroup and the previous item
 // is located past an other (non-extending) focusgroup subtree, a backward arrow
 // key press should move the focus to that previous item without getting stuck
 // in the other focusgroup. The same should still be true when inside a
 // focusgroup that extends a root focusgroup within the original focusgroup.
-void FocusgroupControllerTest::AssertBackwardSkipsRootFocusgroupComplexCase(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       SkipsRootFocusgroupComplexCase) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -2153,14 +1826,6 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpSkipsRootFocusgroupComplexCase) {
-  AssertBackwardSkipsRootFocusgroupComplexCase(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftSkipsRootFocusgroupComplexCase) {
-  AssertBackwardSkipsRootFocusgroupComplexCase(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the last item of a focusgroup and the previous item
 // is located past an extending focusgroup that only supports the orthogonal
 // axis, a backward arrow key press should move the focus to that previous item
@@ -2168,9 +1833,9 @@
 // The same should still be true when inside a focusgroup that extends another
 // extending focusgroup that supports only the orthogonal axis within the
 // original focusgroup.
-void FocusgroupControllerTest::
-    AssertBackwardSkipsOrthogonalFocusgroupComplexCase(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       SkipsOrthogonalFocusgroupComplexCase) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_UP) {
     // The arrow key is vertical, so the middle focusgroup should only support
     // the horizontal axis.
@@ -2225,22 +1890,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpSkipsOrthogonalFocusgroupComplexCase) {
-  AssertBackwardSkipsOrthogonalFocusgroupComplexCase(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowLeftSkipsOrthogonalFocusgroupComplexCase) {
-  AssertBackwardSkipsOrthogonalFocusgroupComplexCase(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the first item of an extending focusgroup that
 // doesn't support the axis of the arrow key pressed but the parent focusgroup
 // does, ascend to that focusgroup. This should work whether the extending
 // focusgroup is the child of the other focusgroup or a distant descendant.
-void FocusgroupControllerTest::AssertBackwardAscendsToParentFocusgroup(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       AscendsToParentFocusgroup) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_UP) {
     // The arrow key is vertical, so the inner focusgroup should only support
     // the horizontal axis and the outer one should only support the vertical
@@ -2292,18 +1948,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item2);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpAscendsToParentFocusgroup) {
-  AssertBackwardAscendsToParentFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftAscendsToParentFocusgroup) {
-  AssertBackwardAscendsToParentFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the first item of a focusgroup, a backward arrow key
 // press shouldn't move the focus since there aren't any previous item.
-void FocusgroupControllerTest::AssertBackwardDoesntWrapWhenNotSupported(
-    int key) {
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntWrapWhenNotSupported) {
+  int key = GetParam();
   ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup>
@@ -2323,19 +1972,11 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntWrapWhenNotSupported) {
-  AssertBackwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntWrapWhenNotSupported) {
-  AssertBackwardDoesntWrapWhenNotSupported(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the first item of a focusgroup that wraps, a
 // backward arrow key press should move the focus to the last item within the
 // focusgroup.
-void FocusgroupControllerTest::AssertBackwardWrapsSuccessfully(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest, WrapsSuccessfully) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <span id=item1 tabindex=0></span>
@@ -2357,19 +1998,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpWrapsSuccessfully) {
-  AssertBackwardWrapsSuccessfully(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftWrapsSuccessfully) {
-  AssertBackwardWrapsSuccessfully(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the first item of a focusgroup that wraps and
 // supports only the axis of the pressed arrow key, a backward arrow key press
 // should move the focus to the last item within the focusgroup.
-void FocusgroupControllerTest::AssertBackwardWrapsSuccessfullyInAxis(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       WrapsSuccessfullyInAxis) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_UP) {
     // The arrow key is in the vertical axis, so the focusgroup should only
     // support the vertical axis.
@@ -2405,20 +2039,12 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpWrapsSuccessfullyInAxis) {
-  AssertBackwardWrapsSuccessfullyInAxis(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftWrapsSuccessfullyInAxis) {
-  AssertBackwardWrapsSuccessfullyInAxis(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the first item of a focusgroup that wraps and
 // supports only the orthogonal axis of the pressed arrow key, a backward arrow
 // key press shouldn't move the focus.
-void FocusgroupControllerTest::AssertBackwardDoesntWrapInOrthogonalAxis(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       DoesntWrapInOrthogonalAxis) {
+  int key = GetParam();
   if (key == ui::DomKey::ARROW_UP) {
     // The arrow key is in the vertical axis, so the focusgroup should only
     // support the horizontal axis.
@@ -2452,21 +2078,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpDoesntWrapInOrthogonalAxis) {
-  AssertBackwardDoesntWrapInOrthogonalAxis(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftDoesntWrapInOrthogonalAxis) {
-  AssertBackwardDoesntWrapInOrthogonalAxis(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the first item of an extending focusgroup that
 // inherited its wrapping behavior, it should only wrap if the focused item is
 // also the first item of that parent focusgroup. If it is, then it should wrap
 // within the parent focusgroup, not within the extending focusgroup.
-void FocusgroupControllerTest::
-    AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       WrapsSuccessfullyInExtendingFocusgroup) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <div focusgroup=extend>
@@ -2507,23 +2125,13 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item1);
 }
 
-TEST_F(FocusgroupControllerTest,
-       ArrowUpWrapsSuccessfullyInExtendingFocusgroup) {
-  AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest,
-       ArrowLeftWrapsSuccessfullyInExtendingFocusgroup) {
-  AssertBackwardWrapsSuccessfullyInExtendingFocusgroup(ui::DomKey::ARROW_LEFT);
-}
-
 // When the focus is set on the first item of an extending focusgroup while
 // there are other non-item elements before, we should still be able to wrap to
 // the last item. Also, if the last item has other non-item elements after
 // itself, skipping these non-item elements shouldn't be an issue.
-void FocusgroupControllerTest::AssertBackwardWrapsSuccessfullyInComplexCase(
-    int key) {
-  ASSERT_TRUE(key == ui::DomKey::ARROW_UP || key == ui::DomKey::ARROW_LEFT);
+TEST_P(FocusgroupControllerBackwardNavigationTest,
+       WrapsSuccessfullyInComplexCase) {
+  int key = GetParam();
   GetDocument().body()->setInnerHTML(R"HTML(
     <div focusgroup=wrap>
       <div>
@@ -2553,11 +2161,4 @@
   ASSERT_EQ(GetDocument().FocusedElement(), item3);
 }
 
-TEST_F(FocusgroupControllerTest, ArrowUpWrapsSuccessfullyInComplexCase) {
-  AssertBackwardWrapsSuccessfullyInComplexCase(ui::DomKey::ARROW_UP);
-}
-
-TEST_F(FocusgroupControllerTest, ArrowLeftWrapsSuccessfullyInComplexCase) {
-  AssertBackwardWrapsSuccessfullyInComplexCase(ui::DomKey::ARROW_LEFT);
-}
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc
index 5947f89..2291543 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -140,7 +140,7 @@
         // introduces subpixel gaps along the corners. Those are avoided by
         // insetting the clipping path by one CSS pixel.
         if (has_opaque_background)
-          rect_to_clip_out.InflateWithRadii(-1);
+          rect_to_clip_out.Inset(1);
 
         if (!rect_to_clip_out.IsEmpty())
           context.ClipOutRoundedRect(rect_to_clip_out);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 0f2ffb1..3e3b2fa2 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -1961,8 +1961,22 @@
     return false;
 
   // Now hit test ourselves.
-  if (hit_test_self &&
-      IsVisibleToHitTest(box_fragment_, hit_test.result->GetHitTestRequest())) {
+  if (hit_test_self) {
+    if (UNLIKELY(!IsVisibleToHitTest(fragment,
+                                     hit_test.result->GetHitTestRequest())))
+      return false;
+    if (UNLIKELY(fragment.IsOpaque()))
+      return false;
+  } else if (UNLIKELY(fragment.IsOpaque() &&
+                      hit_test.result->HasListBasedResult() &&
+                      IsVisibleToHitTest(
+                          fragment, hit_test.result->GetHitTestRequest()))) {
+    // Opaque fragments should not hit, but they are still ancestors in the DOM
+    // tree. They should be added to the list-based result as ancestors if
+    // descendants hit.
+    hit_test_self = true;
+  }
+  if (hit_test_self) {
     PhysicalRect bounds_rect(physical_offset, size);
     if (UNLIKELY(
             hit_test.result->GetHitTestRequest().IsHitTestVisualOverflow())) {
@@ -1988,10 +2002,6 @@
       // See http://crbug.com/1043471
       DCHECK(!box_item_ || box_item_->BoxFragment() == &fragment);
       if (box_item_ && box_item_->IsInlineBox()) {
-        // Opaque fragments should be included only for list-based hit-testing.
-        if (fragment.IsOpaque() &&
-            !hit_test.result->GetHitTestRequest().ListBased())
-          return false;
         DCHECK(inline_box_cursor_);
         if (hit_test.AddNodeToResultWithContentOffset(
                 fragment.NodeForHitTest(),
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 9e9811ab..6eed59d 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -1506,6 +1506,11 @@
 
       UpdateFilterEffect(object_, properties_->Filter(), state.filter);
 
+      // TODO(crbug.com/900241): Remove the setter when we can use
+      // state.direct_compositing_reasons to check for active animations.
+      state.has_active_filter_animation =
+          object_.StyleRef().HasCurrentFilterAnimation();
+
       // The CSS filter spec didn't specify how filters interact with overflow
       // clips. The implementation here mimics the old Blink/WebKit behavior for
       // backward compatibility.
@@ -1524,9 +1529,7 @@
       // On the other hand, "B" should not be clipped because the overflow clip
       // is not in its containing block chain, but as the filter output will be
       // clipped, so a blurred "B" may still be invisible.
-      if (!state.filter.IsEmpty() ||
-          (full_context_.direct_compositing_reasons &
-           CompositingReason::kActiveFilterAnimation))
+      if (!state.filter.IsEmpty() || state.has_active_filter_animation)
         state.output_clip = context_.current.clip;
 
       // TODO(trchen): A filter may contain spatial operations such that an
@@ -1552,11 +1555,6 @@
       state.compositor_element_id =
           GetCompositorElementId(CompositorElementIdNamespace::kEffectFilter);
 
-      // TODO(crbug.com/900241): Remove the setter when we can use
-      // state.direct_compositing_reasons to check for active animations.
-      state.has_active_filter_animation =
-          object_.StyleRef().HasCurrentFilterAnimation();
-
       EffectPaintPropertyNode::AnimationState animation_state;
       animation_state.is_running_filter_animation_on_compositor =
           object_.StyleRef().IsRunningFilterAnimationOnCompositor();
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index aacc0ef..587491ca 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -1020,8 +1020,10 @@
     // missable child fragments. We may enter fragment traversal mode further
     // down in the subtree, and there may be a node that's a direct child of
     // |object|, fragment-wise, while it's further down in the tree, CSS
-    // box-tree-wise.
-    if (box && box->PhysicalFragmentCount()) {
+    // box-tree-wise. This is only an issue for OOF descendants, though, so only
+    // examine OOF containing blocks.
+    if (box && box->CanContainAbsolutePositionObjects() &&
+        box->PhysicalFragmentCount()) {
       DCHECK_EQ(box->PhysicalFragmentCount(), 1u);
       fragment = box->GetPhysicalFragment(0);
     }
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5 b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
index 435f3bb..24f4a25 100644
--- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
+++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
@@ -67,6 +67,18 @@
       permissions_policy_name: "autoplay",
     },
     {
+      name: "BrowsingTopics",
+      permissions_policy_name: "browsing-topics",
+      feature_default: "EnableForAll",
+      depends_on: ["TopicsAPI"],
+    },
+    {
+      name: "BrowsingTopicsBackwardCompatible",
+      permissions_policy_name: "interest-cohort",
+      feature_default: "EnableForAll",
+      depends_on: ["TopicsAPI"],
+    },
+    {
       name: "Camera",
       permissions_policy_name: "camera",
     },
diff --git a/third_party/blink/renderer/core/script/pending_script.cc b/third_party/blink/renderer/core/script/pending_script.cc
index f478790..a3fa931 100644
--- a/third_party/blink/renderer/core/script/pending_script.cc
+++ b/third_party/blink/renderer/core/script/pending_script.cc
@@ -25,9 +25,11 @@
 
 #include "third_party/blink/renderer/core/script/pending_script.h"
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-shared.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_parser_timing.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -36,6 +38,8 @@
 #include "third_party/blink/renderer/core/script/script_element_base.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 
 namespace blink {
 
@@ -136,7 +140,8 @@
     return;
   }
 
-  if (!To<LocalDOMWindow>(context)->GetFrame()) {
+  LocalFrame* frame = To<LocalDOMWindow>(context)->GetFrame();
+  if (!frame) {
     Dispose();
     return;
   }
@@ -154,6 +159,15 @@
     return;
   }
 
+  std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope>
+      task_attribution_scope;
+  DCHECK(ThreadScheduler::Current());
+  ScriptState* script_state = ToScriptStateForMainWorld(frame);
+  if (auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker()) {
+    task_attribution_scope =
+        tracker->CreateTaskScope(script_state, absl::nullopt);
+  }
+
   Script* script = GetSource(document_url);
 
   const bool was_canceled = WasCanceled();
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index 5cc9848a..227c943 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -524,6 +524,13 @@
   if (!back_forward_cache_controller_host_.is_bound()) {
     return;
   }
+  if (!GetExecutionContext()->is_in_back_forward_cache()) {
+    // Don't send an eviction message unless the document associated with this
+    // DedicatedWorker is in back/forward cache.
+    // TODO(crbug.com/1163843): Maybe also check if eviction is already disabled
+    // for the document?
+    return;
+  }
   UMA_HISTOGRAM_ENUMERATION("BackForwardCache.Eviction.Renderer", reason);
   back_forward_cache_controller_host_->EvictFromBackForwardCache(reason);
 }
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index b0e5356..a5d0c34 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -65,6 +65,7 @@
     "//third_party/blink/renderer/modules/beacon",
     "//third_party/blink/renderer/modules/bluetooth",
     "//third_party/blink/renderer/modules/broadcastchannel",
+    "//third_party/blink/renderer/modules/browsing_topics",
     "//third_party/blink/renderer/modules/buckets",
     "//third_party/blink/renderer/modules/breakout_box",
     "//third_party/blink/renderer/modules/cache_storage",
@@ -570,6 +571,7 @@
     "push_messaging/push_subscription_test.cc",
     "remoteplayback/remote_playback_test.cc",
     "scheduler/dom_scheduler_test.cc",
+    "scheduler/task_attribution_tracker_impl_test.cc",
     "screen_orientation/screen_orientation_controller_test.cc",
     "sensor/sensor_test_utils.cc",
     "sensor/sensor_test_utils.h",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 2a23298..f2192581 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1095,22 +1095,13 @@
   if (GetNode()->HasTagName(html_names::kDtTag))
     return ax::mojom::blink::Role::kDescriptionListTerm;
 
-  // MathMLElement instances are not created when MathMLCore is disabled, so one
-  // cannot rely on Node::HasTagName(const MathMLQualifiedName&) to test the
-  // <math> tag. See crbug.com/1272556.
-  if (!RuntimeEnabledFeatures::MathMLCoreEnabled()) {
-    if (auto* element = DynamicTo<Element>(GetNode())) {
-      if (element->namespaceURI() == mathml_names::kNamespaceURI &&
-          element->nodeName() == mathml_names::kMathTag.LocalName()) {
-        return ax::mojom::blink::Role::kMath;
-      }
-    }
-  }
-
   // Mapping of MathML elements. See https://w3c.github.io/mathml-aam/
   if (auto* element = DynamicTo<MathMLElement>(GetNode())) {
-    if (element->HasTagName(mathml_names::kMathTag))
-      return ax::mojom::blink::Role::kMathMLMath;
+    if (element->HasTagName(mathml_names::kMathTag)) {
+      return RuntimeEnabledFeatures::MathMLCoreEnabled()
+                 ? ax::mojom::blink::Role::kMathMLMath
+                 : ax::mojom::blink::Role::kMath;
+    }
     if (element->HasTagName(mathml_names::kMfracTag))
       return ax::mojom::blink::Role::kMathMLFraction;
     if (element->HasTagName(mathml_names::kMiTag))
diff --git a/third_party/blink/renderer/modules/browsing_topics/BUILD.gn b/third_party/blink/renderer/modules/browsing_topics/BUILD.gn
new file mode 100644
index 0000000..236b65c39
--- /dev/null
+++ b/third_party/blink/renderer/modules/browsing_topics/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/blink/renderer/modules/modules.gni")
+
+blink_modules_sources("browsing_topics") {
+  sources = [
+    "browsing_topics_document_supplement.cc",
+    "browsing_topics_document_supplement.h",
+  ]
+
+  public_deps = [ "//third_party/blink/public/mojom:mojom_modules_blink" ]
+}
diff --git a/third_party/blink/renderer/modules/browsing_topics/OWNERS b/third_party/blink/renderer/modules/browsing_topics/OWNERS
new file mode 100644
index 0000000..bb1b03b
--- /dev/null
+++ b/third_party/blink/renderer/modules/browsing_topics/OWNERS
@@ -0,0 +1 @@
+file://components/browsing_topics/OWNERS
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl b/third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl
new file mode 100644
index 0000000..44e5ce6
--- /dev/null
+++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topic.idl
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://github.com/jkarlin/topics
+
+dictionary BrowsingTopic {
+  long topic;
+  DOMString version;
+  DOMString configVersion;
+  DOMString modelVersion;
+  DOMString taxonomyVersion;
+};
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc
new file mode 100644
index 0000000..4514913
--- /dev/null
+++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc
@@ -0,0 +1,93 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h"
+
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom-blink.h"
+#include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-blink.h"
+#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_browsing_topic.h"
+
+namespace blink {
+
+// static
+const char BrowsingTopicsDocumentSupplement::kSupplementName[] =
+    "BrowsingTopicsDocumentSupplement";
+
+// static
+BrowsingTopicsDocumentSupplement* BrowsingTopicsDocumentSupplement::From(
+    Document& document) {
+  auto* supplement =
+      Supplement<Document>::From<BrowsingTopicsDocumentSupplement>(document);
+  if (!supplement) {
+    supplement =
+        MakeGarbageCollected<BrowsingTopicsDocumentSupplement>(document);
+    Supplement<Document>::ProvideTo(document, supplement);
+  }
+  return supplement;
+}
+
+// static
+ScriptPromise BrowsingTopicsDocumentSupplement::browsingTopics(
+    ScriptState* script_state,
+    Document& document,
+    ExceptionState& exception_state) {
+  auto* supplement = From(document);
+  return supplement->GetBrowsingTopics(script_state, document, exception_state);
+}
+
+BrowsingTopicsDocumentSupplement::BrowsingTopicsDocumentSupplement(
+    Document& document)
+    : Supplement<Document>(document) {}
+
+ScriptPromise BrowsingTopicsDocumentSupplement::GetBrowsingTopics(
+    ScriptState* script_state,
+    Document& document,
+    ExceptionState& exception_state) {
+  if (!document.GetFrame()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
+                                      "A browsing context is required when "
+                                      "calling document.browsingTopics().");
+    return ScriptPromise();
+  }
+
+  ScriptPromiseResolver* resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
+
+  if (!document.GetExecutionContext()->IsFeatureEnabled(
+          mojom::blink::PermissionsPolicyFeature::kBrowsingTopics)) {
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidAccessError,
+        "The \"browsing-topics\" Permissions Policy denied the use of "
+        "document.browsingTopics()."));
+
+    return promise;
+  }
+
+  if (!document.GetExecutionContext()->IsFeatureEnabled(
+          mojom::blink::PermissionsPolicyFeature::
+              kBrowsingTopicsBackwardCompatible)) {
+    resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
+        script_state->GetIsolate(), DOMExceptionCode::kInvalidAccessError,
+        "The \"interest-cohort\" Permissions Policy denied the use of "
+        "document.browsingTopics()."));
+
+    return promise;
+  }
+
+  resolver->Resolve(HeapVector<Member<BrowsingTopic>>());
+  return promise;
+}
+
+void BrowsingTopicsDocumentSupplement::Trace(Visitor* visitor) const {
+  Supplement<Document>::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h
new file mode 100644
index 0000000..52317a16
--- /dev/null
+++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BROWSING_TOPICS_BROWSING_TOPICS_DOCUMENT_SUPPLEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_BROWSING_TOPICS_BROWSING_TOPICS_DOCUMENT_SUPPLEMENT_H_
+
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+// Provides the implementation for the Topics API.
+// Explainer: https://github.com/jkarlin/topics
+class MODULES_EXPORT BrowsingTopicsDocumentSupplement
+    : public GarbageCollected<BrowsingTopicsDocumentSupplement>,
+      public Supplement<Document> {
+ public:
+  static const char kSupplementName[];
+
+  // Supplement functionality.
+  static BrowsingTopicsDocumentSupplement* From(Document&);
+  static ScriptPromise browsingTopics(ScriptState* script_state,
+                                      Document& document,
+                                      ExceptionState& exception_state);
+
+  explicit BrowsingTopicsDocumentSupplement(Document&);
+
+  // Implements the document.browsingTopics().
+  ScriptPromise GetBrowsingTopics(ScriptState* script_state,
+                                  Document& document,
+                                  ExceptionState& exception_state);
+
+  // GC functionality.
+  void Trace(Visitor* visitor) const override;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_BROWSING_TOPICS_BROWSING_TOPICS_DOCUMENT_SUPPLEMENT_H_
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl
new file mode 100644
index 0000000..ab1830c
--- /dev/null
+++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.idl
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://github.com/jkarlin/topics
+
+[
+    ImplementedAs=BrowsingTopicsDocumentSupplement,
+    RuntimeEnabled=TopicsAPI
+] partial interface Document {
+    [
+        NewObject,
+        CallWith=ScriptState,
+        RaisesException,
+        SecureContext,
+        MeasureAs=TopicsAPI_BrowsingTopics_Method
+    ]
+    Promise<sequence<BrowsingTopic>> browsingTopics();
+};
diff --git a/third_party/blink/renderer/modules/browsing_topics/idls.gni b/third_party/blink/renderer/modules/browsing_topics/idls.gni
new file mode 100644
index 0000000..75134f2
--- /dev/null
+++ b/third_party/blink/renderer/modules/browsing_topics/idls.gni
@@ -0,0 +1,7 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+modules_dictionary_idl_files = [ "browsing_topic.idl" ]
+
+modules_dependency_idl_files = [ "browsing_topics_document_supplement.idl" ]
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
index eb69c0a8..3ebe2d88 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/containers/contains.h"
+#include "net/base/features.h"
 #include "net/cookies/canonical_cookie.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "services/network/public/mojom/restricted_cookie_manager.mojom-blink.h"
@@ -152,7 +153,10 @@
   }
 
   absl::optional<net::CookiePartitionKey> cookie_partition_key = absl::nullopt;
-  if (options->partitioned() && partitioned_cookies_runtime_feature_enabled) {
+  if (options->partitioned() &&
+      (partitioned_cookies_runtime_feature_enabled ||
+       base::FeatureList::IsEnabled(
+           net::features::kPartitionedCookiesBypassOriginTrial))) {
     // We don't trust the renderer to determine the cookie partition key, so we
     // use this factory to indicate we are using a temporary value here.
     cookie_partition_key = net::CookiePartitionKey::FromScript();
diff --git a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc
index 53608cba..a3fc9de 100644
--- a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc
+++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.cc
@@ -78,6 +78,11 @@
     stream_associator_.Run(*stream_id_, output_device_id);
 }
 
+media::AudioProcessorControls* MojoAudioInputIPC::GetProcessorControls() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return this;
+}
+
 void MojoAudioInputIPC::CloseStream() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   delegate_ = nullptr;
@@ -87,6 +92,19 @@
   processor_controls_.reset();
 }
 
+void MojoAudioInputIPC::GetStats(GetStatsCB callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (processor_controls_)
+    processor_controls_->GetStats(std::move(callback));
+}
+
+void MojoAudioInputIPC::SetPreferredNumCaptureChannels(
+    int32_t num_preferred_channels) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (processor_controls_)
+    processor_controls_->SetPreferredNumCaptureChannels(num_preferred_channels);
+}
+
 void MojoAudioInputIPC::StreamCreated(
     mojo::PendingRemote<media::mojom::blink::AudioInputStream> stream,
     mojo::PendingReceiver<media::mojom::blink::AudioInputStreamClient>
diff --git a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h
index 72e0bbd..3282f42 100644
--- a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h
+++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h
@@ -12,9 +12,9 @@
 #include "base/sequence_checker.h"
 #include "media/audio/audio_input_ipc.h"
 #include "media/audio/audio_source_parameters.h"
+#include "media/base/audio_processor_controls.h"
 #include "media/mojo/mojom/audio_input_stream.mojom-blink.h"
 #include "media/mojo/mojom/audio_processing.mojom-blink.h"
-#include "media/webrtc/audio_processor_controls.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -29,6 +29,7 @@
 // thread.
 class MODULES_EXPORT MojoAudioInputIPC
     : public media::AudioInputIPC,
+      public media::AudioProcessorControls,
       public mojom::blink::RendererAudioInputStreamFactoryClient,
       public media::mojom::blink::AudioInputStreamClient {
  public:
@@ -67,8 +68,13 @@
   void RecordStream() override;
   void SetVolume(double volume) override;
   void SetOutputDeviceForAec(const std::string& output_device_id) override;
+  media::AudioProcessorControls* GetProcessorControls() override;
   void CloseStream() override;
 
+  // AudioProcessorControls implementation
+  void GetStats(GetStatsCB callback) override;
+  void SetPreferredNumCaptureChannels(int32_t num_preferred_channels) override;
+
  private:
   void StreamCreated(
       mojo::PendingRemote<media::mojom::blink::AudioInputStream> stream,
diff --git a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc
index 1333f37..164f16628 100644
--- a/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc
+++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc_test.cc
@@ -66,6 +66,17 @@
   MOCK_METHOD1(SetVolume, void(double));
 };
 
+class MockAudioProcessorControls
+    : public media::mojom::blink::AudioProcessorControls {
+ public:
+  void GetStats(GetStatsCallback cb) override {
+    GetStatsCalled();
+    std::move(cb).Run(media::AudioProcessingStats());
+  }
+  MOCK_METHOD0(GetStatsCalled, void());
+  MOCK_METHOD1(SetPreferredNumCaptureChannels, void(int32_t));
+};
+
 class MockDelegate : public media::AudioInputIPCDelegate {
  public:
   MockDelegate() = default;
@@ -86,10 +97,11 @@
 class FakeStreamCreator {
  public:
   FakeStreamCreator(media::mojom::blink::AudioInputStream* stream,
+                    media::mojom::blink::AudioProcessorControls* controls,
                     bool initially_muted,
                     bool expect_processing_config = false)
-      : stream_(stream),
-        receiver_(stream_),
+      : receiver_(stream),
+        controls_receiver_(controls),
         initially_muted_(initially_muted),
         expect_processing_config_(expect_processing_config) {}
 
@@ -98,12 +110,11 @@
       mojo::PendingRemote<mojom::blink::RendererAudioInputStreamFactoryClient>
           factory_client,
       mojo::PendingReceiver<media::mojom::blink::AudioProcessorControls>
-          controls_receiver,
+          pending_controls_receiver,
       const media::AudioParameters& params,
       bool automatic_gain_control,
       uint32_t total_segments) {
     EXPECT_FALSE(receiver_.is_bound());
-    EXPECT_NE(stream_, nullptr);
     EXPECT_EQ(source_params.session_id, SourceParams().session_id);
     factory_client_.reset();
     factory_client_.Bind(std::move(factory_client));
@@ -111,7 +122,9 @@
     EXPECT_TRUE(
         base::CancelableSyncSocket::CreatePair(&socket_, &foreign_socket));
 
-    EXPECT_EQ(!!controls_receiver, expect_processing_config_);
+    EXPECT_EQ(!!pending_controls_receiver, expect_processing_config_);
+    if (pending_controls_receiver)
+      controls_receiver_.Bind(std::move(pending_controls_receiver));
 
     factory_client_->StreamCreated(
         receiver_.BindNewPipeAndPassRemote(),
@@ -130,6 +143,7 @@
   void Rearm() {
     stream_client_.reset();
     receiver_.reset();
+    controls_receiver_.reset();
     socket_.Close();
   }
 
@@ -139,11 +153,12 @@
   }
 
  private:
-  media::mojom::blink::AudioInputStream* stream_;
   mojo::Remote<media::mojom::blink::AudioInputStreamClient> stream_client_;
   mojo::Remote<mojom::blink::RendererAudioInputStreamFactoryClient>
       factory_client_;
   mojo::Receiver<media::mojom::blink::AudioInputStream> receiver_;
+  mojo::Receiver<media::mojom::blink::AudioProcessorControls>
+      controls_receiver_;
   bool initially_muted_;
   bool expect_processing_config_;
   base::CancelableSyncSocket socket_;
@@ -159,8 +174,9 @@
 
 TEST(MojoAudioInputIPC, OnStreamCreated_Propagates) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, false);
+  FakeStreamCreator creator(&stream, &controls, false);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
       std::make_unique<MojoAudioInputIPC>(
@@ -178,8 +194,9 @@
 
 TEST(MojoAudioInputIPC, OnStreamCreated_Propagates_WithProcessingConfig) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, false,
+  FakeStreamCreator creator(&stream, &controls, false,
                             /*expect_processing_config*/ true);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
@@ -225,8 +242,9 @@
 
 TEST(MojoAudioInputIPC, OnStreamCreated_PropagatesInitiallyMuted) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, true);
+  FakeStreamCreator creator(&stream, &controls, true);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
       std::make_unique<MojoAudioInputIPC>(
@@ -244,8 +262,9 @@
 
 TEST(MojoAudioInputIPC, IsReusable) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, false);
+  FakeStreamCreator creator(&stream, &controls, false);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
       std::make_unique<MojoAudioInputIPC>(
@@ -268,8 +287,9 @@
 
 TEST(MojoAudioInputIPC, IsReusableAfterError) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, false);
+  FakeStreamCreator creator(&stream, &controls, false);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
       std::make_unique<MojoAudioInputIPC>(
@@ -298,8 +318,9 @@
 
 TEST(MojoAudioInputIPC, Record_Records) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, false);
+  FakeStreamCreator creator(&stream, &controls, false);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
       std::make_unique<MojoAudioInputIPC>(
@@ -320,8 +341,9 @@
 
 TEST(MojoAudioInputIPC, SetVolume_SetsVolume) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, false);
+  FakeStreamCreator creator(&stream, &controls, false);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
       std::make_unique<MojoAudioInputIPC>(
@@ -342,8 +364,9 @@
 
 TEST(MojoAudioInputIPC, SetOutputDeviceForAec_AssociatesInputAndOutputForAec) {
   StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
   StrictMock<MockDelegate> delegate;
-  FakeStreamCreator creator(&stream, false);
+  FakeStreamCreator creator(&stream, &controls, false);
 
   const std::unique_ptr<media::AudioInputIPC> ipc =
       std::make_unique<MojoAudioInputIPC>(
@@ -361,4 +384,134 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST(MojoAudioInputIPC,
+     Controls_NotCalled_BeforeStreamCreated_WithoutProcessing) {
+  StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
+  StrictMock<MockDelegate> delegate;
+  FakeStreamCreator creator(&stream, &controls, false);
+
+  const std::unique_ptr<media::AudioInputIPC> ipc =
+      std::make_unique<MojoAudioInputIPC>(
+          SourceParams(), creator.GetCallback(),
+          base::BindRepeating(&AssociateOutputForAec));
+
+  // StrictMock will verify that no calls are made to |controls|.
+  media::AudioProcessorControls* media_controls = ipc->GetProcessorControls();
+  media_controls->SetPreferredNumCaptureChannels(1);
+  media_controls->GetStats(media::AudioProcessorControls::GetStatsCB());
+  base::RunLoop().RunUntilIdle();
+
+  ipc->CloseStream();
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST(MojoAudioInputIPC,
+     Controls_NotCalled_AfterStreamCreated_WithoutProcessing) {
+  StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
+  StrictMock<MockDelegate> delegate;
+  FakeStreamCreator creator(&stream, &controls, false);
+
+  const std::unique_ptr<media::AudioInputIPC> ipc =
+      std::make_unique<MojoAudioInputIPC>(
+          SourceParams(), creator.GetCallback(),
+          base::BindRepeating(&AssociateOutputForAec));
+
+  media::AudioProcessorControls* media_controls = ipc->GetProcessorControls();
+
+  EXPECT_CALL(delegate, GotOnStreamCreated(_));
+
+  ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
+  base::RunLoop().RunUntilIdle();
+
+  // StrictMock will verify that no calls are made to |controls|.
+  media_controls->SetPreferredNumCaptureChannels(1);
+  media_controls->GetStats(media::AudioProcessorControls::GetStatsCB());
+  base::RunLoop().RunUntilIdle();
+
+  ipc->CloseStream();
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST(MojoAudioInputIPC, Controls_NotCalled_BeforeStreamCreated_WithProcessing) {
+  StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
+  StrictMock<MockDelegate> delegate;
+  FakeStreamCreator creator(&stream, &controls, false,
+                            /*expect_processing_config*/ true);
+
+  const std::unique_ptr<media::AudioInputIPC> ipc =
+      std::make_unique<MojoAudioInputIPC>(
+          SourceParamsWithProcessing(), creator.GetCallback(),
+          base::BindRepeating(&AssociateOutputForAec));
+
+  // StrictMock will verify that no calls are made to |controls|.
+  media::AudioProcessorControls* media_controls = ipc->GetProcessorControls();
+  media_controls->SetPreferredNumCaptureChannels(1);
+  media_controls->GetStats(media::AudioProcessorControls::GetStatsCB());
+  base::RunLoop().RunUntilIdle();
+
+  ipc->CloseStream();
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST(MojoAudioInputIPC, Controls_Called_AfterStreamCreated_WithProcessing) {
+  StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
+  StrictMock<MockDelegate> delegate;
+  FakeStreamCreator creator(&stream, &controls, false,
+                            /*expect_processing_config*/ true);
+
+  const std::unique_ptr<media::AudioInputIPC> ipc =
+      std::make_unique<MojoAudioInputIPC>(
+          SourceParamsWithProcessing(), creator.GetCallback(),
+          base::BindRepeating(&AssociateOutputForAec));
+
+  media::AudioProcessorControls* media_controls = ipc->GetProcessorControls();
+
+  EXPECT_CALL(delegate, GotOnStreamCreated(_));
+  EXPECT_CALL(controls, SetPreferredNumCaptureChannels(1));
+  EXPECT_CALL(controls, GetStatsCalled());
+
+  ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
+  base::RunLoop().RunUntilIdle();
+
+  media_controls->SetPreferredNumCaptureChannels(1);
+  media_controls->GetStats(
+      base::BindOnce([](const media::AudioProcessingStats& stats) {}));
+  base::RunLoop().RunUntilIdle();
+
+  ipc->CloseStream();
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST(MojoAudioInputIPC, Controls_NotCalled_AfterStreamClosed_WithProcessing) {
+  StrictMock<MockStream> stream;
+  StrictMock<MockAudioProcessorControls> controls;
+  StrictMock<MockDelegate> delegate;
+  FakeStreamCreator creator(&stream, &controls, false,
+                            /*expect_processing_config*/ true);
+
+  const std::unique_ptr<media::AudioInputIPC> ipc =
+      std::make_unique<MojoAudioInputIPC>(
+          SourceParamsWithProcessing(), creator.GetCallback(),
+          base::BindRepeating(&AssociateOutputForAec));
+
+  media::AudioProcessorControls* media_controls = ipc->GetProcessorControls();
+
+  EXPECT_CALL(delegate, GotOnStreamCreated(_));
+
+  ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
+  base::RunLoop().RunUntilIdle();
+
+  ipc->CloseStream();
+  base::RunLoop().RunUntilIdle();
+
+  // StrictMock will verify that no calls are made to |controls|.
+  media_controls->SetPreferredNumCaptureChannels(1);
+  media_controls->GetStats(media::AudioProcessorControls::GetStatsCB());
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index 1026869..9dc8e1d 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -12,7 +12,6 @@
 #include "build/build_config.h"
 #include "media/base/audio_parameters.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h"
 #include "third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h"
 #include "third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.h"
@@ -110,13 +109,6 @@
   return audio_processor_->OutputFormat();
 }
 
-void MediaStreamAudioProcessor::SetOutputWillBeMuted(bool muted) {
-  DCHECK(main_thread_runner_->BelongsToCurrentThread());
-  DCHECK(base::FeatureList::IsEnabled(
-      features::kMinimizeAudioProcessingForUnusedOutput));
-  audio_processor_->SetOutputWillBeMuted(muted);
-}
-
 void MediaStreamAudioProcessor::OnStartDump(base::File dump_file) {
   DCHECK(main_thread_runner_->BelongsToCurrentThread());
   audio_processor_->OnStartDump(std::move(dump_file));
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
index ae5f60b..7baa6add 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
@@ -88,11 +88,6 @@
     return audio_processor_->has_webrtc_audio_processing();
   }
 
-  // Instructs the Audio Processing Module (APM) to reduce its complexity when
-  // |muted| is true. This mode is triggered when all audio tracks are disabled.
-  // The default APM complexity mode is restored by |muted| set to false.
-  void SetOutputWillBeMuted(bool muted);
-
   // AecDumpAgentImpl::Delegate implementation.
   // Called on the main render thread.
   void OnStartDump(base::File dump_file) override;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
index 0ebce6f2..a4140057 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
@@ -380,25 +380,6 @@
     return;
 
   DidSetMediaStreamTrackEnabled(component_.Get());
-
-  MediaStreamAudioSource* media_stream_audio_source =
-      MediaStreamAudioSource::From(component_->Source());
-  ProcessedLocalAudioSource* processed_local_audio_source =
-      ProcessedLocalAudioSource::From(media_stream_audio_source);
-  if (media_stream_audio_source && processed_local_audio_source) {
-    if (!enabled) {
-      // One track was disabled. Check if all tracks are disabled and inform the
-      // APM about the state. The APM can enter a low-complexity mode if it
-      // knows that all tracks are muted and that saves CPU cycles.
-      const bool all_tracks_disabled =
-          media_stream_audio_source->AllTracksAreDisabled();
-      processed_local_audio_source->SetOutputWillBeMuted(all_tracks_disabled);
-    } else {
-      // At least one track is enabled. Tell the APM to go back to its normal
-      // mode.
-      processed_local_audio_source->SetOutputWillBeMuted(false);
-    }
-  }
 }
 
 bool MediaStreamTrack::muted() const {
diff --git a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
index 0ccb6dd6..d36770b 100644
--- a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
@@ -17,7 +17,6 @@
 #include "media/audio/audio_source_parameters.h"
 #include "media/base/channel_layout.h"
 #include "media/base/sample_rates.h"
-#include "media/webrtc/audio_processor_controls.h"
 #include "media/webrtc/webrtc_features.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
@@ -465,23 +464,12 @@
 scoped_refptr<webrtc::AudioProcessorInterface>
 ProcessedLocalAudioSource::GetAudioProcessor() const {
   DCHECK(media_stream_audio_processor_);
+  if (!media_stream_audio_processor_->has_webrtc_audio_processing())
+    return nullptr;
   return static_cast<scoped_refptr<webrtc::AudioProcessorInterface>>(
       media_stream_audio_processor_);
 }
 
-bool ProcessedLocalAudioSource::HasWebRtcAudioProcessing() const {
-  return media_stream_audio_processor_ &&
-         media_stream_audio_processor_->has_webrtc_audio_processing();
-}
-
-void ProcessedLocalAudioSource::SetOutputWillBeMuted(bool muted) {
-  if (base::FeatureList::IsEnabled(
-          features::kMinimizeAudioProcessingForUnusedOutput) &&
-      HasWebRtcAudioProcessing()) {
-    media_stream_audio_processor_->SetOutputWillBeMuted(muted);
-  }
-}
-
 void ProcessedLocalAudioSource::SetVolume(double volume) {
   DVLOG(1) << "ProcessedLocalAudioSource::SetVolume()";
   DCHECK_LE(volume, 1.0);
diff --git a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h
index 3a98847..f99e103 100644
--- a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h
+++ b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h
@@ -75,17 +75,11 @@
   absl::optional<blink::AudioProcessingProperties>
   GetAudioProcessingProperties() const final;
 
-  // The following accessors are valid after the source is started (when the
-  // first track is connected).
+  // Valid after the source is started (when the first track is connected). Will
+  // return nullptr if WebRTC stats are no available for the current
+  // configuration.
   scoped_refptr<webrtc::AudioProcessorInterface> GetAudioProcessor() const;
 
-  bool HasWebRtcAudioProcessing() const;
-
-  // Instructs the Audio Processing Module (APM) to reduce its complexity when
-  // |muted| is true. This mode is triggered when all audio tracks are disabled.
-  // The default APM complexity mode is restored when |muted| is set to false.
-  void SetOutputWillBeMuted(bool muted);
-
   const scoped_refptr<blink::MediaStreamAudioLevelCalculator::Level>&
   audio_level() const {
     return level_calculator_.level();
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index bbcccda..5be0c77 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -80,6 +80,7 @@
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_client.h"
 #include "third_party/blink/renderer/modules/remoteplayback/html_media_element_remote_playback.h"
 #include "third_party/blink/renderer/modules/remoteplayback/remote_playback.h"
+#include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h"
 #include "third_party/blink/renderer/modules/screen_enumeration/screen_details.h"
 #include "third_party/blink/renderer/modules/screen_enumeration/window_screens.h"
 #include "third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller.h"
@@ -103,6 +104,7 @@
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/widget/frame_widget.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -210,6 +212,9 @@
       std::make_unique<ImageBitmapRenderingContext::Factory>());
   OffscreenCanvas::RegisterRenderingContextFactory(
       std::make_unique<GPUCanvasContext::Factory>());
+
+  ThreadScheduler::Current()->InitializeTaskAttributionTracker(
+      std::make_unique<scheduler::TaskAttributionTrackerImpl>());
 }
 
 void ModulesInitializer::InitLocalFrame(LocalFrame& frame) const {
diff --git a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc
index de6e487..89a86b5c 100644
--- a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc
+++ b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc
@@ -51,18 +51,16 @@
 
 const char kToken[] = "%s";
 
+// Verify custom handler URL security as described in steps 6 and 7
+// https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
 static bool VerifyCustomHandlerURLSecurity(
     const LocalDOMWindow& window,
     const KURL& full_url,
     String& error_message,
     ProtocolHandlerSecurityLevel security_level) {
-  // This matches ProtocolHandler::IsValid().
-  // https://html.spec.whatwg.org/multipage/system-state.html#normalize-protocol-handler-parameters
-  bool has_valid_scheme =
-      full_url.ProtocolIsInHTTPFamily() ||
-      (security_level == ProtocolHandlerSecurityLevel::kExtensionFeatures &&
-       CommonSchemeRegistry::IsExtensionScheme(full_url.Protocol().Ascii()));
-  if (!has_valid_scheme || !network::IsUrlPotentiallyTrustworthy(full_url)) {
+  // The specification says that the API throws SecurityError exception if the
+  // URL's protocol isn't HTTP(S) or is potentially trustworthy.
+  if (!IsAllowedCustomHandlerURL(full_url, security_level)) {
     error_message = "The scheme of the url provided must be HTTP(S).";
     return false;
   }
@@ -115,13 +113,10 @@
     return false;
   }
 
-  bool allow_ext_plus_prefix =
-      security_level >= ProtocolHandlerSecurityLevel::kExtensionFeatures;
-  bool has_custom_scheme_prefix;
+  bool has_custom_scheme_prefix = false;
   StringUTF8Adaptor scheme_adaptor(scheme);
   if (!IsValidCustomHandlerScheme(scheme_adaptor.AsStringPiece(),
-                                  allow_ext_plus_prefix,
-                                  has_custom_scheme_prefix)) {
+                                  security_level, &has_custom_scheme_prefix)) {
     if (has_custom_scheme_prefix) {
       error_string = "The scheme name '" + scheme +
                      "' is not allowed. Schemes starting with '" + scheme +
diff --git a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h
index 3ca6f6a4..607d4fb6 100644
--- a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h
+++ b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.h
@@ -40,14 +40,14 @@
 class NavigatorContentUtilsClient;
 enum class ProtocolHandlerSecurityLevel;
 
-// Verify custom handler schemes for errors as described in
+// Verify custom handler schemes for errors as described in steps 1 and 2
 // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers.
 // Callers should surface an error with |error_message| if it returns false.
 bool VerifyCustomHandlerScheme(const String& scheme,
                                String& error_message,
                                ProtocolHandlerSecurityLevel security_level);
 
-// Verify custom handler URLs for syntax errors as described in
+// Verify custom handler URLs for syntax errors as described in step 3
 // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers.
 // Callers should surface an error with |error_message| if it returns false.
 // |full_url| is calculated URL that needs to resolve to a valid URL.
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 42973ab..3787783 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -39,6 +39,8 @@
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+#include "build/buildflag.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/features.h"
@@ -294,63 +296,27 @@
     DCHECK_EQ(configuration->rtcpMuxPolicy(), "require");
   }
 
-  if (configuration->hasSdpSemantics()) {
-    if (configuration->sdpSemantics() == "plan-b") {
-      web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB;
-      // Extend the Plan B deprecation deadline if
-      // RTCExtendDeadlineForPlanBRemoval is enabled, i.e. if the page has opted
-      // in to the 'RTCPeerConnection Plan B SDP Semantics' Deprecation Trial or
-      // if --enable-blink-features=RTCExtendDeadlineForPlanBRemoval was used.
-      // Local files also get the extended deadline beecause "file://" URLs
-      // cannot sign up for Origin Trials.
-      if (RuntimeEnabledFeatures::RTCExtendDeadlineForPlanBRemovalEnabled(
-              context) ||
-          context->Url().IsLocalFile()) {
-        // TODO(https://crbug.com/857004): In M97, when the Deprecation Trial
-        // ends, remove this code path in favor of throwing the exception below.
-        Deprecation::CountDeprecation(
-            context,
-            WebFeature::
-                kRTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial);
-      } else {
-        // The Deprecation Trial is not active. In this case, throw an exception
-        // if RTCDisallowPlanBOutsideDeprecationTrial is enabled.
-        if (base::FeatureList::IsEnabled(
-                features::kRTCDisallowPlanBOutsideDeprecationTrial)) {
-          // Throw Plan B exception!
-          UseCounter::Count(
-              context, WebFeature::kRTCPeerConnectionPlanBThrewAnException);
-          exception_state->ThrowDOMException(
-              DOMExceptionCode::kNotSupportedError,
-              "Plan B SDP semantics is a legacy version of the Session "
-              "Description Protocol that has severe compatibility issues on "
-              "modern browsers and is no longer supported. See "
-              "https://www.chromestatus.com/feature/5823036655665152 for more "
-              "details, including the possibility of registering for a "
-              "Deprecation Trial in order to extend the Plan B deprecation "
-              "deadline for a limited amount of time.");
-        } else {
-          // Throwing is not enabled, so just show a deprecation warning.
-          Deprecation::CountDeprecation(
-              context, WebFeature::kRTCPeerConnectionSdpSemanticsPlanB);
-        }
-      }
-    } else {
-      DCHECK_EQ(configuration->sdpSemantics(), "unified-plan");
-      web_configuration.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
-    }
-  } else {
-    // RTCUnifiedPlanByDefault decides the SDP semantics unless specified by the
-    // configuration (see above). By default RTCUnifiedPlanByDefault is enabled,
-    // meaning "Unified Plan" is used. For "Plan B"-by-default, pass the flag:
-    // --disable-features=RTCUnifiedPlanByDefault
-    if (!base::FeatureList::IsEnabled(features::kRTCUnifiedPlanByDefault) &&
-        !RuntimeEnabledFeatures::RTCUnifiedPlanByDefaultEnabled()) {
-      web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB;
-    } else {
-      web_configuration.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
-    }
+  // Unified Plan is used by default (RTCUnifiedPlanByDefault is
+  // ENABLED_BY_DEFAULT). For testing-only purposes the default can be set to
+  // Plan B instead using --disable-features=RTCUnifiedPlanByDefault.
+  DCHECK_EQ(web_configuration.sdp_semantics,
+            webrtc::SdpSemantics::kUnifiedPlan);
+  if (!base::FeatureList::IsEnabled(features::kRTCUnifiedPlanByDefault) &&
+      !RuntimeEnabledFeatures::RTCUnifiedPlanByDefaultEnabled()) {
+    web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB;
+    Deprecation::CountDeprecation(
+        context, WebFeature::kRTCPeerConnectionSdpSemanticsPlanB);
   }
+  // Only on Fuchsia is it still possible to overwrite the default sdpSemantics
+  // value in JavaScript.
+  // TODO(https://crbug.com/1302249): Don't support Plan B on Fuchsia either,
+  // delete Plan B from all of Chromium.
+#if BUILDFLAG(IS_FUCHSIA)
+  if (configuration->hasSdpSemantics() &&
+      configuration->sdpSemantics() == "plan-b") {
+    web_configuration.sdp_semantics = webrtc::SdpSemantics::kPlanB;
+  }
+#endif
 
   if (configuration->hasIceServers()) {
     WebVector<webrtc::PeerConnectionInterface::IceServer> ice_servers;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
index 32d27722..7ac9153c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -7,6 +7,8 @@
 #include <string>
 
 #include "base/bind.h"
+#include "build/build_config.h"
+#include "build/buildflag.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
@@ -583,7 +585,10 @@
   EXPECT_FALSE(pc->GetTrackForTesting(track_component.Get()));
 }
 
-TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsPlanB) {
+#if BUILDFLAG(IS_FUCHSIA)
+
+TEST_F(RTCPeerConnectionTest,
+       CheckForComplexSdpWithSdpSemanticsPlanBOnFuchsia) {
   V8TestingScope scope;
   Persistent<RTCPeerConnection> pc = CreatePC(scope, "plan-b");
   RTCSessionDescriptionInit* sdp = RTCSessionDescriptionInit::Create();
@@ -612,6 +617,8 @@
       pc->CheckForComplexSdp(ParsedSessionDescription::Parse(sdp)).has_value());
 }
 
+#endif  // BUILDFLAG(IS_FUCHSIA)
+
 TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsUnifiedPlan) {
   V8TestingScope scope;
   Persistent<RTCPeerConnection> pc = CreatePC(scope, "unified-plan");
@@ -1204,6 +1211,7 @@
     EXPECT_TRUE(scope.GetDocument().IsUseCounted(
         WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan));
   }
+#if BUILDFLAG(IS_FUCHSIA)
   // Constructor with {sdpSemantics:"plan-b"}.
   {
     V8TestingScope scope;
@@ -1232,6 +1240,7 @@
     EXPECT_FALSE(scope.GetDocument().IsUseCounted(
         WebFeature::kRTCPeerConnectionUsingComplexUnifiedPlan));
   }
+#endif  // BUILDFLAG(IS_FUCHSIA)
   // Constructor with {sdpSemantics:"unified-plan"}.
   {
     V8TestingScope scope;
diff --git a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc
index 19d056a..b966170 100644
--- a/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.cc
@@ -192,14 +192,8 @@
   if (auto* media_stream_source = blink::ProcessedLocalAudioSource::From(
           blink::MediaStreamAudioSource::From(component_->Source()))) {
     local_track_audio_sink_->SetLevel(media_stream_source->audio_level());
-    // The sink only grabs stats from the audio processor. Stats are only
-    // available if WebRtc audio processing is turned on. Therefore, only
-    // provide the sink a reference to the processor if audio processing is
-    // turned on.
-    if (media_stream_source->HasWebRtcAudioProcessing()) {
-      local_track_audio_sink_->SetAudioProcessor(
-          media_stream_source->GetAudioProcessor());
-    }
+    local_track_audio_sink_->SetAudioProcessor(
+        media_stream_source->GetAudioProcessor());
   }
   native_track->AddSink(local_track_audio_sink_.get());
   webrtc_track_ = local_track_audio_sink_->webrtc_audio_track();
diff --git a/third_party/blink/renderer/modules/scheduler/BUILD.gn b/third_party/blink/renderer/modules/scheduler/BUILD.gn
index 5fc44e9..c7908c41 100644
--- a/third_party/blink/renderer/modules/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/modules/scheduler/BUILD.gn
@@ -14,6 +14,9 @@
     "dom_task_controller.h",
     "dom_task_signal.cc",
     "dom_task_signal.h",
+    "script_wrappable_task_id.h",
+    "task_attribution_tracker_impl.cc",
+    "task_attribution_tracker_impl.h",
     "task_priority_change_event.cc",
     "task_priority_change_event.h",
   ]
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
index 3f5dd8a1..40505db5 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
@@ -12,7 +12,8 @@
 #include "third_party/blink/renderer/modules/scheduler/dom_task.h"
 #include "third_party/blink/renderer/modules/scheduler/dom_task_signal.h"
 #include "third_party/blink/renderer/platform/bindings/enumeration_base.h"
-#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
 #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
 
@@ -99,6 +100,40 @@
   return resolver->Promise();
 }
 
+scheduler::TaskIdType DOMScheduler::taskId(ScriptState* script_state) {
+  ThreadScheduler* scheduler = ThreadScheduler::Current();
+  DCHECK(scheduler);
+  DCHECK(scheduler->GetTaskAttributionTracker());
+  absl::optional<scheduler::TaskId> task_id =
+      scheduler->GetTaskAttributionTracker()->RunningTaskId(script_state);
+  // task_id cannot be unset here, as a task has presumably already ran in order
+  // for this API call to be called.
+  DCHECK(task_id);
+  return task_id.value().value();
+}
+
+AtomicString DOMScheduler::isAncestor(ScriptState* script_state,
+                                      scheduler::TaskIdType parentId) {
+  scheduler::TaskAttributionTracker::AncestorStatus status =
+      scheduler::TaskAttributionTracker::AncestorStatus::kNotAncestor;
+  ThreadScheduler* scheduler = ThreadScheduler::Current();
+  DCHECK(scheduler);
+  scheduler::TaskAttributionTracker* tracker =
+      scheduler->GetTaskAttributionTracker();
+  DCHECK(tracker);
+  status = tracker->IsAncestor(script_state, scheduler::TaskId(parentId));
+  switch (status) {
+    case scheduler::TaskAttributionTracker::AncestorStatus::kAncestor:
+      return "ancestor";
+    case scheduler::TaskAttributionTracker::AncestorStatus::kNotAncestor:
+      return "not ancestor";
+    case scheduler::TaskAttributionTracker::AncestorStatus::kUnknown:
+      return "unknown";
+  }
+  NOTREACHED();
+  return "not reached";
+}
+
 void DOMScheduler::CreateFixedPriorityTaskQueues(ExecutionContext* context) {
   FrameOrWorkerScheduler* scheduler = context->GetScheduler();
   for (size_t i = 0; i < kWebSchedulingPriorityCount; i++) {
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
index fd9571d0..49d5b4b 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_id.h"
 #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -74,6 +75,9 @@
                          SchedulerPostTaskOptions*,
                          ExceptionState&);
 
+  scheduler::TaskIdType taskId(ScriptState*);
+  AtomicString isAncestor(ScriptState*, scheduler::TaskIdType parent_id);
+
   void ContextDestroyed() override;
 
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.idl b/third_party/blink/renderer/modules/scheduler/scheduler.idl
index 38f2707..411b316 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.idl
@@ -6,10 +6,20 @@
 //
 // currentTaskSignal:
 // https://github.com/WICG/scheduling-apis/blob/main/explainers/post-task-propagation.md
+
+// https://docs.google.com/document/d/1_m-h9_KgDMddTS2OFP0CShr4zjU-C-up64DwCrCfBo4
+enum AncestorStatus {
+    "ancestor",
+    "not ancestor",
+    "unknown"
+};
+
 [
     Exposed=(Window,Worker),
     ImplementedAs=DOMScheduler,
     RuntimeEnabled=WebScheduler
 ] interface Scheduler {
     [CallWith=ScriptState, MeasureAs=SchedulerPostTask, RaisesException] Promise<any> postTask(SchedulerPostTaskCallback callback, optional SchedulerPostTaskOptions options = {});
+    [RuntimeEnabled=UnexposedTaskIds, CallWith=ScriptState, Exposed=Window] readonly attribute unsigned long taskId;
+    [RuntimeEnabled=UnexposedTaskIds, CallWith=ScriptState, Exposed=Window] AncestorStatus isAncestor(unsigned long parentId);
 };
diff --git a/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h
new file mode 100644
index 0000000..d75776c
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h
@@ -0,0 +1,24 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCRIPT_WRAPPABLE_TASK_ID_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCRIPT_WRAPPABLE_TASK_ID_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/v8_set_return_value.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_id.h"
+
+namespace blink {
+
+class ScriptWrappableTaskId final : public ScriptWrappable,
+                                    public scheduler::TaskId {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit ScriptWrappableTaskId(const scheduler::TaskId& id) : TaskId(id) {}
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_SCRIPT_WRAPPABLE_TASK_ID_H_
diff --git a/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl
new file mode 100644
index 0000000..f3b5a4f
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.idl
@@ -0,0 +1,10 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This interface of not really web-exposed, and only used to generate a
+// ScriptWrappable object, that would pass along the TaskId to V8 and back, as
+// continuation embedder data.
+interface ScriptWrappableTaskId {
+  readonly attribute long value;
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
new file mode 100644
index 0000000..75a6c8c
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
@@ -0,0 +1,194 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
+#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_script_wrappable_task_id.h"
+#include "third_party/blink/renderer/modules/scheduler/script_wrappable_task_id.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/to_v8.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+namespace blink::scheduler {
+
+namespace {
+
+static unsigned Hash(TaskId id) {
+  return id.value() % TaskAttributionTrackerImpl::kVectorSize;
+}
+
+}  // namespace
+
+TaskAttributionTrackerImpl::TaskAttributionTrackerImpl()
+    : next_task_id_(0), v8_adapter_(std::make_unique<V8Adapter>()) {}
+
+absl::optional<TaskId> TaskAttributionTrackerImpl::RunningTaskId(
+    ScriptState* script_state) const {
+  DCHECK(v8_adapter_);
+  absl::optional<TaskId> task_id = v8_adapter_->GetValue(script_state);
+
+  // V8 embedder state may have no value in the case of a JSPromise that wasn't
+  // yet resolved.
+  return task_id ? task_id : running_task_id_;
+}
+
+void TaskAttributionTrackerImpl::InsertTaskIdPair(
+    TaskId task_id,
+    absl::optional<TaskId> parent_task_id) {
+  unsigned task_id_hash = Hash(task_id);
+  task_container_[task_id_hash] = TaskIdPair(parent_task_id, task_id);
+}
+
+TaskAttributionTrackerImpl::TaskIdPair&
+TaskAttributionTrackerImpl::GetTaskIdPairFromTaskContainer(TaskId id) {
+  unsigned slot = Hash(id);
+  DCHECK_LT(slot, task_container_.size());
+  return (task_container_[slot]);
+}
+
+TaskAttributionTracker::AncestorStatus TaskAttributionTrackerImpl::IsAncestor(
+    ScriptState* script_state,
+    TaskId ancestor_id) {
+  absl::optional<TaskId> current_task_id = RunningTaskId(script_state);
+  DCHECK(current_task_id);
+  if (current_task_id.value() == ancestor_id) {
+    return AncestorStatus::kAncestor;
+  }
+
+  // Each slot in the `task_container_` contains a struct with a parent and
+  // current task ID optionals. The loop "climbs" up that task dependency tree
+  // until it either reaches the "root" task (which has no parent), or until it
+  // finds a parent, which current task ID doesn't match the one its child
+  // pointed at, indicating that the parent's slot in the array was overwritten.
+  // In that case, it's returning the kUnknown value.
+  const TaskIdPair& current_pair =
+      GetTaskIdPairFromTaskContainer(current_task_id.value());
+  absl::optional<TaskId> parent_id = current_pair.parent;
+  DCHECK(current_pair.current);
+  while (parent_id) {
+    const TaskIdPair& parent_pair =
+        GetTaskIdPairFromTaskContainer(parent_id.value());
+    if (parent_pair.current && parent_pair.current != parent_id) {
+      // Found a parent slot, but its ID doesn't match what we thought it would
+      // be. That means we circled around the circular array, and we can no
+      // longer know the ancestry.
+      return AncestorStatus::kUnknown;
+    }
+    if (parent_id.value() == ancestor_id) {
+      return AncestorStatus::kAncestor;
+    }
+    DCHECK(parent_pair.current.value() != current_task_id.value());
+    DCHECK(!parent_pair.parent || parent_pair.parent < parent_id);
+    parent_id = parent_pair.parent;
+  }
+  return AncestorStatus::kNotAncestor;
+}
+
+std::unique_ptr<TaskAttributionTracker::TaskScope>
+TaskAttributionTrackerImpl::CreateTaskScope(
+    ScriptState* script_state,
+    absl::optional<TaskId> parent_task_id) {
+  next_task_id_ = next_task_id_.NextTaskId();
+  running_task_id_ = next_task_id_;
+
+  InsertTaskIdPair(next_task_id_, parent_task_id);
+  running_task_ids_.push_back(next_task_id_);
+
+  SaveTaskIdStateInV8(script_state, next_task_id_);
+  return std::make_unique<TaskScopeImpl>(script_state, this, next_task_id_);
+}
+
+void TaskAttributionTrackerImpl::TaskScopeCompleted(ScriptState* script_state,
+                                                    TaskId scope_id) {
+  DCHECK(!running_task_ids_.IsEmpty());
+  DCHECK(running_task_ids_.back() == scope_id);
+  running_task_ids_.pop_back();
+  if (!running_task_ids_.IsEmpty()) {
+    running_task_id_ = running_task_ids_.back();
+  } else {
+    running_task_id_ = absl::nullopt;
+  }
+  SaveTaskIdStateInV8(script_state, running_task_id_);
+}
+
+void TaskAttributionTrackerImpl::SaveTaskIdStateInV8(
+    ScriptState* script_state,
+    absl::optional<TaskId> task_id) {
+  DCHECK(v8_adapter_);
+  v8_adapter_->SetValue(script_state, task_id);
+}
+
+// TaskScope's implementation
+//////////////////////////////////////
+TaskAttributionTrackerImpl::TaskScopeImpl::TaskScopeImpl(
+    ScriptState* script_state,
+    TaskAttributionTrackerImpl* task_tracker,
+    TaskId scope_task_id)
+    : task_tracker_(task_tracker),
+      scope_task_id_(scope_task_id),
+      script_state_(script_state) {}
+
+TaskAttributionTrackerImpl::TaskScopeImpl::~TaskScopeImpl() {
+  task_tracker_->TaskScopeCompleted(script_state_, scope_task_id_);
+}
+
+// V8Adapter's implementation
+//////////////////////////////////////
+absl::optional<TaskId> TaskAttributionTrackerImpl::V8Adapter::GetValue(
+    ScriptState* script_state) {
+  DCHECK(script_state);
+  if (!script_state->ContextIsValid()) {
+    return absl::nullopt;
+  }
+
+  v8::Local<v8::Context> context = script_state->GetContext();
+  DCHECK(!context.IsEmpty());
+  v8::Local<v8::Value> v8_value =
+      context->GetContinuationPreservedEmbedderData();
+  if (v8_value->IsNullOrUndefined()) {
+    return absl::nullopt;
+  }
+  v8::Isolate* isolate = script_state->GetIsolate();
+  DCHECK(isolate);
+  // If not empty, the value must be a ScriptWrappableTaskId.
+  NonThrowableExceptionState exception_state;
+  ScriptWrappableTaskId* script_wrappable_task_id =
+      NativeValueTraits<ScriptWrappableTaskId>::NativeValue(isolate, v8_value,
+                                                            exception_state);
+  DCHECK(script_wrappable_task_id);
+  return *script_wrappable_task_id;
+}
+
+void TaskAttributionTrackerImpl::V8Adapter::SetValue(
+    ScriptState* script_state,
+    absl::optional<TaskId> task_id) {
+  DCHECK(script_state);
+  if (!script_state->ContextIsValid()) {
+    return;
+  }
+  ScriptState::Scope scope(script_state);
+  v8::Isolate* isolate = script_state->GetIsolate();
+  DCHECK(isolate);
+  v8::Local<v8::Context> context = script_state->GetContext();
+  DCHECK(!context.IsEmpty());
+
+  if (task_id) {
+    ScriptWrappableTaskId* script_wrappable_task_id =
+        MakeGarbageCollected<ScriptWrappableTaskId>(task_id.value());
+    context->SetContinuationPreservedEmbedderData(
+        ToV8Traits<ScriptWrappableTaskId>::ToV8(script_state,
+                                                script_wrappable_task_id)
+            .ToLocalChecked());
+  } else {
+    context->SetContinuationPreservedEmbedderData(v8::Local<v8::Value>());
+  }
+}
+
+}  // namespace blink::scheduler
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h
new file mode 100644
index 0000000..193bebb
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h
@@ -0,0 +1,112 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_ATTRIBUTION_TRACKER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_TASK_ATTRIBUTION_TRACKER_IMPL_H_
+
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_id.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink::scheduler {
+
+class TaskAttributionTrackerTest;
+
+// This class is used to keep track of tasks posted on the main thread and their
+// ancestry. It assigns an incerementing ID per task, and gets notified when a
+// task is posted, started or ended, and using that, it keeps track of which
+// task is the parent of the current task, and stores that info for later. It
+// then enables callers to determine if a certain task ID is an ancestor of the
+// current task.
+class MODULES_EXPORT TaskAttributionTrackerImpl
+    : public TaskAttributionTracker {
+  friend class TaskAttributionTrackerTest;
+
+ public:
+  TaskAttributionTrackerImpl();
+
+  absl::optional<TaskId> RunningTaskId(ScriptState*) const override;
+
+  AncestorStatus IsAncestor(ScriptState*, TaskId parent_id) override;
+
+  std::unique_ptr<TaskScope> CreateTaskScope(
+      ScriptState* script_state,
+      absl::optional<TaskId> parent_task_id) override;
+
+  // The vector size limits the amount of tasks we keep track of. Setting this
+  // value too small can result in calls to `IsAncestor` returning an `Unknown`
+  // ancestor status. If this happens a lot in realistic scenarios, we'd need to
+  // increase this value (at the expense of memory dedicated to task tracking).
+  static constexpr size_t kVectorSize = 1024;
+
+  void SetRunningTaskId(absl::optional<TaskId> id) { running_task_id_ = id; }
+
+  void TaskScopeCompleted(ScriptState*, TaskId);
+
+ private:
+  struct TaskIdPair {
+    TaskIdPair() = default;
+    TaskIdPair(absl::optional<TaskId> parent_id,
+               absl::optional<TaskId> current_id)
+        : parent(parent_id), current(current_id) {}
+
+    explicit operator bool() const { return parent.has_value(); }
+    absl::optional<TaskId> parent;
+    absl::optional<TaskId> current;
+  };
+
+  class TaskScopeImpl : public TaskScope {
+   public:
+    TaskScopeImpl(ScriptState*, TaskAttributionTrackerImpl*, TaskId);
+    ~TaskScopeImpl() override;
+    TaskScopeImpl(const TaskScopeImpl&) = delete;
+    TaskScopeImpl& operator=(const TaskScopeImpl&) = delete;
+
+   private:
+    TaskAttributionTrackerImpl* task_tracker_;
+    TaskId scope_task_id_;
+    Persistent<ScriptState> script_state_;
+  };
+
+  class MODULES_EXPORT V8Adapter {
+   public:
+    virtual absl::optional<TaskId> GetValue(ScriptState*);
+    virtual void SetValue(ScriptState*, absl::optional<TaskId>);
+    virtual ~V8Adapter() = default;
+  };
+
+  TaskIdPair& GetTaskIdPairFromTaskContainer(TaskId);
+  void InsertTaskIdPair(TaskId task_id, absl::optional<TaskId> parent_task_id);
+  void SaveTaskIdStateInV8(ScriptState*, absl::optional<TaskId>);
+
+  void SetV8AdapterForTesting(std::unique_ptr<V8Adapter> adapter) {
+    v8_adapter_.swap(adapter);
+  }
+
+  TaskId next_task_id_;
+  absl::optional<TaskId> running_task_id_;
+  WTF::Vector<TaskId> running_task_ids_;
+
+  std::unique_ptr<V8Adapter> v8_adapter_;
+
+  // The task container is a vector of optional TaskIdPairs where its indexes
+  // are TaskId hashes, and its values are the TaskId of the parent task for the
+  // TaskId that is resulted in the index. We're using this vector as a circular
+  // array, where in order to find if task A is an ancestor of task B, we look
+  // up the value at B's taskId hash position, get its parent, and repeat that
+  // process until we either find A in the ancestor chain, get no parent task
+  // (indicating that a task has no parent, so wasn't initiated by another JS
+  // task), or reach a parent that doesn't have the current ID its child though
+  // it should have, which indicates that the parent was overwritten by a newer
+  // task, indicating that we went "full circle".
+  WTF::Vector<TaskIdPair> task_container_ =
+      WTF::Vector<TaskIdPair>(kVectorSize);
+};
+
+}  // namespace blink::scheduler
+
+#endif
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl_test.cc b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl_test.cc
new file mode 100644
index 0000000..a5d3744e
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_id.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink::scheduler {
+
+class TaskAttributionTrackerTest : public testing::Test {
+ protected:
+  WTF::Vector<std::unique_ptr<TaskAttributionTracker::TaskScope>> task_stack_;
+
+ public:
+  class MockV8Adapter : public TaskAttributionTrackerImpl::V8Adapter {
+   public:
+    absl::optional<TaskId> GetValue(ScriptState*) override { return value_; }
+    void SetValue(ScriptState*, absl::optional<TaskId> task_id) override {
+      value_ = task_id;
+    }
+
+   private:
+    absl::optional<TaskId> value_;
+  };
+
+  void PostTasks(TaskAttributionTrackerImpl& tracker,
+                 unsigned task_number,
+                 unsigned number_to_assert,
+                 TaskIdType parent_to_assert,
+                 bool complete,
+                 TaskId* task_id = nullptr) {
+    TaskId previous_task_id = TaskId(parent_to_assert);
+    for (unsigned i = 0; i < task_number; ++i) {
+      task_stack_.push_back(tracker.CreateTaskScope(nullptr, previous_task_id));
+      absl::optional<TaskId> running_task_id = tracker.RunningTaskId(nullptr);
+      if (i < number_to_assert) {
+        // Make sure that the parent task is an ancestor.
+        TaskId parent_task(parent_to_assert);
+        TaskAttributionTracker::AncestorStatus is_ancestor =
+            tracker.IsAncestor(nullptr, parent_task);
+        ASSERT_TRUE(is_ancestor ==
+                    TaskAttributionTracker::AncestorStatus::kAncestor);
+        if (!complete) {
+          ASSERT_TRUE(tracker.IsAncestor(nullptr, previous_task_id) ==
+                      TaskAttributionTracker::AncestorStatus::kAncestor);
+        }
+      }
+      if (task_id) {
+        *task_id = running_task_id.value();
+      }
+      previous_task_id = running_task_id.value();
+      if (complete) {
+        task_stack_.pop_back();
+      }
+    }
+  }
+
+  void TestAttributionQueue(unsigned overflow_length,
+                            unsigned asserts_length,
+                            bool nested_tasks_complete,
+                            bool assert_last_task) {
+    TaskAttributionTrackerImpl tracker;
+    MockV8ForTracker(tracker);
+    // Post tasks for half the queue.
+    unsigned half_queue = TaskAttributionTrackerImpl::kVectorSize / 2;
+    TaskId task_id(0);
+    PostTasks(tracker, half_queue, /*number_to_assert=*/0,
+              /*parent_to_assert=*/0,
+              /*complete=*/true, &task_id);
+    // Verify that the ID of the last task that ran is what we expect it to be.
+    ASSERT_EQ(half_queue, task_id.value());
+    // Start the parent task, but don't complete it.
+    task_stack_.push_back(
+        tracker.CreateTaskScope(nullptr, tracker.RunningTaskId(nullptr)));
+    // Get its ID.
+    TaskId parent_task_id = tracker.RunningTaskId(nullptr).value();
+    // Post |overflow_length| tasks.
+    PostTasks(tracker, overflow_length, asserts_length, parent_task_id.value(),
+              nested_tasks_complete);
+    if (assert_last_task && ((overflow_length + half_queue) >
+                             TaskAttributionTrackerImpl::kVectorSize)) {
+      // Post another task.
+      task_stack_.push_back(
+          tracker.CreateTaskScope(nullptr, tracker.RunningTaskId(nullptr)));
+      // Since it goes beyond the queue length and the parent task was
+      // overwritten, we cannot track ancestry.
+      ASSERT_TRUE(tracker.IsAncestor(nullptr, parent_task_id) !=
+                  TaskAttributionTracker::AncestorStatus::kAncestor);
+      if (nested_tasks_complete) {
+        task_stack_.pop_back();
+      }
+    }
+    // Complete all the tasks
+    while (!task_stack_.IsEmpty()) {
+      task_stack_.pop_back();
+    }
+  }
+
+  void MockV8ForTracker(TaskAttributionTrackerImpl& tracker) {
+    tracker.SetV8AdapterForTesting(std::make_unique<MockV8Adapter>());
+  }
+};
+
+TEST_F(TaskAttributionTrackerTest, TrackTaskLargerThanQueue) {
+  TestAttributionQueue(
+      /*overflow_length=*/TaskAttributionTrackerImpl::kVectorSize - 1,
+      /*asserts_length=*/TaskAttributionTrackerImpl::kVectorSize - 1,
+      /*nested_tasks_complete=*/true,
+      /*assert_last_task=*/true);
+}
+
+TEST_F(TaskAttributionTrackerTest, TrackTwoTasksAcrossTheQueue) {
+  TestAttributionQueue(
+      /*overflow_length=*/TaskAttributionTrackerImpl::kVectorSize + 100,
+      /*asserts_length=*/TaskAttributionTrackerImpl::kVectorSize - 1,
+      /*nested_tasks_complete=*/true,
+      /*assert_last_task=*/true);
+}
+
+TEST_F(TaskAttributionTrackerTest, CausalityChain) {
+  TestAttributionQueue(/*overflow_length=*/100, /*asserts_length=*/100,
+                       /*nested_tasks_complete=*/false,
+                       /*assert_last_task=*/false);
+}
+
+TEST_F(TaskAttributionTrackerTest, CausalityChainOverflow) {
+  TestAttributionQueue(
+      /*overflow_length=*/TaskAttributionTrackerImpl::kVectorSize - 1,
+      /*asserts_length=*/TaskAttributionTrackerImpl::kVectorSize - 1,
+      /*nested_tasks_complete=*/false,
+      /*assert_last_task=*/true);
+}
+
+TEST_F(TaskAttributionTrackerTest, NotAncestor) {
+  TaskAttributionTrackerImpl tracker;
+  MockV8ForTracker(tracker);
+  TaskId first_task_id(0);
+
+  // Start a task, get its ID and complete it.
+  {
+    auto scope = tracker.CreateTaskScope(nullptr, absl::nullopt);
+    first_task_id = tracker.RunningTaskId(nullptr).value();
+  }
+
+  // Start an incomplete task.
+  auto scope = tracker.CreateTaskScope(nullptr, absl::nullopt);
+  // Make sure that the first task is not an ancestor.
+  ASSERT_TRUE(tracker.IsAncestor(nullptr, first_task_id) ==
+              TaskAttributionTracker::AncestorStatus::kNotAncestor);
+}
+
+}  // namespace blink::scheduler
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index fd0f1f1f..7e9e2c3 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -5579,6 +5579,14 @@
   bool have_source_canvas_webgl_context = source_canvas_webgl_context;
   DCHECK(have_source_image ^ have_source_canvas_webgl_context);
 
+  if (source_canvas_webgl_context &&
+      source_canvas_webgl_context->isContextLost()) {
+    SynthesizeGLError(GL_INVALID_OPERATION,
+                      GetTexImageFunctionName(function_id),
+                      "Can't upload a texture from a lost WebGL context.");
+    return;
+  }
+
   int width = source_sub_rectangle.width();
   int height = source_sub_rectangle.height();
 
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.cc b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
index 78bfb58..38010b7f 100644
--- a/third_party/blink/renderer/platform/bindings/callback_function_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
@@ -6,6 +6,8 @@
 
 #include "third_party/blink/renderer/platform/bindings/binding_security_for_platform.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 
 namespace blink {
 
@@ -28,6 +30,10 @@
           BindingSecurityForPlatform::ErrorReportOption::kDoNotReport)) {
     callback_relevant_script_state_ =
         ScriptState::From(creation_context.ToLocalChecked());
+    if (auto* tracker =
+            ThreadScheduler::Current()->GetTaskAttributionTracker()) {
+      parent_task_id_ = tracker->RunningTaskId(callback_relevant_script_state_);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.h b/third_party/blink/renderer/platform/bindings/callback_function_base.h
index dcede63..3ee271c 100644
--- a/third_party/blink/renderer/platform/bindings/callback_function_base.h
+++ b/third_party/blink/renderer/platform/bindings/callback_function_base.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_id.h"
 
 namespace blink {
 
@@ -88,6 +89,10 @@
     callback_function_.Reset();
   }
 
+  absl::optional<scheduler::TaskId> GetParentTaskId() const {
+    return parent_task_id_;
+  }
+
  protected:
   explicit CallbackFunctionBase(v8::Local<v8::Object>);
 
@@ -107,6 +112,8 @@
   // converted to an IDL value.
   // https://webidl.spec.whatwg.org/#dfn-callback-context
   Member<ScriptState> incumbent_script_state_;
+
+  absl::optional<scheduler::TaskId> parent_task_id_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
index 55cc3ad..cfce04f 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
@@ -6,6 +6,8 @@
 
 #include "third_party/blink/renderer/platform/bindings/binding_security_for_platform.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 
 namespace blink {
 
@@ -31,6 +33,10 @@
           BindingSecurityForPlatform::ErrorReportOption::kDoNotReport)) {
     callback_relevant_script_state_ =
         ScriptState::From(creation_context.ToLocalChecked());
+    if (auto* tracker =
+            ThreadScheduler::Current()->GetTaskAttributionTracker()) {
+      parent_task_id_ = tracker->RunningTaskId(callback_relevant_script_state_);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
index 43cf545..6d84207c 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.h
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_id.h"
 
 namespace blink {
 
@@ -82,6 +83,10 @@
 
   DOMWrapperWorld& GetWorld() const { return incumbent_script_state_->World(); }
 
+  absl::optional<scheduler::TaskId> GetParentTaskId() const {
+    return parent_task_id_;
+  }
+
  protected:
   explicit CallbackInterfaceBase(v8::Local<v8::Object> callback_object,
                                  SingleOperationOrNot);
@@ -98,6 +103,8 @@
   // converted to an IDL value.
   // https://webidl.spec.whatwg.org/#dfn-callback-context
   Member<ScriptState> incumbent_script_state_;
+
+  absl::optional<scheduler::TaskId> parent_task_id_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
index cd220c5..098e4495 100644
--- a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
+++ b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
@@ -225,21 +225,6 @@
   radii_.OutsetForMarginOrShadow(size);
 }
 
-void FloatRoundedRect::InflateWithRadii(int size) {
-  gfx::RectF old = rect_;
-
-  rect_.Outset(size);
-  // Considering the inflation factor of shorter size to scale the radii seems
-  // appropriate here
-  float factor;
-  if (rect_.width() < rect_.height())
-    factor = old.width() ? (float)rect_.width() / old.width() : int(0);
-  else
-    factor = old.height() ? (float)rect_.height() / old.height() : int(0);
-
-  radii_.Scale(factor);
-}
-
 bool FloatRoundedRect::IntersectsQuad(const gfx::QuadF& quad) const {
   if (!quad.IntersectsRect(rect_))
     return false;
diff --git a/third_party/blink/renderer/platform/geometry/float_rounded_rect.h b/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
index 68eb652..f4b088e4 100644
--- a/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
+++ b/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
@@ -138,8 +138,6 @@
   void SetRadii(const Radii& radii) { radii_ = radii; }
 
   void Move(const gfx::Vector2dF& offset) { rect_.Offset(offset); }
-  // TODO(wangxianzhu): Consider merging this into Outset().
-  void InflateWithRadii(int size);
 
   // Inflates/shrinks the rounded rect by the specified amount on each side and
   // corner. Zero widths and heights of radii are kept zero so that sharp
diff --git a/third_party/blink/renderer/platform/media/key_system_config_selector.cc b/third_party/blink/renderer/platform/media/key_system_config_selector.cc
index fa06a896..1e90c89c 100644
--- a/third_party/blink/renderer/platform/media/key_system_config_selector.cc
+++ b/third_party/blink/renderer/platform/media/key_system_config_selector.cc
@@ -992,9 +992,6 @@
     const WebString& key_system,
     const WebVector<WebMediaKeySystemConfiguration>& candidate_configurations,
     SelectConfigCB cb) {
-  DCHECK(key_systems_->IsUpToDate())
-      << "The caller must make sure the Key Systems are up to date";
-
   // Continued from requestMediaKeySystemAccess(), step 6, from
   // https://w3c.github.io/encrypted-media/#requestmediakeysystemaccess
   //
diff --git a/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc b/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc
index 07e5650..3ad3405 100644
--- a/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc
+++ b/third_party/blink/renderer/platform/media/key_system_config_selector_unittest.cc
@@ -199,12 +199,10 @@
   ~FakeKeySystems() override = default;
 
   void UpdateIfNeeded(base::OnceClosure done_cb) override {
-    NOTREACHED() << "Not needed since IsUpToDate() always returns true";
+    // Call the callback directly since it's always up to date.
     std::move(done_cb).Run();
   }
 
-  bool IsUpToDate() override { return true; }
-
   std::string GetBaseKeySystemName(
       const std::string& key_system) const override {
     DCHECK(IsSupportedKeySystem(key_system));
diff --git a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
index 6a824434..cbc8011 100644
--- a/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_encrypted_media_client_impl.cc
@@ -113,15 +113,10 @@
     WebEncryptedMediaRequest request) {
   GetReporter(request.KeySystem())->ReportRequested();
 
-  if (!key_systems_->IsUpToDate()) {
-    pending_requests_.push_back(std::move(request));
-    key_systems_->UpdateIfNeeded(
-        base::BindOnce(&WebEncryptedMediaClientImpl::OnKeySystemsUpdated,
-                       weak_factory_.GetWeakPtr()));
-    return;
-  }
-
-  SelectConfig(request);
+  pending_requests_.push_back(std::move(request));
+  key_systems_->UpdateIfNeeded(
+      base::BindOnce(&WebEncryptedMediaClientImpl::OnKeySystemsUpdated,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void WebEncryptedMediaClientImpl::CreateCdm(
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc
index 34298fe3..5b4c8433 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc
@@ -129,22 +129,6 @@
   return device().matched_output_device_id.has_value();
 }
 
-bool MediaStreamAudioSource::AllTracksAreDisabled() {
-  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
-
-  unsigned int num_disabled_tracks = 0;
-  Vector<MediaStreamAudioTrack*> audio_tracks;
-  deliverer_.GetConsumerList(&audio_tracks);
-  for (MediaStreamAudioTrack* track : audio_tracks) {
-    if (!track->IsEnabled())
-      ++num_disabled_tracks;
-  }
-  LogMessage(base::StringPrintf("%s => (%u of %u tracks are disabled))",
-                                __func__, num_disabled_tracks,
-                                audio_tracks.size()));
-  return (num_disabled_tracks == audio_tracks.size());
-}
-
 void* MediaStreamAudioSource::GetClassIdentifier() const {
   return nullptr;
 }
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h
index 66912cb8..7867fa9 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h
@@ -103,9 +103,6 @@
   bool disable_local_echo() const { return disable_local_echo_; }
   bool RenderToAssociatedSinkEnabled() const;
 
-  // Checks all tracks acting as consumers and returns true if all are disabled.
-  bool AllTracksAreDisabled();
-
   // Returns a unique class identifier. Some subclasses override and use this
   // method to provide safe down-casting to their type.
   virtual void* GetClassIdentifier() const;
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
index 5d26751..2725f226 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -1196,9 +1196,9 @@
 
   // All non-native frames require a copy because we can't tell if non-copy
   // conditions are met.
-  bool requires_copy =
+  bool requires_copy_or_scale =
       buffer->type() != webrtc::VideoFrameBuffer::Type::kNative;
-  if (!requires_copy) {
+  if (!requires_copy_or_scale) {
     const WebRtcVideoFrameAdapter* frame_adapter =
         static_cast<WebRtcVideoFrameAdapter*>(buffer.get());
     frame = frame_adapter->getMediaVideoFrame();
@@ -1206,25 +1206,32 @@
     const bool is_shmem_frame = storage == media::VideoFrame::STORAGE_SHMEM;
     const bool is_gmb_frame =
         storage == media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER;
-    requires_copy =
+    requires_copy_or_scale =
         RequiresSizeChange(*frame) || !(is_shmem_frame || is_gmb_frame);
   }
 
-  if (requires_copy) {
+  if (requires_copy_or_scale) {
     const base::TimeDelta timestamp =
         frame ? frame->timestamp()
               : base::Milliseconds(next_frame->ntp_time_ms());
-    // TODO(https://crbug.com/1194500): Android (e.g. android-pie-arm64-rel)
-    // and CrOS does not support the optimzed path, perhaps due to not
-    // supporting STORAGE_GPU_MEMORY_BUFFER or NV12? When this is fixed, remove
-    // the special casing on platform and the legacy code path.
-    bool optimized_scaling =
+    // Native buffer scaling is performed by WebRtcVideoFrameAdapter, which may
+    // be more efficient in some cases. E.g. avoiding I420 conversion or scaling
+    // from a middle layer instead of top layer.
+    //
+    // Native buffer scaling is only supported when `input_frame_coded_size_`
+    // and `input_visible_size_` strides match. This ensures the strides of the
+    // frame that we pass to the encoder fits the input requirements.
+    bool native_buffer_scaling =
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
-        buffer->type() == webrtc::VideoFrameBuffer::Type::kNative;
+        buffer->type() == webrtc::VideoFrameBuffer::Type::kNative &&
+        input_frame_coded_size_.width() == input_visible_size_.width();
 #else
+        // TODO(https://crbug.com/1194500): Android (e.g. android-pie-arm64-rel)
+        // and CrOS does not support the native buffer scaling path. Investigate
+        // why and find a way to enable it, if possible.
         false;
 #endif
-    if (optimized_scaling) {
+    if (native_buffer_scaling) {
       DCHECK_EQ(buffer->type(), webrtc::VideoFrameBuffer::Type::kNative);
       auto scaled_buffer = buffer->Scale(input_visible_size_.width(),
                                          input_visible_size_.height());
@@ -1250,8 +1257,6 @@
         return;
       }
     } else {
-      // TODO(https://crbug.com/1194500): Remove this code path in favor of the
-      // above code path. This will allow us to remove |input_buffers_|.
       std::pair<base::UnsafeSharedMemoryRegion,
                 base::WritableSharedMemoryMapping>* input_buffer =
           input_buffers_[index].get();
@@ -1270,7 +1275,6 @@
 
       // Do a strided copy and scale (if necessary) the input frame to match
       // the input requirements for the encoder.
-      // TODO(sheu): Support zero-copy from WebRTC. http://crbug.com/269312
       // TODO(magjed): Downscale with an image pyramid instead.
       rtc::scoped_refptr<webrtc::I420BufferInterface> i420_buffer =
           next_frame->video_frame_buffer()->ToI420();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 3f68fb4..7505a8d 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1985,13 +1985,6 @@
       origin_trial_feature_name: "RtcAudioJitterBufferRtxHandling",
       status: "experimental",
     },
-    // Reverse Origin Trial for Plan B. When enabled through the Origin Trial,
-    // Plan B is available beyond the initial removal date of Plan B (M93).
-    {
-      name: "RTCExtendDeadlineForPlanBRemoval",
-      origin_trial_feature_name: "RTCExtendDeadlineForPlanBRemoval",
-      status: "test",
-    },
     // Enables the use of the RTCIceTransport with extensions.
     {
       name: "RTCIceTransportExtension",
@@ -2295,6 +2288,9 @@
       name: "TimeZoneChangeEvent",
       status: "experimental",
     },
+    {
+      name: "TopicsAPI",
+    },
     // This feature allows touch dragging and a context menu to occur
     // simultaneously, with the assumption that the menu is non-modal.  Without
     // this feature, a long-press touch gesture can start either a drag or a
@@ -2367,6 +2363,9 @@
       name: "UnderlineOffsetThickness",
       status: "stable",
     },
+    {
+      name: "UnexposedTaskIds",
+    },
     // This is a reverse OT used for a phased deprecation, on desktop
     // https://crbug.com/1071424
     {
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index c305569..3082f88a 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -126,6 +126,8 @@
     "public/rail_mode_observer.h",
     "public/scheduling_lifecycle_state.h",
     "public/scheduling_policy.h",
+    "public/task_attribution_tracker.h",
+    "public/task_id.h",
     "public/thread.h",
     "public/thread_cpu_throttler.h",
     "public/thread_scheduler.h",
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index 374727c..b176a68 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/rail_mode_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -266,6 +267,16 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> VirtualTimeControlTaskRunner();
 
+  TaskAttributionTracker* GetTaskAttributionTracker() override {
+    return main_thread_only().task_attribution_tracker.get();
+  }
+
+  void InitializeTaskAttributionTracker(
+      std::unique_ptr<TaskAttributionTracker> tracker) override {
+    DCHECK(!main_thread_only().task_attribution_tracker);
+    main_thread_only().task_attribution_tracker = std::move(tracker);
+  }
+
   // Returns a new task queue created with given params.
   scoped_refptr<MainThreadTaskQueue> NewTaskQueue(
       const MainThreadTaskQueue::QueueCreationParams& params);
@@ -909,6 +920,8 @@
     WTF::Vector<AgentGroupSchedulerScope> agent_group_scheduler_scope_stack;
 
     std::unique_ptr<power_scheduler::PowerModeVoter> audible_power_mode_voter;
+
+    std::unique_ptr<TaskAttributionTracker> task_attribution_tracker;
   };
 
   struct AnyThread {
diff --git a/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h
new file mode 100644
index 0000000..af303a9
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h
@@ -0,0 +1,58 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ATTRIBUTION_TRACKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ATTRIBUTION_TRACKER_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_id.h"
+
+namespace blink {
+class ScriptState;
+}  // namespace blink
+
+namespace blink::scheduler {
+
+// This public interface enables platform/ and core/ callers to create a task
+// scope on the one hand, and check on the ID of the currently running task as
+// well as its ancestry on the other.
+class PLATFORM_EXPORT TaskAttributionTracker {
+ public:
+  // Return value for encestry related queries.
+  enum class AncestorStatus {
+    kAncestor,
+    kNotAncestor,
+    kUnknown,
+  };
+
+  // A class maintaining the scope of the current task. Keeping it alive ensures
+  // that the current task is counted as a continuous one.
+  class TaskScope {
+   public:
+    virtual ~TaskScope() = default;
+    TaskScope(const TaskScope&) = delete;
+    TaskScope& operator=(const TaskScope&) = delete;
+
+   protected:
+    TaskScope() = default;
+  };
+
+  virtual ~TaskAttributionTracker() = default;
+
+  // Create a new task scope.
+  virtual std::unique_ptr<TaskScope> CreateTaskScope(
+      ScriptState*,
+      absl::optional<TaskId> parent_task_id) = 0;
+
+  // Get the ID of the currently running task.
+  virtual absl::optional<TaskId> RunningTaskId(ScriptState*) const = 0;
+  // Check for ancestry of the currently running task against an input
+  // |parentId|.
+  virtual AncestorStatus IsAncestor(ScriptState*, TaskId parentId) = 0;
+};
+
+}  // namespace blink::scheduler
+
+#endif
diff --git a/third_party/blink/renderer/platform/scheduler/public/task_id.h b/third_party/blink/renderer/platform/scheduler/public/task_id.h
new file mode 100644
index 0000000..84267f9
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/public/task_id.h
@@ -0,0 +1,35 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ID_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ID_H_
+
+#include <cstdint>
+#include "base/types/strong_alias.h"
+
+namespace blink::scheduler {
+
+using TaskIdType = uint32_t;
+
+// TaskId represents the ID of a task, enabling comparison and incrementation
+// operations on it, while abstracting the underlying value from callers.
+class TaskId {
+ public:
+  explicit TaskId(TaskIdType value) : value_(value) {}
+  TaskId(const TaskId&) = default;
+  TaskId& operator=(const TaskId&) = default;
+  scheduler::TaskIdType value() const { return value_; }
+
+  bool operator==(const TaskId& id) const { return id.value_ == value_; }
+  bool operator!=(const TaskId& id) const { return id.value_ != value_; }
+  bool operator<(const TaskId& id) const { return value_ < id.value_; }
+  TaskId NextTaskId() const { return TaskId(value_ + 1); }
+
+ private:
+  TaskIdType value_;
+};
+
+}  // namespace blink::scheduler
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_TASK_ID_H_
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
index dab565b7..32506d1 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -14,6 +14,7 @@
 #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -154,10 +155,19 @@
   // Associates |isolate| to the scheduler.
   virtual void SetV8Isolate(v8::Isolate* isolate) = 0;
 
+  // Returns the scheduler's TaskAttributionTracker is we're running on the main
+  // thread, or a nullptr otherwise.
+  virtual scheduler::TaskAttributionTracker* GetTaskAttributionTracker() {
+    return nullptr;
+  }
+
   // Test helpers.
 
   virtual scheduler::NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() = 0;
 
+  virtual void InitializeTaskAttributionTracker(
+      std::unique_ptr<scheduler::TaskAttributionTracker>) {}
+
  private:
   // For GetWebMainThreadScheduler().
   friend class scheduler::WebThreadScheduler;
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 6fef402a..ed1bd01 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -560,6 +560,9 @@
             # nested in the blink namespace.
             'internal::.+',
 
+            # TODO(crbug.com/1296161): Remove this when the CHIPS OT ends.
+            "net::features::kPartitionedCookiesBypassOriginTrial",
+
             # HTTP structured headers
             'net::structured_headers::.+',
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index 85f12b4..f15d5d8 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -39,7 +39,7 @@
 TIMEOUT_SECONDS = 210 * 60
 
 # Sheriff calendar URL, used for getting the ecosystem infra sheriff to cc.
-ROTATIONS_URL = 'https://chrome-ops-rotation-proxy.appspot.com/current/grotation:chrome-ecosystem-infra'
+ROTATIONS_URL = 'https://chrome-ops-rotation-proxy.appspot.com/current/grotation:chromium-wpt-two-way-sync'
 SHERIFF_EMAIL_FALLBACK = 'weizhong@google.com'
 RUBBER_STAMPER_BOT = 'rubber-stamper@appspot.gserviceaccount.com'
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index ef22101d..5154211 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -13,6 +13,10 @@
 # Tests that fail in legacy but pass in NG
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-cross-origin-subframe-navigation.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-main-frame-navigation.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-remove-subframe.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-same-origin-subframe-navigation.html [ Timeout ]
 crbug.com/626703 virtual/prerender/external/wpt/speculation-rules/prerender/opt-out.html [ Crash ]
 crbug.com/626703 virtual/no-forced-frame-updates/external/wpt/html/dom/render-blocking/header-inserted-preload-link.tentative.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
index 9e9bb4d..0caa6aa 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
+++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -47,6 +47,10 @@
 crbug.com/1209223 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-cross-origin-subframe-navigation.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-main-frame-navigation.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-remove-subframe.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-same-origin-subframe-navigation.html [ Timeout ]
 crbug.com/626703 virtual/prerender/external/wpt/speculation-rules/prerender/opt-out.html [ Crash ]
 crbug.com/626703 virtual/no-forced-frame-updates/external/wpt/html/dom/render-blocking/header-inserted-preload-link.tentative.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 3b1d9bfc..2c692c0 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -69,6 +69,10 @@
 # Most DevTools tests are slow in Debug.
 webkit.org/b/90488 [ Debug ] http/tests/devtools/* [ Slow ]
 webkit.org/b/90488 [ Debug ] inspector-protocol/* [ Slow ]
+webkit.org/b/90488 [ Debug ] http/tests/inspector-protocol/* [ Slow ]
+
+# Conversion tests are slow on Windows
+crbug.com/1306848 [ Win ] http/tests/inspector-protocol/conversion/* [ Slow ]
 
 # DevTools tests cause flakes on slow hardware in Release as well.
 crbug.com/1063051 [ Release ] http/tests/devtools/* [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index d137b67..a4486d4 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1633,6 +1633,18 @@
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/flex-container-fragmentation-007.tentative.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-015.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-016.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html [ Pass ]
+virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-007.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-008.html [ Pass ]
 virtual/layout_ng_flex_frag/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-010.html [ Pass ]
@@ -3444,6 +3456,13 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-grid/subgrid/subgrid-stretch.html [ Failure ]
+crbug.com/626703 [ Mac11 ] external/wpt/css/css-animations/parsing/animation-computed.html [ Failure Timeout ]
+crbug.com/626703 [ Mac11 ] virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed.html [ Failure Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-cross-origin-subframe-navigation.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-main-frame-navigation.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-remove-subframe.html [ Timeout ]
+crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-same-origin-subframe-navigation.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-backgrounds/background-clip/clip-text-flex.html [ Failure ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-backgrounds/background-clip/clip-text-flex.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-backgrounds/background-clip/clip-text-flex.html [ Failure ]
@@ -4290,6 +4309,10 @@
 crbug.com/626703 external/wpt/screen-orientation/orientation-reading.html [ Timeout ]
 crbug.com/626703 external/wpt/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-closeable.html [ Failure ]
 
+# Tests that rely on a non-exposed test-only API. Tested in virtual/task-tracking
+wpt_internal/task-tracking/* [ Skip Failure ]
+virtual/task-tracking/* [ Pass ]
+
 # navigation.sub.html fails or times out when run with run_web_tests.py but passes with run_wpt_tests.py
 crbug.com/626703 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html?encoding=windows-1252 [ Failure Timeout ]
 crbug.com/626703 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html?encoding=x-cp1251 [ Failure Timeout ]
@@ -4402,6 +4425,18 @@
 crbug.com/660611 external/wpt/css/css-break/flexbox/flex-container-fragmentation-007.tentative.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-015.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-016.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html [ Failure ]
+crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-007.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-008.html [ Failure ]
 crbug.com/660611 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-010.html [ Failure ]
@@ -6533,10 +6568,6 @@
 crbug.com/1205659 [ Mac ] storage/indexeddb/empty-blob-file.html [ Pass Timeout ]
 crbug.com/1205673 [ Linux ] virtual/threaded-prefer-compositing/fast/scroll-snap/snaps-after-wheel-scrolling-single-tick.html [ Failure Pass ]
 
-# Browser Infra 2021-05-04
-# Re-enable once all Linux CI/CQ builders migrated to Bionic
-crbug.com/1200134 [ Linux ] fast/gradients/unprefixed-repeating-gradient-color-hint.html [ Failure Pass ]
-
 # Sheriff 2021-05-05
 crbug.com/1205796 [ Mac10.12 ] external/wpt/html/dom/idlharness.https.html?include=HTML.* [ Failure ]
 crbug.com/1205796 [ Mac10.14 ] external/wpt/html/dom/idlharness.https.html?include=HTML.* [ Failure ]
@@ -7683,9 +7714,6 @@
 crbug.com/1296179 [ Mac Release ] virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/service-worker-background-fetch.https.window.html [ Failure Pass ]
 crbug.com/1296179 [ Linux Release ] virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/service-worker-background-fetch.https.window.html [ Failure Pass ]
 
-# Flaky due to https://crrev.com/c/3453430
-crbug.com/1298944 virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https.html [ Failure Pass ]
-
 # Fails flakily on Win
 crbug.com/1173382 [ Win ] virtual/stable/http/tests/navigation/replacestate-base-legal.html [ Failure Pass ]
 
@@ -7699,6 +7727,10 @@
 crbug.com/1299948 [ Mac ] external/wpt/css/css-tables/crashtests/textarea-intrinsic-size-crash.html [ Pass Timeout ]
 crbug.com/1299972 [ Linux ] screen_orientation/screenorientation-unsupported-no-crash.html [ Failure Pass Timeout ]
 
+# Until Plan B is removed on Fuchsia, allow these tests to fail to allow contradictory expectations.
+crbug.com/1302249 [ Fuchsia ] external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported.html [ Failure Pass ]
+crbug.com/1302249 [ Fuchsia ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported.html [ Failure Pass ]
+
 # Disable flaky test for further investigation
 crbug.com/1229801 http/tests/devtools/elements/css-rule-hover-highlights-selectors.js [ Failure Pass ]
 
@@ -7730,5 +7762,7 @@
 crbug.com/1298633 http/tests/inspector-protocol/network/cross-origin-isolation/coep-load-error-reporting-worker.js [ Pass Timeout ]
 
 # Sheriff 2022-03-16
-crbug.com/1306848 http/tests/inspector-protocol/conversion/attribution-invalid-data.js [ Failure Timeout Pass ]
 crbug.com/1306770 http/tests/inspector-protocol/network/interception-download.js [ Failure Pass ]
+
+# Sheriff 2022-03-18
+crbug.com/1290040 [ Linux ] external/wpt/service-workers/service-worker/same-site-cookies.https.html [ Skip ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 37251c11..5fb67156 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -557,7 +557,8 @@
   },
   {
     "prefix": "dark-color-scheme",
-    "bases": ["external/wpt/css/css-color-adjust/rendering/dark-color-scheme",
+    "bases": ["external/wpt/css/css-color/system-color-consistency.html",
+              "external/wpt/css/css-color-adjust/rendering/dark-color-scheme",
               "fast/forms/color-scheme",
               "fast/forms/validation-bubble-appearance-edge.html",
               "fast/forms/validation-bubble-appearance-wrap.html",
@@ -857,12 +858,12 @@
               "external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/",
               "http/tests/cookies/partitioned-cookies",
               "http/tests/inspector-protocol/network/"],
-    "args": ["--enable-features=PartitionedCookies"]
+    "args": ["--enable-features=PartitionedCookies,PartitionedCookiesBypassOriginTrial"]
   },
   {
     "prefix": "partitioned-cookies-first-party-sets",
     "bases": ["http/tests/inspector-protocol/network/"],
-    "args": ["--enable-features=PartitionedCookies",
+    "args": ["--enable-features=PartitionedCookies,PartitionedCookiesBypassOriginTrial",
              "--use-first-party-set=https://devtools.test,https://example.test"]
   },
   {
@@ -1004,6 +1005,13 @@
     "args": ["--enable-features=SharedStorageAPI,FencedFrames:implementation_type/shadow_dom"]
   },
   {
+    "prefix": "task-tracking",
+    "bases": [
+      "wpt_internal/task-tracking"
+    ],
+    "args": ["--enable-blink-features=UnexposedTaskIds"]
+  },
+  {
     "prefix": "fenced-frame-mparch",
     "bases": [
       "http/tests/fenced_frame",
@@ -1011,7 +1019,7 @@
       "http/tests/inspector-protocol/fenced-frame",
       "external/wpt/html/cross-origin-embedder-policy/anonymous-iframe"
     ],
-    "args": ["--enable-features=FencedFrames:implementation_type/mparch,Prerender2,PartitionedCookies"]
+    "args": ["--enable-features=FencedFrames:implementation_type/mparch,Prerender2,PartitionedCookies,PartitionedCookiesBypassOriginTrial"]
   },
   {
     "prefix": "fenced-frame-shadow-dom",
@@ -1020,7 +1028,7 @@
       "wpt_internal/fenced_frame",
       "external/wpt/html/cross-origin-embedder-policy/anonymous-iframe"
     ],
-    "args": ["--enable-features=FencedFrames:implementation_type/shadow_dom,Prerender2,PartitionedCookies"]
+    "args": ["--enable-features=FencedFrames:implementation_type/shadow_dom,Prerender2,PartitionedCookies,PartitionedCookiesBypassOriginTrial"]
   },
   {
     "prefix": "disable-custom-element-default-style",
diff --git a/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png b/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png
index a26c8cfe..1de1030 100644
--- a/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png
+++ b/third_party/blink/web_tests/compositing/overflow/border-radius-above-composited-subframe-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png b/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png
index ca07de2..496f2b6 100644
--- a/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png
+++ b/third_party/blink/web_tests/compositing/overflow/border-radius-composited-subframe-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 75be44f7..662ad985 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: afd66ac5976672821b2788cd5f6ae57701240308
+Version: 378ce87107fcdc462957e337f41731226f66698f
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 6724e9a..9656bee 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -383,6 +383,13 @@
        {}
       ]
      ],
+     "break-before-float-after-line-after-floats-crash.html": [
+      "9066cd77ecdc74037b9e75016fe0ec5975e399b0",
+      [
+       null,
+       {}
+      ]
+     ],
      "break-before-with-no-fragmentation-crash.html": [
       "fb80ec45bceec093481fa54513c606c5952628b1",
       [
@@ -525,6 +532,13 @@
        {}
       ]
      ],
+     "line-and-fragmentainer-break-before-float-crash.html": [
+      "14bce6b80c54453edefc51ceb9b210b46bc42719",
+      [
+       null,
+       {}
+      ]
+     ],
      "nested-oof-in-multicol-crash.html": [
       "6b2f81005d3e41642c6a545050d7c5536bae6af3",
       [
@@ -591,195 +605,197 @@
       ]
      ],
      "container-queries": {
-      "canvas-as-container-crash.html": [
-       "8d825606561884090c74b63e3d7bdfef19709169",
-       [
-        null,
-        {}
+      "crashtests": {
+       "canvas-as-container-crash.html": [
+        "8d825606561884090c74b63e3d7bdfef19709169",
+        [
+         null,
+         {}
+        ]
+       ],
+       "chrome-bug-1289718-000-crash.html": [
+        "5dab634a2a2586722ddbd0a9cf9eea8e31001dc6",
+        [
+         null,
+         {}
+        ]
+       ],
+       "chrome-bug-1289718-001-crash.html": [
+        "7236714183fd2130c9d12ffaf5226376d3ee7e58",
+        [
+         null,
+         {}
+        ]
+       ],
+       "columns-in-table-001-crash.html": [
+        "fe421500dade86b6fedbbf6289a668dcc5174f45",
+        [
+         null,
+         {}
+        ]
+       ],
+       "container-type-change-chrome-legacy-crash.html": [
+        "609142a2c5f7fa16f241b6ac06842a62ed68be8c",
+        [
+         null,
+         {}
+        ]
+       ],
+       "flex-in-columns-000-crash.html": [
+        "e7b789345c807362888a651952486b7e8fcc8ba5",
+        [
+         null,
+         {}
+        ]
+       ],
+       "flex-in-columns-001-crash.html": [
+        "0c0648c15bc1f6873bd6da9d868267cbc7d1df23",
+        [
+         null,
+         {}
+        ]
+       ],
+       "flex-in-columns-002-crash.html": [
+        "ef3052d2c98656bf8996b394cc5f66589e04421f",
+        [
+         null,
+         {}
+        ]
+       ],
+       "flex-in-columns-003-crash.html": [
+        "a86f25a773dbbcd1f622a0b3c32d00d8e845293c",
+        [
+         null,
+         {}
+        ]
+       ],
+       "focus-inside-content-visibility-crash.html": [
+        "1bf68d6686c2c902205faa912573e2b3c97f0ad8",
+        [
+         null,
+         {}
+        ]
+       ],
+       "grid-in-columns-000-crash.html": [
+        "56cf6cfdbbcc43c1403692895cec7c0071f6eea8",
+        [
+         null,
+         {}
+        ]
+       ],
+       "grid-in-columns-001-crash.html": [
+        "b9cf100533a74c12e3c26f274d65363d12f36705",
+        [
+         null,
+         {}
+        ]
+       ],
+       "grid-in-columns-002-crash.html": [
+        "762ad44f247d8773d978bc99740d7192669ea006",
+        [
+         null,
+         {}
+        ]
+       ],
+       "grid-in-columns-003-crash.html": [
+        "11089e690205d3ac03928b8d60c0030d6d16e91c",
+        [
+         null,
+         {}
+        ]
+       ],
+       "inline-multicol-inside-container-crash.html": [
+        "7e209f7ffd389744f843fe97b8483404f8ace7e5",
+        [
+         null,
+         {}
+        ]
+       ],
+       "inline-with-columns-000-crash.html": [
+        "733b2c4ee9a7406f15ffb819440d906e76c6740f",
+        [
+         null,
+         {}
+        ]
+       ],
+       "inline-with-columns-001-crash.html": [
+        "3b9bdf32bd2920256147d0b78bea9481e6921c51",
+        [
+         null,
+         {}
+        ]
+       ],
+       "input-column-group-container-crash.html": [
+        "5e520a45cffb9c203085118efde823b6b40cdf60",
+        [
+         null,
+         {}
+        ]
+       ],
+       "input-placeholder-inline-size-crash.html": [
+        "4b1284e5cb3d90a76a756a8d89a439c43d7e65e1",
+        [
+         null,
+         {}
+        ]
+       ],
+       "math-block-container-child-crash.html": [
+        "00b6836655e904f1f0d64f7e3f42afaf02fc351b",
+        [
+         null,
+         {}
+        ]
+       ],
+       "pseudo-container-crash.html": [
+        "f998c3a4464ca3eb3ce07687cf24d9dcdc9a16af",
+        [
+         null,
+         {}
+        ]
+       ],
+       "svg-layout-root-crash.html": [
+        "75a3839add0f0dcc33378ab194f37acdfcc39095",
+        [
+         null,
+         {}
+        ]
+       ],
+       "svg-text-crash.html": [
+        "aadba08679e8ce4c0645f7673c64ea41eb7c1869",
+        [
+         null,
+         {}
+        ]
+       ],
+       "table-in-columns-000-crash.html": [
+        "566a4eb1eb2daca575dc9bbb7a19d31dd66c3b4f",
+        [
+         null,
+         {}
+        ]
+       ],
+       "table-in-columns-001-crash.html": [
+        "4fab9de88f61016e5214a91b9c7efd898eca0803",
+        [
+         null,
+         {}
+        ]
+       ],
+       "table-in-columns-002-crash.html": [
+        "4f0cdc07400c0a597346cdb3b3c37571ef0fb58e",
+        [
+         null,
+         {}
+        ]
+       ],
+       "table-in-columns-003-crash.html": [
+        "436da592d97993f64d465bc1faecff7c805fc766",
+        [
+         null,
+         {}
+        ]
        ]
-      ],
-      "chrome-bug-1289718-000-crash.html": [
-       "5dab634a2a2586722ddbd0a9cf9eea8e31001dc6",
-       [
-        null,
-        {}
-       ]
-      ],
-      "chrome-bug-1289718-001-crash.html": [
-       "7236714183fd2130c9d12ffaf5226376d3ee7e58",
-       [
-        null,
-        {}
-       ]
-      ],
-      "columns-in-table-001-crash.html": [
-       "fe421500dade86b6fedbbf6289a668dcc5174f45",
-       [
-        null,
-        {}
-       ]
-      ],
-      "container-type-change-chrome-legacy-crash.html": [
-       "609142a2c5f7fa16f241b6ac06842a62ed68be8c",
-       [
-        null,
-        {}
-       ]
-      ],
-      "flex-in-columns-000-crash.html": [
-       "e7b789345c807362888a651952486b7e8fcc8ba5",
-       [
-        null,
-        {}
-       ]
-      ],
-      "flex-in-columns-001-crash.html": [
-       "0c0648c15bc1f6873bd6da9d868267cbc7d1df23",
-       [
-        null,
-        {}
-       ]
-      ],
-      "flex-in-columns-002-crash.html": [
-       "ef3052d2c98656bf8996b394cc5f66589e04421f",
-       [
-        null,
-        {}
-       ]
-      ],
-      "flex-in-columns-003-crash.html": [
-       "a86f25a773dbbcd1f622a0b3c32d00d8e845293c",
-       [
-        null,
-        {}
-       ]
-      ],
-      "focus-inside-content-visibility-crash.html": [
-       "1bf68d6686c2c902205faa912573e2b3c97f0ad8",
-       [
-        null,
-        {}
-       ]
-      ],
-      "grid-in-columns-000-crash.html": [
-       "56cf6cfdbbcc43c1403692895cec7c0071f6eea8",
-       [
-        null,
-        {}
-       ]
-      ],
-      "grid-in-columns-001-crash.html": [
-       "b9cf100533a74c12e3c26f274d65363d12f36705",
-       [
-        null,
-        {}
-       ]
-      ],
-      "grid-in-columns-002-crash.html": [
-       "762ad44f247d8773d978bc99740d7192669ea006",
-       [
-        null,
-        {}
-       ]
-      ],
-      "grid-in-columns-003-crash.html": [
-       "11089e690205d3ac03928b8d60c0030d6d16e91c",
-       [
-        null,
-        {}
-       ]
-      ],
-      "inline-multicol-inside-container-crash.html": [
-       "7e209f7ffd389744f843fe97b8483404f8ace7e5",
-       [
-        null,
-        {}
-       ]
-      ],
-      "inline-with-columns-000-crash.html": [
-       "733b2c4ee9a7406f15ffb819440d906e76c6740f",
-       [
-        null,
-        {}
-       ]
-      ],
-      "inline-with-columns-001-crash.html": [
-       "3b9bdf32bd2920256147d0b78bea9481e6921c51",
-       [
-        null,
-        {}
-       ]
-      ],
-      "input-column-group-container-crash.html": [
-       "5e520a45cffb9c203085118efde823b6b40cdf60",
-       [
-        null,
-        {}
-       ]
-      ],
-      "input-placeholder-inline-size-crash.html": [
-       "4b1284e5cb3d90a76a756a8d89a439c43d7e65e1",
-       [
-        null,
-        {}
-       ]
-      ],
-      "math-block-container-child-crash.html": [
-       "00b6836655e904f1f0d64f7e3f42afaf02fc351b",
-       [
-        null,
-        {}
-       ]
-      ],
-      "pseudo-container-crash.html": [
-       "f998c3a4464ca3eb3ce07687cf24d9dcdc9a16af",
-       [
-        null,
-        {}
-       ]
-      ],
-      "svg-layout-root-crash.html": [
-       "75a3839add0f0dcc33378ab194f37acdfcc39095",
-       [
-        null,
-        {}
-       ]
-      ],
-      "svg-text-crash.html": [
-       "aadba08679e8ce4c0645f7673c64ea41eb7c1869",
-       [
-        null,
-        {}
-       ]
-      ],
-      "table-in-columns-000-crash.html": [
-       "566a4eb1eb2daca575dc9bbb7a19d31dd66c3b4f",
-       [
-        null,
-        {}
-       ]
-      ],
-      "table-in-columns-001-crash.html": [
-       "4fab9de88f61016e5214a91b9c7efd898eca0803",
-       [
-        null,
-        {}
-       ]
-      ],
-      "table-in-columns-002-crash.html": [
-       "4f0cdc07400c0a597346cdb3b3c37571ef0fb58e",
-       [
-        null,
-        {}
-       ]
-      ],
-      "table-in-columns-003-crash.html": [
-       "436da592d97993f64d465bc1faecff7c805fc766",
-       [
-        null,
-        {}
-       ]
-      ]
+      }
      },
      "content-visibility": {
       "content-visibility-continuations-crash.html": [
@@ -2435,6 +2451,13 @@
     },
     "filter-effects": {
      "crashtests": {
+      "broken-reference-crash-001.html": [
+       "6e18e06c317e329a58b2b5132cf17dc6086d444f",
+       [
+        null,
+        {}
+       ]
+      ],
       "multiple-references-id-crash-001.html": [
        "9ee04e1015d3c9fc04e8fab240a15d59ae92892d",
        [
@@ -3918,6 +3941,13 @@
        null,
        {}
       ]
+     ],
+     "reparent-animating-element-002.html": [
+      "0d3549fc33dae6c4a351f00790e63262b7258ff1",
+      [
+       null,
+       {}
+      ]
      ]
     },
     "interfaces": {
@@ -79871,6 +79901,214 @@
         {}
        ]
       ],
+      "multi-line-column-flex-fragmentation-017.html": [
+       "070b0d6e854f631743fb77522b0fc5d4ea2ac72e",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-018.html": [
+       "a491dfac810a6a4e4dc4ee9668b5e55b90e87e54",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-019.html": [
+       "414326a5815a6a34bcaf04ff274ec8d331c17c99",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-020.html": [
+       "18030dd0025679163fb9103918bb99aadf75ff17",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-021.html": [
+       "833da1afce4d57a9c0644d962d3e7b014c93a93b",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-022.html": [
+       "60f543b5d12e466352feb63467a9f16c16b3e75b",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-023.html": [
+       "626d2679ea4063ae4135dcb63034eeb22e4e7a97",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-024.html": [
+       "4cd53adf6d5e4117ea1c327bd569855f8ee7980b",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-025.html": [
+       "93dd9b4a6211a9dc0688cd6b1bd17658d4a5ed1f",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-026.html": [
+       "204cc15a2d514fc1c081dcdbaa29de9f8650d69b",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-027.html": [
+       "95b73907b860320a6859e77095d3f1519f6fc4d5",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-028.html": [
+       "d96cd56d6924c315d30094bef6805dd5c46b983a",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-029.html": [
+       "ec47c9f3af5c6ca997592a4d03b6796c860534d4",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-030.html": [
+       "443984588b1d9f65cf2fbadf834db984fb8a93cc",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-031.html": [
+       "fc88e70a9cd1f790759f772ee10d791c25cddc99",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-032.html": [
+       "978dbd1fa2f8e0e17e6abbbfbf0c7f0a0c1225a1",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "multi-line-row-flex-fragmentation-001.html": [
        "ba6b0103e447994a2778cbd9ea356a490f62b8fb",
        [
@@ -122424,6 +122662,19 @@
         ],
         {}
        ]
+      ],
+      "subgrid-stretch.html": [
+       "321e12d2a3ba6d1bab3623c385153b2375c1b441",
+       [
+        null,
+        [
+         [
+          "/css/css-grid/subgrid/subgrid-stretch-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
       ]
      },
      "table-grid-item-dynamic-001.html": [
@@ -213163,6 +213414,19 @@
        {}
       ]
      ],
+     "visited-inheritance.html": [
+      "1442307a9293ed0b26970dd25e70e8c57fef8ee8",
+      [
+       null,
+       [
+        [
+         "/css/selectors/visited-inheritance-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "xml-class-selector.xml": [
       "5666c0065d6262e0ca3c586d145a67e7f9c2a3cf",
       [
@@ -235732,6 +235996,14 @@
        "9e65b42435908e33ed5b16e97f8bee3700bab340",
        []
       ],
+      "preload-csp-report.https.sub.html": [
+       "6b79414edd532d6b1a0128479eb9a788caefa1fa",
+       []
+      ],
+      "preload-csp-report.https.sub.html.sub.headers": [
+       "bb0506b41da4fbfb43ed2a0c581495019d0690fd",
+       []
+      ],
       "redirect-throw-function.sub.py": [
        "1bc89abf7176bbada8f0c8c35fd699f52bb0e8a9",
        []
@@ -236825,7 +237097,7 @@
      []
     ],
     "OWNERS": [
-     "f0d1e9f64b369dd5c46866e3008aac3f9c710215",
+     "c03182b1e8f0b29918bce193453359dee5410068",
      []
     ],
     "fedcm-logout-rps.https.html.headers": [
@@ -244546,6 +244818,10 @@
        "0f58883c97448d970d76b822f26f36efe3f14489",
        []
       ],
+      "animation-computed-expected.txt": [
+       "7cf62fe5bbfce3752012ad4adfe7773b7ce127ce",
+       []
+      ],
       "animation-name-invalid-expected.txt": [
        "a6d072671edbf2b2528754cf15f469eb7ce08d41",
        []
@@ -246999,7 +247275,7 @@
        []
       ],
       "system-color-valid-expected.txt": [
-       "5194a9465974f66b1965d80e3ad4307797608c1e",
+       "dde0e81fbed53164e38009674385ca18af22c659",
        []
       ]
      },
@@ -247682,6 +247958,10 @@
        "ecd72b7516c41c83b6e8d320c9b0b87f9c231781",
        []
       ],
+      "layout-dependent-focus-expected.txt": [
+       "c69221b27d619ce342cc52163d2b42d778d450c2",
+       []
+      ],
       "multicol-container-001-expected.txt": [
        "81e1ba5e89ba840e12b22c5475412827d6db71f5",
        []
@@ -260847,6 +261127,10 @@
       "subgrid-mbp-overflow-004-ref.html": [
        "5188a3c9fbf98f3730dd9195bbbf2cb56ab1aa90",
        []
+      ],
+      "subgrid-stretch-ref.html": [
+       "33e8669da01787826ea27895323f17b5b3a6a2c9",
+       []
       ]
      },
      "support": {
@@ -281171,6 +281455,10 @@
       "60549107fbeec9ba2ae7c07bcf3afe5d5bc94b34",
       []
      ],
+     "visited-inheritance-ref.html": [
+      "64300b13f4291f416eac17f70ff62e2febf604fe",
+      []
+     ],
      "xml-class-selector-ref.xml": [
       "6b44280737ab254649036ff50808b764bc87cc67",
       []
@@ -282022,7 +282310,7 @@
       []
      ],
      "testdriver.md": [
-      "dd751c3b7668790054d68cd1858b7ec0e7de89c6",
+      "37ceb1ca4776235d4abaacb97b99f04190e7bcc2",
       []
      ],
      "testharness-api.md": [
@@ -287368,7 +287656,7 @@
       []
      ],
      "message-target.js": [
-      "211caeb6cca2fe9e5012a65bd18fde5c56ce4abb",
+      "175d139c604e882b2bf8ae4334e643d858786d65",
       []
      ],
      "messaging-blob-helpers.js": [
@@ -287392,7 +287680,7 @@
       []
      ],
      "sync-access-handle-test.js": [
-      "ec13ff9a06bc67e9a770489fddc3e84678281027",
+      "489eeb8e2fe4ed3bb7eb0882e7fc6d6464fecb93",
       []
      ],
      "test-helpers.js": [
@@ -288924,6 +289212,10 @@
      "1a2996928163f16a8dd496ef98e71bf7b523db45",
      []
     ],
+    "permission.https-expected.txt": [
+     "3a82cfc21afb5d7b8dbfbca32b3c436ee1135179",
+     []
+    ],
     "resources": {
      "iframe.html": [
       "25cf3d92f3942738686a267c22d95d4a6464af7e",
@@ -307312,14 +307604,22 @@
     ]
    },
    "lint.ignore": [
-    "bc320dcd71e42da152519358865499226919fb4a",
+    "f1d1c2b273ada22f4e5f64aa785b74aa97808c0d",
     []
    ],
    "loading": {
     "early-hints": {
      "resources": {
+      "csp-basic-loader.h2.py": [
+       "080901efe60e70d8b178d0d9ffea5bbe04d71b94",
+       []
+      ],
+      "csp-basic.html": [
+       "0086711fb7c7bb5de136346e4ab7be50bb895d99",
+       []
+      ],
       "early-hints-helpers.sub.js": [
-       "5c1b2198eaee880fe6cd0a97684619788fd07b8c",
+       "33f80bc5f2dbe53ee1d7599f3ac46086951d356b",
        []
       ],
       "early-hints-test-loader.h2.py": [
@@ -309725,11 +310025,7 @@
      []
     ],
     "OWNERS": [
-     "cc6a94b332a04fdf400165265f41194abaf67d3e",
-     []
-    ],
-    "README.md": [
-     "068ae5b7114745e50c7c8a86fec8ef684ee02f61",
+     "e962bea003364aff10975e414f8a68f3b69cee97",
      []
     ],
     "focus-reset": {
@@ -309826,7 +310122,7 @@
         []
        ],
        "helpers.js": [
-        "bf711b18ade2c881fbfa40fc8e44ddf456fa37d2",
+        "5b147f4329094fb39c9c166532a06b963cb365fc",
         []
        ],
        "navigate-opaque-origin-page.html": [
@@ -309843,7 +310139,7 @@
      ],
      "resources": {
       "helpers.mjs": [
-       "b5a1e6b03186bfcc063a0a49ec46575d03bae969",
+       "359774de4f72c4c0bcb19ff33081ab7325b1756a",
        []
       ],
       "notify-top-early.html": [
@@ -314321,7 +314617,7 @@
      []
     ],
     "testdriver-actions.js": [
-     "ef097961fa7d51ba05bba4c90806b0aceb24bba8",
+     "3e5ba74b4cab356e56c73a4662e8f4b91dddf6de",
      []
     ],
     "testdriver-vendor.js": [
@@ -314341,7 +314637,7 @@
      []
     ],
     "testharness.js": [
-     "fcff33a59406ced4ef0b3a2da1afde19418cf5c2",
+     "a6f48e1f943b9952a8fbe5d882278c2cfb86baba",
      []
     ],
     "testharness.js.headers": [
@@ -318270,6 +318566,18 @@
     }
    },
    "speculation-rules": {
+    "prefetch": {
+     "resources": {
+      "executor.sub.html": [
+       "05b1295e9204f6ab92a16b2a5da939c426b09ba9",
+       []
+      ],
+      "utils.js": [
+       "e5e5bd8ee34dd1803a658f6bb4db3edb9f25760e",
+       []
+      ]
+     }
+    },
     "prerender": {
      "resources": {
       "about-blank-iframes.html": [
@@ -318280,6 +318588,10 @@
        "456f15ff699eb36b230b5079f918cbd57ea2705a",
        []
       ],
+      "broadcast-channel.html": [
+       "14980720a1640de6bc4141ae08d7238366f54ffa",
+       []
+      ],
       "cache.txt": [
        "89164cfabed56b5852445eb70470cfd1331bdb48",
        []
@@ -324397,6 +324709,10 @@
     "META.yml": [
      "17d93c51a9f195f5093882b1934c79f0a0635554",
      []
+    ],
+    "RTCRtpParameters-scalability-expected.txt": [
+     "79d1d57591325dccc5818ad89f288e27ba48c49b",
+     []
     ]
    },
    "websockets": {
@@ -355894,6 +356210,13 @@
        {}
       ]
      ],
+     "report-preload-and-consume.https.html": [
+      "c4481cfd27a6fad1a07ee9ffe606569fa440f73e",
+      [
+       null,
+       {}
+      ]
+     ],
      "report-same-origin-with-cookies.html": [
       "aa2ec6bd9d4e31dc704da8a7ac2bf65c1ed0baac",
       [
@@ -360023,6 +360346,13 @@
         {}
        ]
       ],
+      "block-in-inline-hittest-001.html": [
+       "c51e4c5329a686808009f04d0392f688aacbd236",
+       [
+        null,
+        {}
+       ]
+      ],
       "block-in-inline-hittest-float-001.html": [
        "6ede29df26d72485dad385539ebc20d9fb07bf70",
        [
@@ -361432,7 +361762,7 @@
        ]
       ],
       "animation-computed.html": [
-       "467f4357f102fb75ed8df196d221d83cd2efdabc",
+       "f8d34b889ba19dfc7d9fd4d644da499b979d4166",
        [
         null,
         {}
@@ -363361,7 +363691,7 @@
        ]
       ],
       "system-color-valid.html": [
-       "77c6d214fbce92e33df6829f09a2d9fd7fcfbf83",
+       "eea01e5d4ee60d1d00c2da6e887851d369477182",
        [
         null,
         {}
@@ -363381,6 +363711,13 @@
        null,
        {}
       ]
+     ],
+     "system-color-consistency.html": [
+      "f8fc5ef0d00ec92ed377021b5b8537851f20f13c",
+      [
+       null,
+       {}
+      ]
      ]
     },
     "css-color-adjust": {
@@ -363873,6 +364210,13 @@
         {}
        ]
       ],
+      "layout-dependent-focus.html": [
+       "a16370ac566d7044efb03f0466889a89dda49c4c",
+       [
+        null,
+        {}
+       ]
+      ],
       "multicol-container-001.html": [
        "3032170ac61bee9d4322d1992ab8a27af668c48e",
        [
@@ -381375,7 +381719,7 @@
      ],
      "parsing": {
       "transition-computed.html": [
-       "3f253c3a0d1fbe2193bc101872ad67e4a24835cc",
+       "a82551372f160fa77fdd0dcc7ac7d62d546db20d",
        [
         null,
         {}
@@ -422327,7 +422671,7 @@
      ]
     ],
     "sandboxed_FileSystemFileHandle-sync-access-handle-writable-lock.https.tentative.worker.js": [
-     "a2ed99db1d538ada4d76796ccd6a6c6cfba2565e",
+     "51e7f63d12f04b27d304832ef8659485c8961765",
      [
       "file-system-access/sandboxed_FileSystemFileHandle-sync-access-handle-writable-lock.https.tentative.worker.html",
       {}
@@ -423353,6 +423697,13 @@
       {}
      ]
     ],
+    "permission.https.html": [
+     "4062843349d77263fad2d8e12c6558f5d715ac04",
+     [
+      null,
+      {}
+     ]
+    ],
     "watchPosition_TypeError.https.html": [
      "64f0616949f3c00ed0d24f7d34f173c69e8e65d6",
      [
@@ -450967,6 +451318,15 @@
         ]
        ]
       }
+     },
+     "the-hidden-attribute": {
+      "hidden-idl.html": [
+       "331b63f93fd80a360dbc46e7b2da93d24596367c",
+       [
+        null,
+        {}
+       ]
+      ]
      }
     },
     "infrastructure": {
@@ -473323,6 +473683,168 @@
    },
    "loading": {
     "early-hints": {
+     "csp-early-hints-absent-final-absent.h2.window.js": [
+      "f61e268c8a3e2692951607eb8ad4dddd988f0ad3",
+      [
+       "loading/early-hints/csp-early-hints-absent-final-absent.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-absent-final-allowed.h2.window.js": [
+      "0e1762a28c9937a0086f6768d75fef24b7ebf388",
+      [
+       "loading/early-hints/csp-early-hints-absent-final-allowed.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-absent-final-disallowed.h2.window.js": [
+      "3fcd89c4cc089c38a53ab759bf7727813c4c5017",
+      [
+       "loading/early-hints/csp-early-hints-absent-final-disallowed.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-allowed-final-absent.h2.window.js": [
+      "15128ce5d9dca0dd8034939695ec99d07d3b5ce2",
+      [
+       "loading/early-hints/csp-early-hints-allowed-final-absent.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-allowed-final-allowed.h2.window.js": [
+      "ee51e78bf168c111a60dbc14972691b412a945b8",
+      [
+       "loading/early-hints/csp-early-hints-allowed-final-allowed.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-allowed-final-disallowed.h2.window.js": [
+      "67b39333ad7b58220bc73090ce350c842f2987d8",
+      [
+       "loading/early-hints/csp-early-hints-allowed-final-disallowed.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-disallowed-final-absent.h2.window.js": [
+      "80b563dd888f2defd34b4b993156a6ad23d2feb6",
+      [
+       "loading/early-hints/csp-early-hints-disallowed-final-absent.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-disallowed-final-allowed.h2.window.js": [
+      "cfac9b0b9864b1d42035de1ba4cf51837cdce3d4",
+      [
+       "loading/early-hints/csp-early-hints-disallowed-final-allowed.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "csp-early-hints-disallowed-final-disallowed.h2.window.js": [
+      "c8a7ca346fca680038ae2a18b82363f8759fac23",
+      [
+       "loading/early-hints/csp-early-hints-disallowed-final-disallowed.h2.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/early-hints-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "preload-initiator-type.h2.window.js": [
       "959aacef4978b935b44fec694d5f3ca3cb074485",
       [
@@ -480943,154 +481465,154 @@
    "navigation-api": {
     "currententrychange-event": {
      "anchor-click.html": [
-      "55e45561351ff1c6f43d211f1b8d7c78cfa098cc",
+      "e0bf91166aa2386b7043ccc12aed636c965952a5",
       [
        null,
        {}
       ]
      ],
      "constructor.html": [
-      "64ea8cf37ebd545cb349e4b0d863be3061851b99",
+      "b09e68e1c71e0edc9b2cf5a045cce9bba3a42706",
       [
        null,
        {}
       ]
      ],
      "history-back-same-doc.html": [
-      "bee3649395a8b1f4802de17d7fb4b21f894863e2",
+      "768805b7526fcd25898200d0d33f87f9440e6893",
       [
        null,
        {}
       ]
      ],
      "history-pushState.html": [
-      "c0e2d9a783cbe508bd065792ff115f62e26770a6",
+      "1a17a7cec0cade51f537d1ef719fed647e6ebfb6",
       [
        null,
        {}
       ]
      ],
      "history-replaceState.html": [
-      "da5505a6c14a2485b2ba194d0060bdfadef53a5c",
+      "e8ae8ee57a2c78f36f27643274be01c9fe44b602",
       [
        null,
        {}
       ]
      ],
      "location-api.html": [
-      "f06dc8ec615942aa277a602b7070502e74396725",
+      "88ebd985a1e9803a4c66b71afc4dda3033fa0565",
       [
        null,
        {}
       ]
      ],
      "navigate-from-initial-about-blank-same-doc-popup.html": [
-      "a8aa2a25fb51c0bfc0a40c953d50dc3786ddd409",
+      "2399fb2ef9cc88aa2d3cb306fdc7fe846d171ce8",
       [
        null,
        {}
       ]
      ],
      "navigate-from-initial-about-blank-same-doc.html": [
-      "e2c56743588e0bb1919565495fed603ff6b2becf",
+      "f0fa9cdf57b70e8dc80eafbfb2bb7f549b8c13b7",
       [
        null,
        {}
       ]
      ],
      "navigate-from-initial-about-blank.html": [
-      "7b406c733c4a11712cabc7596daacc7eb769d5ce",
+      "56eaa1af265b40433f085b600ac06fdf36a730a7",
       [
        null,
        {}
       ]
      ],
      "navigation-back-forward-cross-doc.html": [
-      "df230bd9acb5daae40f30a2dfe625ebe0f8aa78f",
+      "7416caa94b1f6588ce4779a4339df97509302c63",
       [
        null,
        {}
       ]
      ],
      "navigation-back-forward-same-doc.html": [
-      "41cc4afd446e6338792a0f4cceb285082903d858",
+      "8182673aa59dc007d52de97d9cce27614eec38cb",
       [
        null,
        {}
       ]
      ],
      "navigation-navigate-cross-doc.html": [
-      "2f804a307f6cbeef899411f6fd58c382b2f7f1cf",
+      "81a4e239baeefd9c73c1126fd9069c4315c1fdca",
       [
        null,
        {}
       ]
      ],
      "navigation-navigate-preventDefault.html": [
-      "997fb559492c7383e630e41c399a2106bcf58732",
+      "34b98353ba3745441a830c7d2a78a107f9aef7b4",
       [
        null,
        {}
       ]
      ],
      "navigation-navigate-replace-cross-doc.html": [
-      "52478f22799b0593749a2b59963ed0de43ee0ff9",
+      "d37ba34d62bc93b6c47fe5080875908ef789392e",
       [
        null,
        {}
       ]
      ],
      "navigation-navigate-replace-same-doc.html": [
-      "a4027709687949140948681c9115d9eeff8d9307",
+      "1c0f985b9854ffa3530c7da9c68c004166204846",
       [
        null,
        {}
       ]
      ],
      "navigation-navigate-replace-transitionWhile.html": [
-      "55b314a554d2542c3a8de9388135180fae2cada0",
+      "a8babf298dc2bc2bcc57f49117cd7004fe3db220",
       [
        null,
        {}
       ]
      ],
      "navigation-navigate-same-doc.html": [
-      "96de3de6d3d62b2d33d566c2e38568cb8d327f36",
+      "87fc28d174a6c8bfbcde746fc019cbd833800319",
       [
        null,
        {}
       ]
      ],
      "navigation-navigate-transitionWhile.html": [
-      "2132016404dd17968100315ec78fc4bf998bfb84",
+      "e5855cf63e81517bcc53a37a95bbfdeca9c68e16",
       [
        null,
        {}
       ]
      ],
      "navigation-reload-cross-doc.html": [
-      "cb4279cfe53f44146bca35f6aa529997f70207fb",
+      "e590cab3828e3f8472713462f82e7e7efa8c4fd5",
       [
        null,
        {}
       ]
      ],
      "navigation-reload-transitionWhile.html": [
-      "cfa6258dc578de7fe011ed8a938b333ea8e38388",
+      "c732de941f7ed8d0ee52085b1c9039e2d41728d4",
       [
        null,
        {}
       ]
      ],
      "navigation-updateCurrentEntry.html": [
-      "8d548706be938d26e459cb4c5c7bf69620517f3e",
+      "4423b2bc90e1c52dfc17de0770c1d7f2ed57a48f",
       [
        null,
        {}
       ]
      ],
      "properties.html": [
-      "7048034e4f051bf1f302383fbc0d5828ede3a837",
+      "e862543bcc1eca7569a094080c8491df65f4f161",
       [
        null,
        {}
@@ -481292,7 +481814,7 @@
       ]
      },
      "event-constructor.html": [
-      "501857feba4471575531965c865bcaf163d17985",
+      "6ef5cdfc911955b18f31261bc1313704e8cb22a6",
       [
        null,
        {}
@@ -481520,49 +482042,49 @@
       ]
      ],
      "signal-abort-detach-in-onnavigate.html": [
-      "adef895565b9bc283cbc570323c617acf763104a",
+      "467ea88899323bbf47e9a12fd3a03ee577963a43",
       [
        null,
        {}
       ]
      ],
      "signal-abort-preventDefault.html": [
-      "f281617866c0cd6a8e7e2a61ef6ba8e58cf38115",
+      "60fed90ce6c36afbd6221feba367bb45085d1297",
       [
        null,
        {}
       ]
      ],
      "signal-abort-transitionWhile.html": [
-      "d90ee5b0837c41bd3bdce5f28284037e76aa0e0c",
+      "ce195cf677229eb31591bec780cb7684ba9cb502",
       [
        null,
        {}
       ]
      ],
      "signal-abort-window-stop-after-transitionWhile.html": [
-      "aad3be34d7e80bc6e613f309f45089c180fb8fc4",
+      "bc44a07b20e8303095511dadb0b03b9563a63ca9",
       [
        null,
        {}
       ]
      ],
      "signal-abort-window-stop-in-onnavigate.html": [
-      "dde666c93386175a50d08da6296a8d8121d73243",
+      "1b406c42d367f98e0d993f5e4d66d8b704386697",
       [
        null,
        {}
       ]
      ],
      "signal-abort-window-stop.html": [
-      "49399fb663777ea87b6c5b0686a1168557ed5e97",
+      "43e005e8b41180b058055ef30714e7dd49954861",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-after-dispatch.html": [
-      "7f4a25cef528a1609731b2b1cc8c5139eee56a12",
+      "910665cb3deb51b8537b9f069841d26ecec1ae5e",
       [
        null,
        {}
@@ -481576,91 +482098,91 @@
       ]
      ],
      "transitionWhile-canceled-event.html": [
-      "ed6dbdc85cfe0796e26fc519ca2b498fd0fdf526",
+      "f1fe3ece38bf202dcba44cced42a348dc97554fe",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-cross-document-same-origin.html": [
-      "79e03f1c397c5d3bc6b5ee82695e188575400542",
+      "9e55958c533785d6656292bd408232c76fd42435",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-cross-origin.html": [
-      "ece79d263571838352ab74244df296a798708629",
+      "e53371a96a2ce5d7bf22c1a37b4fc81e9c53d111",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-detach.html": [
-      "d6a7d536276b6af21a96de2b20d5abd05e544a7e",
+      "25159850803d819e8319b61c59cbcdbafbc4a65c",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-history-pushState.html": [
-      "fb6152fc2289ffe74082307a439ca6de004832d1",
+      "a08e4ea2823764c7e7856149f84e8e63dfd853ab",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-history-replaceState.html": [
-      "00f85dcc0fcc958f4224cf8b3ae4f0b5a5a757f2",
+      "e575876de245b3c6d2d0e391663efa47885116a5",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-multiple-times-reject.html": [
-      "c325829001572dfd082c98d1b64fec117c3b12b4",
+      "0532d755fd343fee914967c351e7c0283601716c",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-multiple-times.html": [
-      "0f6caf16f1411e850522df207289c4f933b80b65",
+      "d5661a54eba39a76fa66dfc00e76b2052b603cc9",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-navigation-back.html": [
-      "a0bc453d36e3095fcb0d5c76f6fabb0c6ad3f612",
+      "3a4629005be58a3314cffae0fcc8fc524b53f7cf",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-on-synthetic-event.html": [
-      "21a32e7b4c93946f7c8a899a78d12133c2a2dfb9",
+      "3acf41745811aca4d5e31de5b257e97a72b556fa",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-reject.html": [
-      "2cdea05072911cf6883f90071547f20b5693c01a",
+      "28864b38a5186aadc8233940870ed9e0f53848dd",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-resolve.html": [
-      "2ee4fb39713cffe36724b85beda4cba9c0ca3287",
+      "b49c66399daee59a595aafabbb09c94463ac6c48",
       [
        null,
        {}
       ]
      ],
      "transitionWhile-same-document-history-back.html": [
-      "5e8ac39ef20ec07b78fd3ee58437dbe90a0a6a45",
+      "4c404386fe216092d87bbe87c876eb29ee47d17d",
       [
        null,
        {}
@@ -481697,21 +482219,21 @@
       ]
      ],
      "entries-after-bfcache.html": [
-      "11bd7df931d56880163f42af33676ef76a0b74e9",
+      "ef93d1e27e76cc72f8fb24ee711b76361c71f42a",
       [
        null,
        {}
       ]
      ],
      "entries-after-blank-navigation.html": [
-      "6f6dbcc20df9bb8c0beb4921d84da359b8dbc01d",
+      "f54ae06e148d8c45352acbf927b458a6c83e9cd2",
       [
        null,
        {}
       ]
      ],
      "entries-after-blob-navigation.html": [
-      "77c25174da2f87a04c9c642e0fa732717bc1937e",
+      "67611f4d446cc25895058c18007190c957ea458a",
       [
        null,
        {}
@@ -481725,7 +482247,7 @@
       ]
      ],
      "entries-after-javascript-url-navigation.html": [
-      "27a07c9e55d37c364595b393cf4282e425854f6a",
+      "c5ef7f33e425a2eb128c600742f3a6541c7c5085",
       [
        null,
        {}
@@ -481739,7 +482261,7 @@
       ]
      ],
      "entries-after-srcdoc-navigation.html": [
-      "434b376224c6bd121c28e2667050f8fd263a4a04",
+      "b80e8aa0e21636c9627eaccd3a64e31a3a46a271",
       [
        null,
        {}
@@ -481753,14 +482275,14 @@
       ]
      ],
      "entries-in-new-javascript-url-iframe.html": [
-      "cd4279a7f31fb4264b01a372e62cad9fad94c660",
+      "6f217f5e3c4f335888c84128b3b32febe88a61cb",
       [
        null,
        {}
       ]
      ],
      "entries-in-new-srcdoc-iframe.html": [
-      "d261efd0778a787df0194fb40c59cd979680d01a",
+      "a7e0f88d37a3746c0fdefd991c2b8d013d5e86e6",
       [
        null,
        {}
@@ -481774,56 +482296,56 @@
       ]
      ],
      "entry-after-detach.html": [
-      "83962d2a8e146244720f687b5980fec6e5b0bc15",
+      "69c52d140983bc2a781706b13a3c7c1c3bc05bb7",
       [
        null,
        {}
       ]
      ],
      "index-not-in-entries.html": [
-      "37cccd39d829be865485649762b91cc6687710af",
+      "1c09a0f53ae4a9d7582a299daa9c90a77446e54b",
       [
        null,
        {}
       ]
      ],
      "key-id-back-cross-document.html": [
-      "f0dc182be41f27d5abae0b67e0d3fe38d06a135a",
+      "2dd58c03e9d7cd3f81f50ad0c320e60dcaa07bf7",
       [
        null,
        {}
       ]
      ],
      "key-id-back-same-document.html": [
-      "b5137f3e51bfcce5e29a72766f230eae1da328d7",
+      "858b5fd2c896347ae86680ed554f4160636aa3fd",
       [
        null,
        {}
       ]
      ],
      "key-id-location-reload-transitionWhile.html": [
-      "a06b645fa6f2f190cb91d4f8bb462a79c972db4e",
+      "e5924d243160cd9abcc7928a356defcc972543d3",
       [
        null,
        {}
       ]
      ],
      "key-id-location-reload.html": [
-      "267906d2c38cc4cc65d3c56c8dca0c77bb551fff",
+      "f950e2f3a2172e266810fa44ded85647a8eb5247",
       [
        null,
        {}
       ]
      ],
      "key-id-location-replace-cross-origin.html": [
-      "ee0f749a9edc3435e454f96ef83a366494a1de99",
+      "65aff4a3abc3166d31ffbdeb9f9ffd236f8f75a0",
       [
        null,
        {}
       ]
      ],
      "key-id-location-replace.html": [
-      "ec96371e5648b2bc5e64802cdfc134ff18b18efa",
+      "a58772a7f479ba2f64f2b220c85588e496e8e4c4",
       [
        null,
        {}
@@ -481837,21 +482359,21 @@
       ]
      ],
      "no-referrer-from-meta-url-censored.html": [
-      "51eed0800fb02814ef90342bc2a87bb36b01c161",
+      "fc563f509e79c8bcc98487bcde12d3068cc3d456",
       [
        null,
        {}
       ]
      ],
      "no-referrer-url-censored.html": [
-      "20397b8ef9c8af8bf2188257e9a57e687ef6694d",
+      "e7eb1afc7d1d2d80df8131bcc449187093f0deb2",
       [
        null,
        {}
       ]
      ],
      "opaque-origin-data-url.html": [
-      "3275a30ef420f7220aacf85788e1ef9e3b486def",
+      "65123fa60babbdc77bb58de6b4e68300c14256b2",
       [
        null,
        {}
@@ -481865,7 +482387,7 @@
       ]
      ],
      "sameDocument-after-fragment-navigate.html": [
-      "acb67ee72c705563fe850b41f9aa166e634c3313",
+      "a197f825d8eda300250af2961bfd14448cdc3fc2",
       [
        null,
        {}
@@ -481879,7 +482401,7 @@
       ]
      ],
      "sameDocument-after-navigate.html": [
-      "e1376a07f2550210be1304ae16a37df28ed3daa6",
+      "bfcb7c6599729c551bc447cde058b4477570e043",
       [
        null,
        {}
@@ -482028,7 +482550,7 @@
       ]
      ],
      "reload-base-url.html": [
-      "936ad75c832a13b3aff1f05d21fbf7e9a8f2f728",
+      "1e8d3b90c34691fd080cdd928b38788bb84383a7",
       [
        null,
        {}
@@ -482063,7 +482585,7 @@
       ]
      ],
      "reload-state-and-info.html": [
-      "3ee6a8b8fa0218767e4322e2e4014d12db68d93e",
+      "a68dd2ab8be014f5f1b578dc631adbd92578e2d8",
       [
        null,
        {}
@@ -482534,7 +483056,7 @@
       ]
      ],
      "traverseTo-detach-between-navigate-and-navigatesuccess.html": [
-      "9a9529578147b557c83a10f499227645b0ceb746",
+      "25dd134649627eb646afbae39420c1aa5d717552",
       [
        null,
        {}
@@ -482564,7 +483086,7 @@
     },
     "ordering-and-transition": {
      "back-same-document-transitionWhile-reject.html": [
-      "68cff8085d49fd8f5751d05e81088d828728fff6",
+      "05d86b4910213a89cdf0f458ed80653a9d936d08",
       [
        null,
        {}
@@ -482575,7 +483097,7 @@
       ]
      ],
      "back-same-document-transitionWhile.html": [
-      "30d083c9e6dbb80b1f60401cc6ba2ddba92c6366",
+      "d435aab37b4b16d825c525fec0f99629067587c9",
       [
        null,
        {}
@@ -482586,7 +483108,7 @@
       ]
      ],
      "back-same-document.html": [
-      "cc303754119484bf3338e64fe7af8c3a000e50f1",
+      "76c3e99311ed3e792446d9b250d0bef48dbd2847",
       [
        null,
        {}
@@ -482597,28 +483119,28 @@
       ]
      ],
      "currententrychange-before-popstate-transitionWhile.html": [
-      "f4c36fa0ab21143fd69f4fe5079845d87231ad7f",
+      "59f37e97125e6d438866b628624f889d57da315c",
       [
        null,
        {}
       ]
      ],
      "currententrychange-dispose-ordering.html": [
-      "396a46ff969b2f3649b39850662bd37186bc0cd8",
+      "7cbed8b1bbc90a810d92971e1a620c83a39be30d",
       [
        null,
        {}
       ]
      ],
      "location-href-canceled.html": [
-      "f4247413d3aa2f325c920352c643857c0d09ca12",
+      "eef10cd1730bbb56a9dab64bfeec90ecb34b770f",
       [
        null,
        {}
       ]
      ],
      "location-href-double-transitionWhile.html": [
-      "6e7c89502d60844035eea74f35530e81d63402cd",
+      "5ec22b119fa673b8125c98bf2f6eadd34007f55a",
       [
        null,
        {}
@@ -482629,7 +483151,7 @@
       ]
      ],
      "location-href-transitionWhile-reentrant.html": [
-      "36b23554ddb9566c4a7ec894a050944bcae0dea9",
+      "4dedb58d5b042f55dc97f3672d1534946cbf66fc",
       [
        null,
        {}
@@ -482640,7 +483162,7 @@
       ]
      ],
      "location-href-transitionWhile-reject.html": [
-      "e3f65e81d44e0dc1fe53532953f40d4fc767ce6d",
+      "dbef3928e30c9da1b3e0242448cc62cae04745b3",
       [
        null,
        {}
@@ -482651,7 +483173,7 @@
       ]
      ],
      "location-href-transitionWhile.html": [
-      "0ae41447213ba7bb6e9ea8baf819715d0328c8e2",
+      "0e1d22df7254f8ca385922f6f29c9ea4505d9458",
       [
        null,
        {}
@@ -482662,7 +483184,7 @@
       ]
      ],
      "navigate-canceled.html": [
-      "851fa349937c207d97ada62d790d22d6a93f53b4",
+      "2604a60e37bfc240e84df5c7bf02fda31425a303",
       [
        null,
        {}
@@ -482683,7 +483205,7 @@
       ]
      ],
      "navigate-double-transitionWhile.html": [
-      "4413ca4c35d93a33360d565c9fdaf40fa24fcc4b",
+      "556d1737a6e37e0828a78558d5886953c7adb164",
       [
        null,
        {}
@@ -482694,7 +483216,7 @@
       ]
      ],
      "navigate-in-transition-finished.html": [
-      "f8ebd6927322e5d360ad648a95995a972ecd9dbe",
+      "8c74ba2f632f7b82af69cacf9d1918dc38ca34d5",
       [
        null,
        {}
@@ -482705,7 +483227,7 @@
       ]
      ],
      "navigate-same-document-transitionWhile-reentrant.html": [
-      "8cbb98e85819e20d742c2b1ac48b2fe33edce6a6",
+      "2ef4c4fe27294c0028c3ed3fefae95355f64faea",
       [
        null,
        {}
@@ -482716,7 +483238,7 @@
       ]
      ],
      "navigate-same-document-transitionWhile-reject.html": [
-      "45ce700057053b7d7e873f6c21765e5c1ca0cc35",
+      "b3a10b422e9444ce77487ac788b9904b9c660507",
       [
        null,
        {}
@@ -482727,7 +483249,7 @@
       ]
      ],
      "navigate-same-document.html": [
-      "07f7bdce5d6ea10acd7801bcbed26deea01dbc98",
+      "589e1105e65a74655e218181835b20c83ac83fd2",
       [
        null,
        {}
@@ -482738,7 +483260,7 @@
       ]
      ],
      "navigate-transitionWhile-stop.html": [
-      "9d06ba17969fa4b19db945589cf5a3933c4709e9",
+      "9a8c412b3333efab9b6fde4e01a8df3539595f5e",
       [
        null,
        {}
@@ -482749,7 +483271,7 @@
       ]
      ],
      "navigate-transitionWhile.html": [
-      "d3125b0fc099877014eddfb401115f1a3833f248",
+      "b72bf49eae20f53e1650644a525fe1792b1d4fab",
       [
        null,
        {}
@@ -482760,14 +483282,14 @@
       ]
      ],
      "reload-canceled.html": [
-      "a7ae7913c6107f89e79f9b964d3ae103bf657770",
+      "3e9e24e777112908703e45daebd87b88dd5d1e59",
       [
        null,
        {}
       ]
      ],
      "reload-transitionWhile-reject.html": [
-      "0a70c6deed4fdfe49edbe10d1956fa1d2adecf98",
+      "6939ee2df8208b9ed8867242f797cedb0689679a",
       [
        null,
        {}
@@ -482778,7 +483300,7 @@
       ]
      ],
      "reload-transitionWhile.html": [
-      "8cc237f2dddafa98b4fcf19f9df54f35920f0a87",
+      "31cd813f7419bb6c91da98e604c7c34851723369",
       [
        null,
        {}
@@ -482803,7 +483325,7 @@
       ]
      ],
      "transition-realms-and-identity.html": [
-      "16fb8184545e7df127dd581f61ab7e497771c474",
+      "c2c06fcca2604438e0f9e48277971e22ade657ab",
       [
        null,
        {}
@@ -482870,49 +483392,49 @@
     },
     "state": {
      "cross-document-away-and-back.html": [
-      "25d0c1e79811cea7de5b747aab2066caaceb558e",
+      "60f70363d09c3482a46e0e185f8ae6bbf9b10f4d",
       [
        null,
        {}
       ]
      ],
      "cross-document-location-api.html": [
-      "9c869ca1d276e465b29ae719a1e24a735e0e91b8",
+      "29e5a025d7c1fb2a4bd22d2ba65772b780ed8c05",
       [
        null,
        {}
       ]
      ],
      "history-pushState.html": [
-      "1c8858c0af0c363bb12f74fbc1c76b5914264449",
+      "dad01fd8371d57d4f00dc213b93db078aad5a6c9",
       [
        null,
        {}
       ]
      ],
      "history-replaceState.html": [
-      "cc37949415eb8b29ac85ebd4efc8c6dd96616b8d",
+      "14934d69f4520be20a623ebc800d4f8f6817a75d",
       [
        null,
        {}
       ]
      ],
      "location-reload.html": [
-      "94d466434638ef5823dee86541a69349a4d5878c",
+      "ae483bf900eb6e7f284961278c0ac36e2a31e74f",
       [
        null,
        {}
       ]
      ],
      "same-document-away-and-back-location-api.html": [
-      "9cb6215089fdb56e2a241e927a8fb5366de94b62",
+      "d161df8b5295f1bbc8e396a4345ef2ae0112264f",
       [
        null,
        {}
       ]
      ],
      "same-document-away-and-back-navigation-api.html": [
-      "eed296d583afc722bf14f378e4b0641b64d1c16a",
+      "0ece14d4b7d3bc61fa5f6cffff6ea3297ce5edc5",
       [
        null,
        {}
@@ -482928,14 +483450,14 @@
       ]
      ],
      "cross-document-away-and-back.html": [
-      "a192314050df0a2251a7b89d8eea4569862de7de",
+      "c37d5e979a069f73faf7972801792bc36c5a6f01",
       [
        null,
        {}
       ]
      ],
      "cross-document-location-api.html": [
-      "3b4ec68bae006db8149a323ffb542f7f7fbcbec8",
+      "26191fb8761b6dfbca7881f5b5bb2e62d7690155",
       [
        null,
        {}
@@ -482956,14 +483478,14 @@
       ]
      ],
      "history-pushState.html": [
-      "73fb89f2a0f8d4daf76537038b1eb315cd1bb0b0",
+      "852294c64f4e8fd596637032ecc363076ca86ee7",
       [
        null,
        {}
       ]
      ],
      "history-replaceState.html": [
-      "15472db2e777daf05d107998729b0b3f7f79035a",
+      "3eb91a9a80cef8e1a2036181503867ec490c1168",
       [
        null,
        {}
@@ -482977,7 +483499,7 @@
       ]
      ],
      "location-reload.html": [
-      "664ff1b23a5c2223e462c1c0feba2a991ba8044d",
+      "8589eeb694e73cb08852ec31c0577cb5d21e2a30",
       [
        null,
        {}
@@ -483005,7 +483527,7 @@
       ]
      ],
      "same-document-away-and-back-location-api.html": [
-      "a1f54f5b7c21f1a8767c9ed2623bf819c7bad0d8",
+      "47b1904f4f46f6cbd1a8bbdebbd2516e9669df93",
       [
        null,
        {}
@@ -509907,6 +510429,15 @@
     ]
    },
    "speculation-rules": {
+    "prefetch": {
+     "single-url.https.html": [
+      "e98f06f30f5b722c9efcec00e0611fb44afd59e3",
+      [
+       null,
+       {}
+      ]
+     ]
+    },
     "prerender": {
      "about-blank-iframes.html": [
       "9cc0ab3792714828be6235a4405db9cd77eec906",
@@ -510007,6 +510538,15 @@
        }
       ]
      ],
+     "restriction-broadcast-channel.html": [
+      "7225e64cf9848921de4e33982e733104265daa43",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "restriction-focus.html": [
       "1149b8bd0981e8cf0109f2bca9dd974b9cd9a604",
       [
@@ -527778,6 +528318,13 @@
        {}
       ]
      ],
+     "script-invalid-json.https.tentative.html": [
+      "57bd82c39d32a8183895efb16ecb8cc370d4d5eb",
+      [
+       null,
+       {}
+      ]
+     ],
      "script-nested-bundle.https.tentative.html": [
       "4daf60acf5eecf6420e8e0b82a74662aec5f6ea7",
       [
@@ -537753,7 +538300,7 @@
    },
    "webrtc-svc": {
     "RTCRtpParameters-scalability.html": [
-     "52907af55eab90bcddc958d0cb44723e5079715b",
+     "c32a12807e97fcd5735052288bfc21e5f70fc05c",
      [
       null,
       {}
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-preload-and-consume.https.html b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-preload-and-consume.https.html
new file mode 100644
index 0000000..c4481cf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/report-preload-and-consume.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test that reports are sent with credentials to same-origin endpoints</title>
+  <script src="/common/utils.js"></script>
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+  <script src='/reporting/resources/report-helper.js'></script>
+</head>
+<body>
+  <script>
+    const endpoint = '/reporting/resources/report.py';
+
+    promise_test(async t => {
+        const uid = token();
+        const win = window.open(`./support/preload-csp-report.https.sub.html?uid=${uid}`);
+        t.add_cleanup(() => win.close());
+        await wait(3000);
+        const reports = await pollReports(endpoint, uid);
+        const failures = reports.filter(r => r['csp-report']['blocked-uri'].endsWith('fail.png'));
+        assert_equals(failures.length, 2);
+    }, "Reporting endpoints received credentials.");
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html
new file mode 100644
index 0000000..6b79414
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<body>
+<!-- This image will cause a CSP violation, which will trigger an immediate report -->
+<script>
+    const href = "/reporting/resources/fail.png";
+
+    window.addEventListener('load', async () => {
+        // Trigger a CSP error.
+        await new Promise(resolve => {
+            const link = document.createElement('link');
+            link.rel = 'preload';
+            link.href = href;
+            link.as = 'image';
+            document.head.appendChild(link);
+            link.addEventListener('error', resolve);
+        });
+
+        // Trigger a second CSP error by consuming.
+        await new Promise(resolve => {
+            const img = document.createElement('img');
+            img.src = href;
+            img.addEventListener('error', resolve);
+            document.body.appendChild(img);
+        });
+    });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html.sub.headers b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html.sub.headers
new file mode 100644
index 0000000..bb0506b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/reporting/support/preload-csp-report.https.sub.html.sub.headers
@@ -0,0 +1 @@
+Content-Security-Policy: img-src none; report-uri /reporting/resources/report.py?op=put&reportID={{GET[uid]}}
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-hittest-002.html b/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-hittest-002.html
new file mode 100644
index 0000000..b01eaa9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-hittest-002.html
@@ -0,0 +1,41 @@
+<html>
+<meta name="assert" content="Test list-based hit-testing for block-in-inline">
+<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+  <div>
+    <a href="#">
+      <h3 id="target">
+        text
+      </h3>
+    </a>
+  </div>
+<script>
+function ancestors(element) {
+  const list = [];
+  for (; element; element = element.parentElement)
+    list.push(element);
+  return list;
+}
+
+const target = document.getElementById('target');
+const bounds = target.getBoundingClientRect();
+const x = bounds.x + bounds.width / 2;
+const y = bounds.y + bounds.height / 2;
+
+test(() => {
+  const result = document.elementFromPoint(x, y);
+  assert_equals(result, target);
+}, "elementFromPoint");
+
+test(() => {
+  const results = document.elementsFromPoint(x, y);
+  assert_array_equals(results, ancestors(target));
+}, "elementsFromPoint");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
new file mode 100644
index 0000000..7cf62fe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+FAIL Default animation value assert_equals: expected "0s ease 0s 1 normal none running none" but got "none 0s ease 0s 1 normal none running"
+PASS Property animation value '1s'
+PASS Property animation value 'cubic-bezier(0, -2, 1, 3)'
+PASS Property animation value '1s -3s'
+PASS Property animation value '4'
+PASS Property animation value 'reverse'
+PASS Property animation value 'both'
+PASS Property animation value 'paused'
+PASS Property animation value 'none'
+PASS Property animation value 'anim'
+PASS Property animation value 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)'
+PASS Property animation value 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html
index 467f435..f8d34b8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html
@@ -16,6 +16,11 @@
 // <single-animation-iteration-count> || <single-animation-direction> ||
 // <single-animation-fill-mode> || <single-animation-play-state> ||
 // [ none | <keyframes-name> ]
+
+test(() => {
+  assert_equals(getComputedStyle(document.getElementById('target')).animation, "0s ease 0s 1 normal none running none");
+}, "Default animation value");
+
 test_computed_value("animation", "1s", "1s ease 0s 1 normal none running none");
 test_computed_value("animation", "cubic-bezier(0, -2, 1, 3)", "0s cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none");
 test_computed_value("animation", "1s -3s", "1s ease -3s 1 normal none running none");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html
new file mode 100644
index 0000000..070b0d6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-017.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with break-inside: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 10px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+  }
+  #flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div id="flex">
+    <div style="height: 250px; break-inside: avoid;"></div>
+    <div style="height: 200px; break-inside: avoid;"></div>
+    <div style="height: 120px; break-inside: avoid;"></div>
+    <div style="height: 180px; break-inside: avoid;"></div>
+    <div style="height: 100px; break-inside: avoid;"></div>
+  </div>
+  <div class="abs" style="top: 20px; left: 30px; height: 80px;"></div>
+  <div class="abs" style="top: 50px; left: 40px; height: 50px;"></div>
+  <div class="abs" style="top: 80px; left: 70px; height: 20px;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html
new file mode 100644
index 0000000..a491dfa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-018.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with break-before: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 5px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+  }
+  #flex > div {
+    background: green;
+    width: 5px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div>
+  <div id="flex">
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 350px; break-before: avoid;"></div>
+    <div style="height: 100px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 250px; break-before: avoid;"></div>
+    <div style="height: 350px; break-before: avoid;"></div>
+    <div style="height: 150px; break-before: avoid;"></div>
+    <div style="height: 500px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html
new file mode 100644
index 0000000..414326a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-019.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with break-before: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 5px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+  }
+  #flex > div {
+    background: green;
+    width: 5px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div>
+  <div id="flex">
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 350px; break-before: avoid;"></div>
+    <div style="height: 100px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 250px; break-before: avoid;"></div>
+    <div style="height: 400px;"></div>
+    <div style="height: 100px; break-before: avoid;"></div>
+    <div style="height: 500px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html
new file mode 100644
index 0000000..18030dd0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-020.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with break-after: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 5px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+  }
+  #flex > div {
+    background: green;
+    width: 5px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div class="abs" style="top: 50px; left: 25px; height: 50px;"></div>
+  <div id="flex">
+    <div style="height: 50px;"></div>
+    <div style="height: 50px; break-after: avoid;"></div>
+    <div style="height: 350px;"></div>
+    <div style="height: 100px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 50px; break-after: avoid;"></div>
+    <div style="height: 250px;"></div>
+    <div style="height: 400px; break-after: avoid;"></div>
+    <div style="height: 100px; break-after: avoid;"></div>
+    <div style="height: 500px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html
new file mode 100644
index 0000000..833da1a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-021.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with break-before: column.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 10px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+  }
+  #flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div class="abs" style="top: 50px; left: 30px; height: 50px;"></div>
+  <div id="flex">
+    <div style="height: 50px;"></div>
+    <div style="height: 50px; break-before: column;"></div>
+    <div style="height: 350px;"></div>
+    <div style="height: 100px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 50px; break-before: column;"></div>
+    <div style="height: 250px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html
new file mode 100644
index 0000000..60f543b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-022.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with break-after: column.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 10px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+  }
+  #flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div class="abs" style="top: 50px; left: 30px; height: 50px;"></div>
+  <div id="flex">
+    <div style="height: 50px; break-after: column;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 350px;"></div>
+    <div style="height: 100px;"></div>
+    <div style="height: 50px; break-after: column;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 250px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html
new file mode 100644
index 0000000..626d267
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-023.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: break-before values on the first item
+  are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 20px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 400px;
+  }
+  #flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div style="width: 20px; height: 50px; background: green;"></div>
+  <div style="width: 20px; height: 50px; background: green;"></div>
+  <div id="flex">
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 250px;"></div>
+    <div style="height: 100px; break-before: avoid;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 150px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html
new file mode 100644
index 0000000..4cd53ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-024.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: break-after values on the first item
+  are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 2;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 50px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 50px;
+  }
+  #flex > div {
+    background: green;
+    width: 25x;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div style="width: 50px; height: 50px; background: green;"></div>
+  <div id="flex">
+    <div style="height: 25px;"></div>
+    <div style="height: 25px; break-after: avoid;"></div>
+    <div style="height: 50px;"></div>
+  </div>
+  <div style="width: 50px; height: 50px; background: green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html
new file mode 100644
index 0000000..93dd9b4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-025.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: break-before values on the first item
+  are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 20px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 350px;
+  }
+  #flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div style="width: 20px; height: 50px; background: green;"></div>
+  <div id="flex">
+    <div style="height: 50px; break-before: avoid;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 250px;"></div>
+    <div style="height: 100px; break-before: column;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 150px;"></div>
+  </div>
+  <div style="width: 20px; height: 50px; background: green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html
new file mode 100644
index 0000000..204cc15
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-026.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: break-after values on the first item
+  are propagated to the flex container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 2;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 50px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 50px;
+  }
+  #flex > div {
+    background: green;
+    width: 25x;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div id="flex">
+    <div style="height: 25px;"></div>
+    <div style="height: 25px; break-after: column;"></div>
+    <div style="height: 50px; break-after: avoid;"></div>
+  </div>
+  <div style="width: 50px; height: 50px; background: green;"></div>
+  <div style="width: 50px; height: 50px; background: green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html
new file mode 100644
index 0000000..95b7390
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-027.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: early break inside columns.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 2;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 50px;
+    background: green;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 200px;
+  }
+  #flex > div {
+    background: green;
+    width: 25px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="top: 50px; height: 50px;"></div>
+  <div class="abs" style="left: 100px; height: 50px; background: white;"></div>
+  <div id="flex">
+    <div style="height: 50px;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 100px; break-before: avoid;"></div>
+    <div style="height: 50px;"></div>
+    <div style="height: 50px; break-after: avoid;"></div>
+    <div style="height: 100px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html
new file mode 100644
index 0000000..d96cd56
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-028.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: early break inside columns.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 2;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  .abs {
+    position: absolute;
+    width: 50px;
+    background: white;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 200px;
+  }
+  #flex > div {
+    background: green;
+    width: 25px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="abs" style="left: 100px; height: 50px;"></div>
+  <div id="flex">
+    <div>
+      <div style="height: 50px; width: 25px;"></div>
+      <div style="height: 50px; width: 25px;"></div>
+    </div>
+    <div style="height: 100px; break-before: avoid;"></div>
+    <div>
+      <div style="height: 50px; width: 25px;"></div>
+      <div style="height: 50px; width: 25px; break-after: avoid;"></div>
+    </div>
+    <div style="height: 100px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html
new file mode 100644
index 0000000..ec47c9f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-029.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: we shouldn't insert a forced break if
+  there's no preceding content at the start of a fragmentainer.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 2;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div style="display: flex; flex-direction: column; flex-wrap: wrap; width: 50px; height: 200px;">
+    <div style="height: 100px; width: 25px; break-before: column; background: green;"></div>
+    <div style="height: 100px; width: 25px; break-before: column; background: green;"></div>
+    <div style="height: 50px; width: 25px; break-before: column; background: green;"></div>
+    <div style="height: 150px; width: 25px; background: green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html
new file mode 100644
index 0000000..4439845
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-030.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation: the flex container consumes the
+  remaining fragmentainer space if an item breaks before.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 2;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div style="display: flex; flex-direction: column; flex-wrap: wrap; width: 50px; height: 200px; background: green;">
+    <div style="height: 10px; width: 25px;"></div>
+    <div style="height: 100px; width: 25px; break-before: column;"></div>
+    <div style="height: 50px; width: 25px;"></div>
+    <div style="height: 100px; width: 25px; break-before: column;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html
new file mode 100644
index 0000000..fc88e70
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-031.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with break-inside: avoid.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+    position: relative;
+  }
+  #flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div id="flex">
+    <div>
+      <div style="contain: size; width: 10px; height: 90px;"></div>
+      <div style="contain: size; width: 10px; height: 80px;"></div>
+    </div>
+    <div style="height: 30px; break-inside: avoid;"></div>
+    <div style="height: 170px;"></div>
+    <div style="height: 100px;"></div>
+    <div>
+      <div style="contain: size; width: 10px; height: 90px;"></div>
+      <div style="contain: size; width: 10px; height: 80px;"></div>
+    </div>
+    <div style="height: 30px; break-inside: avoid;"></div>
+    <div style="height: 170px;"></div>
+    <div style="height: 100px;"></div>
+    <div style="position: absolute; height: 20px; width: 20px; top: 180px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html
new file mode 100644
index 0000000..978dbd1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-column-flex-fragmentation-032.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>
+  Multi-line column flex fragmentation with forced break and negative margin.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+    position: relative;
+    background: red;
+    z-index: -1;
+  }
+  #flex {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: wrap;
+    height: 500px;
+    position: relative;
+  }
+  #flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div id="flex">
+    <div style="height: 150px;"></div>
+    <div style="height: 350px; break-before: column; margin-top: -50px;"></div>
+    <div style="height: 150px;"></div>
+    <div style="height: 300px; break-before: column;"></div>
+  </div>
+  <div style="position: absolute; top: 50px; left: 20px; width: 20px; height: 50px; background: green;"></div>
+  <div style="position: absolute; top: -50px; left: 40px; width: 10px; height: 50px; background: white;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/system-color-consistency.html b/third_party/blink/web_tests/external/wpt/css/css-color/system-color-consistency.html
new file mode 100644
index 0000000..f8fc5ef0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-color/system-color-consistency.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <title>CSS Color 4: Computed value of color-contrast()</title>
+  <link rel="author" title="Jan Keitel" href="mailto:jkeitel@google.com">
+  <link rel="help" href="https://www.w3.org/TR/css-color-4/#css-system-colors">
+  <meta name="assert" content="computed style of form elements is consistent with definition of system colors">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/css/support/computed-testcommon.js"></script>
+</head>
+
+<body>
+  <div id="target"></div>
+  <!-- Reference elements -->
+  <!-- Buttons -->
+  <div id="buttons">
+    <button name="button"></button><input type="submit" name="submit button"><input type="reset" name="reset button">
+  </div>
+  <script>
+    for (let element of document.getElementById("buttons").children) {
+      style = document.defaultView.getComputedStyle(element);
+      test_computed_value(`color`, `ButtonText`, style.getPropertyValue('color'), 'has the same color as text on a ' + element.name);
+    }
+    // Test with no specified target contrast
+  </script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/layout-dependent-focus-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/layout-dependent-focus-expected.txt
deleted file mode 100644
index c69221b..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/layout-dependent-focus-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Verify that onblur is called on hidden input assert_unreached: Event listener for 'blur' not called Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch-ref.html
new file mode 100644
index 0000000..33e8669d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: The subgrid is always stretched in its subgridded dimension(s)</title>
+  <link rel="author" title="Matt Woodrow" href="mailto:mattwoodrow@apple.com">
+  <link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrid-box-alignment">
+  <style>
+   body {
+     margin: 0;
+   }
+  .grid {
+    display: inline-block;
+    width: 100px;
+    height: 100px;
+    background-color: blue;
+  }
+  </style>
+</head>
+<body>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+
+  <br>
+
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+  <div class="grid"></div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch.html
new file mode 100644
index 0000000..321e12d2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-stretch.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: The subgrid is always stretched in its subgridded dimension(s)</title>
+  <link rel="author" title="Matt Woodrow" href="mailto:mattwoodrow@apple.com">
+  <link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrid-box-alignment">
+  <link rel="match" href="subgrid-stretch-ref.html">
+  <style>
+  body {
+    margin: 0;
+  }
+  .grid {
+    display: inline-grid;
+    grid-template-columns: 100px;
+    grid-template-rows: 100px;
+  }
+  .subgrid {
+    display: grid;
+    background-color: blue;
+  }
+  .rows {
+    grid-template-rows: subgrid;
+    align-self: baseline;
+  }
+  .columns {
+    grid-template-columns: subgrid;
+    justify-self: baseline;
+  }
+  .vrl {
+    writing-mode: vertical-rl;
+  }
+  .vrl.rows {
+    align-self: initial;
+    justify-self: baseline;
+  }
+  .vrl.columns {
+    justify-self: initial;
+    align-self: baseline;
+  }
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <div class="subgrid rows" style="height: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid rows" style="height: 150px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid rows" style="max-height: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid rows" style="min-height: 150px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid columns" style="width: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid columns" style="width: 150px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid columns" style="max-width: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid columns" style="min-width: 150px;"></div>
+  </div>
+
+  <br>
+
+  <div class="grid">
+    <div class="subgrid vrl rows" style="width: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid vrl rows" style="width: 150px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid vrl rows" style="max-width: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid vrl rows" style="min-width: 150px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid vrl columns" style="height: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid vrl columns" style="height: 150px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid vrl columns" style="max-height: 50px;"></div>
+  </div>
+  <div class="grid">
+    <div class="subgrid vrl columns" style="min-height: 150px;"></div>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html
index 3f253c3..a8255137 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/parsing/transition-computed.html
@@ -14,6 +14,11 @@
 <script>
 // <single-transition> = [ none | <single-transition-property> ] ||
 // <time> || <easing-function> || <time>
+
+test(() => {
+  assert_equals(getComputedStyle(document.getElementById('target')).transition, "all 0s ease 0s");
+}, "Default transition value");
+
 test_computed_value("transition", "1s", "all 1s ease 0s");
 test_computed_value("transition", "cubic-bezier(0, -2, 1, 3)", "all 0s cubic-bezier(0, -2, 1, 3) 0s");
 test_computed_value("transition", "1s -3s", "all 1s ease -3s");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html
index 0202da4..f3f50f8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/contain.html
@@ -21,6 +21,7 @@
   { syntax: 'layout' },
   { syntax: 'style' },
   { syntax: 'paint' },
+  { syntax: 'inline-size' },
 ]);
 
 runUnsupportedPropertyTests('contain', [
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/broken-reference-crash-001.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/broken-reference-crash-001.html
new file mode 100644
index 0000000..6e18e06
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/broken-reference-crash-001.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>CSS Filters: non-existent filters should not crash</title>
+  <link rel="author" title="Philip Rogers" href="mailto:pdr@chromium.org">
+  <link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#FilterProperty">
+  <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1304748">
+  <meta name="assert" content="Check that a non-existent filter with an animation does not crash."/>
+  <style>
+    @keyframes filter_keyframes {
+      0% { filter: blur(5px); }
+      100% { filter: blur(10px); }
+    }
+    #animating {
+      font-size: 36pt;
+      animation: filter_keyframes 999s linear 999s;
+      will-change: contents;
+      filter: url("#doesotexist");
+    }
+    #child {
+      width: 10px;
+      height: 10px;
+      transform: translateX(10px);
+    }
+    #force_document_scroll {
+      width: 2000px;
+      height: 2000px;
+    }
+    </style>
+
+</head>
+<body>
+  <div id="animating">
+    <div id="child">PASS</div>
+  </div>
+  <div id="force_document_scroll"></div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
index dd751c3..37ceb1c 100644
--- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
+++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
@@ -167,7 +167,7 @@
     .setContext(frames[0])
     .keyDown("p")
     .keyUp("p");
-actions.send();
+await actions.send();
 ```
 
 Note that if an action uses an element reference, the context will be
diff --git a/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https-expected.txt b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https-expected.txt
new file mode 100644
index 0000000..3a82cfc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Query "geolocation" powerful feature assert_equals: permission's state must be "prompt" by default expected "prompt" but got "denied"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https.html b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https.html
new file mode 100644
index 0000000..40628433
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/geolocation-API/permission.https.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test geolocation is a powerful feature via Permissions API</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<script>
+promise_test(async (test) => {
+  const status = await navigator.permissions.query({ name: "geolocation" });
+  assert_true(status instanceof PermissionStatus);
+  assert_equals(status.name, "geolocation", `permission's name must be "geolocation"`);
+  assert_equals(status.state, "prompt", `permission's state must be "prompt" by default`);
+}, `Query "geolocation" powerful feature`);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/editing/the-hidden-attribute/hidden-idl.html b/third_party/blink/web_tests/external/wpt/html/editing/the-hidden-attribute/hidden-idl.html
new file mode 100644
index 0000000..331b63f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/editing/the-hidden-attribute/hidden-idl.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/7475">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div>hello</div>
+<script>
+const div = document.querySelector('div');
+
+function runPropertyTest(assignedValue, expectedValue, expectedAttribute) {
+  test(() => {
+    div.hidden = assignedValue;
+    assert_equals(div.hidden, expectedValue,
+      `div.hidden = ${JSON.stringify(assignedValue)} should return ${JSON.stringify(expectedValue)}`);
+    assert_equals(div.getAttribute('hidden'), expectedAttribute,
+      `div.hidden = ${JSON.stringify(assignedValue)} should set the hidden attribute to ${JSON.stringify(expectedAttribute)}`);
+  }, `div.hidden = ${Number.isNaN(assignedValue) ? 'NaN' : JSON.stringify(assignedValue)}`);
+}
+
+function runAttributeTest(assignedAttribute, expectedValue) {
+  test(() => {
+    div.setAttribute('hidden', assignedAttribute);
+    assert_equals(div.hidden, expectedValue);
+  }, `div.setAttribute('hidden', ${JSON.stringify(assignedAttribute)}) should make div.hidden return ${JSON.stringify(expectedValue)}`);
+}
+
+runPropertyTest(false, false, null);
+runPropertyTest(true, true, '');
+runPropertyTest('foo', true, '');
+runPropertyTest('false', true, '');
+runPropertyTest('', false, null);
+
+runAttributeTest('false', true);
+runAttributeTest('foo', true);
+
+runPropertyTest('until-found', 'until-found', 'until-found');
+runPropertyTest('UNTIL-FOUND', 'until-found', 'until-found');
+runPropertyTest('UnTiL-FoUnD', 'until-found', 'until-found');
+runPropertyTest('unt\u0131l-found', true, '');
+runPropertyTest('unt\u0130l-found', true, '');
+
+runPropertyTest(null, false, null);
+runPropertyTest(undefined, false, null);
+
+runPropertyTest(1, true, '');
+runPropertyTest(0, false, null);
+runPropertyTest(NaN, false, null);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js b/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js
index ef097961..3e5ba74 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js
@@ -29,7 +29,7 @@
    *    .keyDown("p")
    *    .keyUp("p");
    *
-   * actions.send();
+   * await actions.send();
    *
    * @param {number} [defaultTickDuration] - The default duration of a
    * tick. Be default this is set ot 16ms, which is one frame time
diff --git a/third_party/blink/web_tests/external/wpt/resources/testharness.js b/third_party/blink/web_tests/external/wpt/resources/testharness.js
index fcff33a..a6f48e1f9 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testharness.js
@@ -648,7 +648,7 @@
     /**
      * Create a promise test.
      *
-     * Promise tests are tests which are represeted by a promise
+     * Promise tests are tests which are represented by a promise
      * object. If the promise is fulfilled the test passes, if it's
      * rejected the test fails, otherwise the test passes.
      *
diff --git a/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png b/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png
index e331c49b..355aa8c 100644
--- a/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png
+++ b/third_party/blink/web_tests/fast/borders/border-radius-with-box-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html b/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html
index 65d8434..248b3cd1 100644
--- a/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html
+++ b/third_party/blink/web_tests/fast/gradients/unprefixed-repeating-gradient-color-hint.html
@@ -11,7 +11,7 @@
     }
 
     .linear1 {
-      background-image: repeating-linear-gradient(to right, red 90%, 95%, blue 105%);
+      background-image: repeating-linear-gradient(to right, green 10%, 20%, blue 105%);
     }
 
     .linear2 {
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html
index b14f9ba..e7ae400b4 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-events.https.html
@@ -5,76 +5,74 @@
 <script>
 'use strict';
 
-['plan-b', 'unified-plan'].forEach(sdpSemantics => {
-  promise_test(async t => {
-    console.log(sdpSemantics + ': onaddstream fires for new remote streams.');
-    const pc1 = new RTCPeerConnection({sdpSemantics:sdpSemantics});
-    t.add_cleanup(() => { pc1.close(); });
-    const pc2 = new RTCPeerConnection({sdpSemantics:sdpSemantics});
-    t.add_cleanup(() => { pc2.close(); });
+promise_test(async t => {
+  console.log('onaddstream fires for new remote streams.');
+  const pc1 = new RTCPeerConnection();
+  t.add_cleanup(() => { pc1.close(); });
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => { pc2.close(); });
 
-    const localStream = await navigator.mediaDevices.getUserMedia({audio:true});
-    const [localTrack] = localStream.getTracks();
-    pc1.addTrack(localTrack, localStream);
+  const localStream = await navigator.mediaDevices.getUserMedia({audio:true});
+  const [localTrack] = localStream.getTracks();
+  pc1.addTrack(localTrack, localStream);
 
-    const streamWatcher = new EventWatcher(t, pc2, ['addstream']);
-    const addstreamEvent = streamWatcher.wait_for('addstream');
-    await performOfferAnswer(pc1, pc2);
-    const remoteStream = (await addstreamEvent).stream;
-    assert_equals(remoteStream.getTracks().length, 1,
-                  'The stream should contain a track.');
-  }, sdpSemantics + ': onaddstream fires for new remote streams.');
+  const streamWatcher = new EventWatcher(t, pc2, ['addstream']);
+  const addstreamEvent = streamWatcher.wait_for('addstream');
+  await performOfferAnswer(pc1, pc2);
+  const remoteStream = (await addstreamEvent).stream;
+  assert_equals(remoteStream.getTracks().length, 1,
+                'The stream should contain a track.');
+}, 'onaddstream fires for new remote streams.');
 
-  promise_test(async t => {
-    console.log(sdpSemantics + ': onremovestream for removed remote streams.');
-    const pc1 = new RTCPeerConnection({sdpSemantics:sdpSemantics});
-    t.add_cleanup(() => { pc1.close(); });
-    const pc2 = new RTCPeerConnection({sdpSemantics:sdpSemantics});
-    t.add_cleanup(() => { pc2.close(); });
+promise_test(async t => {
+  console.log('onremovestream for removed remote streams.');
+  const pc1 = new RTCPeerConnection();
+  t.add_cleanup(() => { pc1.close(); });
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => { pc2.close(); });
 
-    const localStream = await navigator.mediaDevices.getUserMedia({audio:true});
-    const [localTrack] = localStream.getTracks();
-    const sender = pc1.addTrack(localTrack, localStream);
+  const localStream = await navigator.mediaDevices.getUserMedia({audio:true});
+  const [localTrack] = localStream.getTracks();
+  const sender = pc1.addTrack(localTrack, localStream);
 
-    const streamWatcher = new EventWatcher(t, pc2, ['addstream',
-                                                    'removestream']);
-    const addstreamEvent = streamWatcher.wait_for('addstream');
-    await performOfferAnswer(pc1, pc2);
-    const remoteStream = (await addstreamEvent).stream;
-    const removestreamEvent = streamWatcher.wait_for('removestream');
+  const streamWatcher = new EventWatcher(t, pc2, ['addstream',
+                                                  'removestream']);
+  const addstreamEvent = streamWatcher.wait_for('addstream');
+  await performOfferAnswer(pc1, pc2);
+  const remoteStream = (await addstreamEvent).stream;
+  const removestreamEvent = streamWatcher.wait_for('removestream');
 
-    pc1.removeTrack(sender);
-    await performOfferAnswer(pc1, pc2);
-    const removedRemoteStream = (await removestreamEvent).stream;
-    assert_equals(removedRemoteStream, remoteStream);
-  }, sdpSemantics + ': onremovestream for removed remote streams.');
+  pc1.removeTrack(sender);
+  await performOfferAnswer(pc1, pc2);
+  const removedRemoteStream = (await removestreamEvent).stream;
+  assert_equals(removedRemoteStream, remoteStream);
+}, 'onremovestream for removed remote streams.');
 
-  promise_test(async t => {
-    console.log(sdpSemantics + ': onaddstream for a re-added remote stream.');
-    const pc1 = new RTCPeerConnection({sdpSemantics:sdpSemantics});
-    t.add_cleanup(() => { pc1.close(); });
-    const pc2 = new RTCPeerConnection({sdpSemantics:sdpSemantics});
-    t.add_cleanup(() => { pc2.close(); });
+promise_test(async t => {
+  console.log('onaddstream for a re-added remote stream.');
+  const pc1 = new RTCPeerConnection();
+  t.add_cleanup(() => { pc1.close(); });
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => { pc2.close(); });
 
-    const localStream = await navigator.mediaDevices.getUserMedia({audio:true});
-    const [localTrack] = localStream.getTracks();
-    const sender = pc1.addTrack(localTrack, localStream);
+  const localStream = await navigator.mediaDevices.getUserMedia({audio:true});
+  const [localTrack] = localStream.getTracks();
+  const sender = pc1.addTrack(localTrack, localStream);
 
-    const streamWatcher = new EventWatcher(t, pc2, ['addstream']);
-    let addstreamEvent = streamWatcher.wait_for('addstream');
-    await performOfferAnswer(pc1, pc2);
-    const remoteStream = (await addstreamEvent).stream;
+  const streamWatcher = new EventWatcher(t, pc2, ['addstream']);
+  let addstreamEvent = streamWatcher.wait_for('addstream');
+  await performOfferAnswer(pc1, pc2);
+  const remoteStream = (await addstreamEvent).stream;
 
-    pc1.removeTrack(sender);
-    await performOfferAnswer(pc1, pc2);
+  pc1.removeTrack(sender);
+  await performOfferAnswer(pc1, pc2);
 
-    pc1.addTrack(localTrack, localStream);
-    addstreamEvent = streamWatcher.wait_for('addstream');
-    await performOfferAnswer(pc1, pc2);
-    const readdedRemoteStream = (await addstreamEvent).stream;
-    assert_equals(readdedRemoteStream.id, remoteStream.id);
-  }, sdpSemantics + ': onaddstream for a re-added remote stream.');
-});
+  pc1.addTrack(localTrack, localStream);
+  addstreamEvent = streamWatcher.wait_for('addstream');
+  await performOfferAnswer(pc1, pc2);
+  const readdedRemoteStream = (await addstreamEvent).stream;
+  assert_equals(readdedRemoteStream.id, remoteStream.id);
+}, 'onaddstream for a re-added remote stream.');
 
 async function performOfferAnswer(pc1, pc2) {
   const offer = await pc1.createOffer();
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-sdpSemantics.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-sdpSemantics.html
deleted file mode 100644
index 508cd20..0000000
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-sdpSemantics.html
+++ /dev/null
@@ -1,188 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>RTCPeerConnection.createOffer</title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-  </head>
-  <body>
-    <script>
-// These tests are testing the non-standard "sdpFormat" extension.
-promise_test(async t => {
-  const pc = new RTCPeerConnection({sdpSemantics: "plan-b"});
-  t.add_cleanup(() => pc.close());
-  return navigator.mediaDevices.getUserMedia({audio:true})
-    .then(stream => {
-      stream2 = stream.clone();
-      pc.addTrack(stream.getAudioTracks()[0]);
-      pc.addTrack(stream2.getAudioTracks()[0]);
-      return pc.createOffer();
-    })
-    .then(offer => {
-      let section_count = offer.sdp.match(/\nm=audio /g || []).length;
-      assert_equals(section_count, 1);
-    });
-}, 'Plan B createOffer creates only one media section for two audio tracks');
-
-promise_test(async t => {
-  const pc = new RTCPeerConnection({sdpSemantics: "unified-plan"});
-  t.add_cleanup(() => pc.close());
-  return navigator.mediaDevices.getUserMedia({audio:true})
-    .then(stream => {
-      stream2 = stream.clone();
-      pc.addTrack(stream.getAudioTracks()[0]);
-      pc.addTrack(stream2.getAudioTracks()[0]);
-      return pc.createOffer();
-    })
-    .then(offer => {
-      let section_count = (offer.sdp.match(/\nm=audio /g) || []).length;
-      assert_equals(section_count, 2);
-    });
-}, 'Unified plan createOffer for audio creates two media sections ' +
-   'for two audio tracks');
-
-promise_test(async t => {
-  const pc = new RTCPeerConnection({sdpSemantics: "plan-b"});
-  t.add_cleanup(() => pc.close());
-  return navigator.mediaDevices.getUserMedia({video:true})
-    .then(stream => {
-      stream2 = stream.clone();
-      pc.addTrack(stream.getVideoTracks()[0]);
-      pc.addTrack(stream2.getVideoTracks()[0]);
-      return pc.createOffer();
-    })
-    .then(offer => {
-      let section_count = offer.sdp.match(/\nm=video /g || []).length;
-      assert_equals(section_count, 1);
-    });
-}, 'Plan B createOffer for video creates one media section for two ' +
-   'video tracks');
-
-promise_test(async t => {
-  const pc = new RTCPeerConnection({sdpSemantics: "unified-plan"});
-  t.add_cleanup(() => pc.close());
-  return navigator.mediaDevices.getUserMedia({video:true})
-    .then(stream => {
-      stream2 = stream.clone();
-      pc.addTrack(stream.getVideoTracks()[0]);
-      pc.addTrack(stream2.getVideoTracks()[0]);
-      return pc.createOffer();
-    })
-    .then(offer => {
-      let section_count = offer.sdp.match(/\nm=video /g || []).length;
-      assert_equals(section_count, 2);
-    });
-}, 'Unified plan createOffer for video creates two media sections ' +
-'for two video tracks');
-
-// TODO(hta): Add a test case with both audio and video tracks.
-
-promise_test(async t => {
-  const pc1 = new RTCPeerConnection({sdpSemantics: "plan-b"});
-  t.add_cleanup(() => pc1.close());
-  const pc2 = new RTCPeerConnection({sdpSemantics: "plan-b"});
-  t.add_cleanup(() => pc2.close());
-  return navigator.mediaDevices.getUserMedia({video: true})
-    .then(stream => {
-      pc1.addTrack(stream.getVideoTracks()[0]);
-      return pc1.createOffer();
-    })
-    .then(offer => {
-      return Promise.all([pc2.setRemoteDescription(offer),
-                          pc1.setLocalDescription(offer)]);
-    })
-    .then(() => {
-      return pc2.createAnswer();
-    })
-    .then(answer => {
-      return Promise.all([pc2.setLocalDescription(answer),
-                          pc1.setRemoteDescription(answer)]);
-    });
-}, 'Plan B can connect to itself');
-
-promise_test(async t => {
-  const pc1 = new RTCPeerConnection({sdpSemantics: "unified-plan"});
-  t.add_cleanup(() => pc1.close());
-  const pc2 = new RTCPeerConnection({sdpSemantics: "unified-plan"});
-  t.add_cleanup(() => pc2.close());
-  return navigator.mediaDevices.getUserMedia({video: true})
-    .then(stream => {
-      pc1.addTrack(stream.getVideoTracks()[0]);
-      return pc1.createOffer();
-    })
-    .then(offer => {
-      return Promise.all([pc2.setRemoteDescription(offer),
-                          pc1.setLocalDescription(offer)]);
-    })
-    .then(() => {
-      return pc2.createAnswer();
-    })
-    .then(answer => {
-      return Promise.all([pc2.setLocalDescription(answer),
-                          pc1.setRemoteDescription(answer)]);
-    });
-}, 'Unified Plan can connect to itself');
-
-promise_test(async t => {
-  const pc1 = new RTCPeerConnection({sdpSemantics: "unified-plan"});
-  t.add_cleanup(() => pc1.close());
-  const pc2 = new RTCPeerConnection({sdpSemantics: "plan-b"});
-  t.add_cleanup(() => pc2.close());
-  return navigator.mediaDevices.getUserMedia({video: true})
-    .then(stream => {
-      pc1.addTrack(stream.getVideoTracks()[0]);
-      return pc1.createOffer();
-    })
-    .then(offer => {
-      return Promise.all([pc2.setRemoteDescription(offer),
-                          pc1.setLocalDescription(offer)]);
-    })
-    .then(() => {
-      return pc2.createAnswer();
-    })
-    .then(answer => {
-      return Promise.all([pc2.setLocalDescription(answer),
-                          pc1.setRemoteDescription(answer)]);
-    });
-
-}, 'Unified plan with one video track can connect to Plan B');
-
-promise_test(async t => {
-  pc1 = new RTCPeerConnection({sdpSemantics: "plan-b"});
-  t.add_cleanup(() => pc1.close());
-  pc2 = new RTCPeerConnection({sdpSemantics: "unified-plan"});
-  t.add_cleanup(() => pc2.close());
-  return navigator.mediaDevices.getUserMedia({video: true})
-    .then(stream => {
-      pc1.addTrack(stream.getVideoTracks()[0]);
-      return pc1.createOffer();
-    })
-    .then(offer => {
-      return Promise.all([pc2.setRemoteDescription(offer),
-                          pc1.setLocalDescription(offer)]);
-    })
-    .then(() => {
-      return pc2.createAnswer();
-    })
-    .then(answer => {
-
-      return Promise.all([pc2.setLocalDescription(answer),
-                          pc1.setRemoteDescription(answer)]);
-    });
-
-}, 'Plan B with one video track can connect to Unified plan');
-
-promise_test(async t => {
-  pc1 = new RTCPeerConnection({sdpSemantics: 'unified-plan'});
-  t.add_cleanup(() => pc1.close());
-  pc2 = new RTCPeerConnection({sdpSemantics: 'plan-b'});
-  t.add_cleanup(() => pc2.close());
-  pc1.addTransceiver('video');
-  pc1.addTransceiver('video');
-  await pc2.setRemoteDescription(await pc1.createOffer());
-  assert_equals(pc1.getReceivers().length, 2);
-  assert_equals(pc2.getReceivers().length, 1);
-}, 'Plan B rejects additional m-lines without throwing an error');
-    </script>
-  </body>
-</html>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png
index eee32fa..1d85574 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/css/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png
index 3c2f1a0..758f638c 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png
+++ b/third_party/blink/web_tests/flag-specific/highdpi/compositing/geometry/video-fixed-scrolling-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-animations/parsing/animation-computed-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
new file mode 100644
index 0000000..7cf62fe
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+FAIL Default animation value assert_equals: expected "0s ease 0s 1 normal none running none" but got "none 0s ease 0s 1 normal none running"
+PASS Property animation value '1s'
+PASS Property animation value 'cubic-bezier(0, -2, 1, 3)'
+PASS Property animation value '1s -3s'
+PASS Property animation value '4'
+PASS Property animation value 'reverse'
+PASS Property animation value 'both'
+PASS Property animation value 'paused'
+PASS Property animation value 'none'
+PASS Property animation value 'anim'
+PASS Property animation value 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)'
+PASS Property animation value 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
new file mode 100644
index 0000000..7cf62fe
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/virtual/threaded/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+FAIL Default animation value assert_equals: expected "0s ease 0s 1 normal none running none" but got "none 0s ease 0s 1 normal none running"
+PASS Property animation value '1s'
+PASS Property animation value 'cubic-bezier(0, -2, 1, 3)'
+PASS Property animation value '1s -3s'
+PASS Property animation value '4'
+PASS Property animation value 'reverse'
+PASS Property animation value 'both'
+PASS Property animation value 'paused'
+PASS Property animation value 'none'
+PASS Property animation value 'anim'
+PASS Property animation value 'anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)'
+PASS Property animation value 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png
index 6afdc3a..57a5729 100644
--- a/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png
+++ b/third_party/blink/web_tests/flag-specific/skia-vulkan-swiftshader/compositing/visibility/visibility-simple-video-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js b/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js
index 271580e..055b0f1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js
+++ b/third_party/blink/web_tests/http/tests/devtools/bindings/jssourcemap-bindings-overlapping-sources.js
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+const GC = async () => {
+  await TestRunner.evaluateInPageAsync(`new Promise(resolve =>
+    GCController.asyncCollectAll(resolve))`);
+};
+
 (async function () {
   TestRunner.addResult(`Verify that JavaScript SourceMap handle different sourcemap with overlapping sources.`);
   await TestRunner.loadTestModule('bindings_test_runner');
diff --git a/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js b/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js
index 1b917ab..174713f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js
+++ b/third_party/blink/web_tests/http/tests/devtools/bindings/sourcemap-navigator-multiple-frames.js
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+const GC = async () => {
+  await TestRunner.evaluateInPageAsync(`new Promise(resolve =>
+    GCController.asyncCollectAll(resolve))`);
+};
+
 (async function () {
   TestRunner.addResult(`Verify that SourceMap sources are correctly displayed in navigator.\n`);
   await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js b/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js
index cd3db5e8..6511808 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/conversion/attribution-invalid-data.js
@@ -14,9 +14,17 @@
     <!DOCTYPE html>
     <img src="https://devtools.test:8443/inspector-protocol/conversion/resources/conversion-redirect.php"></img>
     <img src="https://devtools.test:8443/inspector-protocol/conversion/resources/conversion-redirect.php?trigger-data=badinteger"></img>`);
-  const issues = await Promise.all(issuePromises);
-  testRunner.log(issues[0].params.issue, "Issue reported: ", ['requestId']);
-  testRunner.log(issues[1].params.issue, "Issue reported: ", ['requestId']);
+
+  const issues = (await Promise.all(issuePromises)).map(issue => issue.params.issue);
+
+  // In rare cases the issues arrive in reverse order. Lets sort them to be sure.
+  issues.sort((i1, i2) => {
+    return (i1.invalidParamter < i2.invalidParamter) ? -1 :
+           (i2.invalidParamter < i1.invalidParamter) ?  1 : 0;
+  });
+
+  testRunner.log(issues[0], "Issue reported: ", ['requestId']);
+  testRunner.log(issues[1], "Issue reported: ", ['requestId']);
 
   testRunner.completeTest();
 })
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js
index b2ade739..5a701e2 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js
@@ -10,7 +10,7 @@
       return;
     }
 
-    testRunner.faiil(`FAIL: ${
+    testRunner.fail(`FAIL: ${
         description}: networkId !== requestId or one or more ids are missing (${
         networkId} vs. ${requestId})`);
   };
@@ -43,15 +43,27 @@
     });
   });
 
-  const [initScriptWillBeSentEvent, [ initScriptPausedEvent, subFetchWillBeSentEvent, subFetchPausedEvent ]] =
+  const [[
+    initScriptWillBeSentEvent, initScriptPausedEvent, subFetchWillBeSentEvent,
+    subFetchPausedEvent
+  ]] =
       await Promise.all([
-        dp.Network.onceRequestWillBeSent(e => e.params.request.url.endsWith('/service-worker-with-fetch.js')),
         workerProtocol.then(wdp => Promise.all([
-            wdp.Fetch.onceRequestPaused(e => e.params.request.url.endsWith('/service-worker-with-fetch.js')),
-            wdp.Network.onceRequestWillBeSent(e => e.params.request.url.endsWith('/request-within-service-worker')),
-            wdp.Fetch.onceRequestPaused(e => e.params.request.url.endsWith('/request-within-service-worker')),
+          wdp.Network.onceRequestWillBeSent(
+              e => e.params.request.url.endsWith(
+                  '/service-worker-with-fetch.js')),
+          wdp.Fetch.onceRequestPaused(
+              e => e.params.request.url.endsWith(
+                  '/service-worker-with-fetch.js')),
+          wdp.Network.onceRequestWillBeSent(
+              e => e.params.request.url.endsWith(
+                  '/request-within-service-worker')),
+          wdp.Fetch.onceRequestPaused(
+              e => e.params.request.url.endsWith(
+                  '/request-within-service-worker')),
         ])),
-        page.navigate(testRunner.url('./resources/service-worker-with-fetch.html')),
+        page.navigate(
+            testRunner.url('./resources/service-worker-with-fetch.html')),
       ]);
 
   assertNetworkIdAlignment(
diff --git a/third_party/blink/web_tests/platform/linux/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/linux/compositing/visibility/visibility-simple-video-layer-expected.png
new file mode 100644
index 0000000..f17218b9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/compositing/visibility/visibility-simple-video-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png b/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
index ba9b4ef..0a6653d 100644
--- a/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png
index e7d09d54..0bf4b5b 100644
--- a/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/css/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/linux/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
deleted file mode 100644
index 2506bca..0000000
--- a/third_party/blink/web_tests/platform/linux/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/linux/images/yuv-decode-eligible/color-profile-layer-expected.png
new file mode 100644
index 0000000..ea53e31
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/images/yuv-decode-eligible/color-profile-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png b/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png
index 3a15e02..52c49c1 100644
--- a/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png
+++ b/third_party/blink/web_tests/platform/linux/transforms/shadows-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/shadows-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/shadows-expected.png
new file mode 100644
index 0000000..52c49c1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/shadows-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
index a37a161..8a11dec 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
index 8ec696c..c73f5ae9 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
new file mode 100644
index 0000000..4de7b2969
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index eb2b80a..5f012ce 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index b9717090..ada90607 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index b9717090..ada90607 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/linux/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png
new file mode 100644
index 0000000..f17218b9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
index 77b66e55..775a8b4 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 92df9800..9369e29 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
index e8f7d78..61c7d5d 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
index bdd9125..3b54d6f9 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 322b0482..1173792 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
index 5b76563..295be6e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
index c7efa486..81b5f09 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 965b37f..4e3ced0 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
index fa25ea7..6fed204d7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
index 9a92395..8f3aa59 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 9359e5564..52819bd 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
index f3e933f..66cd3e9d 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png
index 64458de..f7cc94e 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png
+++ b/third_party/blink/web_tests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png
index 1fb603f0..c7cfce3d 100644
--- a/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/css/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
index eb9d753..1f9a4e4 100644
--- a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png
index 38c34f4..d866d9d 100644
--- a/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png
+++ b/third_party/blink/web_tests/platform/mac/images/yuv-decode-eligible/color-profile-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
index 831971c..1524b09 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
index 2249366..0fa1a61 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
index ca93aa5..da05ec4 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index b0d5eda5c..5d404c3f 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png
index 843dbae..f17218b9 100644
--- a/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png
+++ b/third_party/blink/web_tests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png b/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
index 11a7ad0..de9a177 100644
--- a/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png b/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png
index 26f8393f..51ace56 100644
--- a/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/css/shadow-multiple-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
index fb89d76..ad44d768 100644
--- a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png
index 3c38207..ea53e31 100644
--- a/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png
+++ b/third_party/blink/web_tests/platform/win/images/yuv-decode-eligible/color-profile-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png b/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png
index ef14df0..5fafff95 100644
--- a/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/shadows-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
index 15639ef..5b9ac852 100644
--- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-edge-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
index abd21bb4..8a5d1907 100644
--- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/validation-bubble-appearance-wrap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
index fb8aa4c..4de7b2969 100644
--- a/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/exotic-color-space/images/yuv-decode-eligible/color-profile-layer-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index daafc9d..cd45df5 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index d64bf62..dd9fc52 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index d64bf62..dd9fc52 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 73cc1a2..19a3c67 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor150/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index d9975d4a..c19eea8 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index d9975d4a..c19eea8 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200withzoom/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/resources/testdriver-actions.js b/third_party/blink/web_tests/resources/testdriver-actions.js
index ef097961..3e5ba74 100644
--- a/third_party/blink/web_tests/resources/testdriver-actions.js
+++ b/third_party/blink/web_tests/resources/testdriver-actions.js
@@ -29,7 +29,7 @@
    *    .keyDown("p")
    *    .keyUp("p");
    *
-   * actions.send();
+   * await actions.send();
    *
    * @param {number} [defaultTickDuration] - The default duration of a
    * tick. Be default this is set ot 16ms, which is one frame time
diff --git a/third_party/blink/web_tests/resources/testharness.js b/third_party/blink/web_tests/resources/testharness.js
index ca0a92a..a6f48e1f9 100644
--- a/third_party/blink/web_tests/resources/testharness.js
+++ b/third_party/blink/web_tests/resources/testharness.js
@@ -126,7 +126,7 @@
                             } catch (e) {}
                         }
                     }
-                    if (supports_post_message(w) && w !== self) {
+                    if (w !== self) {
                         w.postMessage(message_arg, "*");
                     }
                 });
@@ -648,7 +648,7 @@
     /**
      * Create a promise test.
      *
-     * Promise tests are tests which are represeted by a promise
+     * Promise tests are tests which are represented by a promise
      * object. If the promise is fulfilled the test passes, if it's
      * rejected the test fails, otherwise the test passes.
      *
@@ -4416,14 +4416,6 @@
 
     const get_stack = function() {
         var stack = new Error().stack;
-        // IE11 does not initialize 'Error.stack' until the object is thrown.
-        if (!stack) {
-            try {
-                throw new Error();
-            } catch (e) {
-                stack = e.stack;
-            }
-        }
 
         // 'Error.stack' is not supported in all browsers/versions
         if (!stack) {
@@ -4652,43 +4644,6 @@
         return "Untitled";
     }
 
-    function supports_post_message(w)
-    {
-        var supports;
-        var type;
-        // Given IE implements postMessage across nested iframes but not across
-        // windows or tabs, you can't infer cross-origin communication from the presence
-        // of postMessage on the current window object only.
-        //
-        // Touching the postMessage prop on a window can throw if the window is
-        // not from the same origin AND post message is not supported in that
-        // browser. So just doing an existence test here won't do, you also need
-        // to wrap it in a try..catch block.
-        try {
-            type = typeof w.postMessage;
-            if (type === "function") {
-                supports = true;
-            }
-
-            // IE8 supports postMessage, but implements it as a host object which
-            // returns "object" as its `typeof`.
-            else if (type === "object") {
-                supports = true;
-            }
-
-            // This is the case where postMessage isn't supported AND accessing a
-            // window property across origins does NOT throw (e.g. old Safari browser).
-            else {
-                supports = false;
-            }
-        } catch (e) {
-            // This is the case where postMessage isn't supported AND accessing a
-            // window property across origins throws (e.g. old Firefox browser).
-            supports = false;
-        }
-        return supports;
-    }
-
     /**
      * Setup globals
      */
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/event-handler-attributes.html b/third_party/blink/web_tests/virtual/mathml-disabled/event-handler-attributes.html
new file mode 100644
index 0000000..82ad550
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/mathml-disabled/event-handler-attributes.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+  ["click", "dblclick", "mouseover"].forEach(eventName => {
+    const attributeName = `on${eventName}`;
+    test(t => {
+      const el = document.createElementNS(
+        "http://www.w3.org/1998/Math/MathML",
+        "math"
+      );
+      el.setAttribute(attributeName, `window.${eventName}Happened = true;`);
+      assert_equals(el[attributeName], null);
+    }, `The ${attributeName} property on the <math> element must be null when MathML is disabled.`);
+    test(t => {
+      const el = document.createElementNS(
+        "http://www.w3.org/1998/Math/MathML",
+        "math"
+      );
+      let target = undefined;
+      el[attributeName] = (e) => { target = e.currentTarget; }
+      el.dispatchEvent(new Event(name));
+      assert_equals(target, undefined);
+    }, `${eventName} should not be handled by the <math> element when MathML is disabled.`);
+  });
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline-expected.html b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline-expected.html
new file mode 100644
index 0000000..8fa7b21
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<script src="../../resources/ahem.js"></script>
+<html>
+  <head>
+    <title>mtable baseline with MathML disabled (reference)</title>
+    <meta charset="utf-8"/>
+    <style>
+      math {
+          font: 25px/1 Ahem;
+          display: block;
+      }
+      mtext {
+          display: inline-block;
+      }
+      mtable {
+          display: inline-block;
+      }
+      mtr {
+          display: inline-block;
+      }
+      mtd {
+          display: inline-block;
+          padding: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <p>When MathML is disabled, these three squares should be aligned:</p>
+    <math><mtext>X</mtext><mtable><mtr><mtd><mtext>X</mtext></mtd></mtr></mtable><mtext>X</mtext></math>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline.html b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline.html
new file mode 100644
index 0000000..00cffd2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/mathml-disabled/mtable-baseline.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<script src="../../resources/ahem.js"></script>
+<html>
+  <head>
+    <title>mtable baseline with MathML disabled</title>
+    <meta charset="utf-8"/>
+    <style>
+      math {
+          font: 25px/1 Ahem;
+          display: block;
+      }
+      mtext {
+          display: inline-block;
+      }
+      mtable {
+          display: inline-table;
+      }
+      mtr {
+          display: table-row;
+      }
+      mtd {
+          display: table-cell;
+          padding: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <p>When MathML is disabled, these three squares should be aligned:</p>
+    <math><mtext>X</mtext><mtable><mtr><mtd><mtext>X</mtext></mtd></mtr></mtable><mtext>X</mtext></math>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes-expected.html b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes-expected.html
new file mode 100644
index 0000000..e7a54a68
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes-expected.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="../../resources/ahem.js"></script>
+<html>
+  <head>
+    <title>presentation attributes with MathML disabled</title>
+    <meta charset="utf-8"/>
+    <style>
+      math {
+          font: 25px/1 Ahem;
+      }
+      math, mtext {
+          display: inline-block;
+      }
+    </style>
+  </head>
+  <body>
+    <p>Presentation attributes should have no effects when MathML is disabled:</p>
+    <math><mtext>X</mtext><mtext>p</mtext></math>
+    <math><mtext>X</mtext></math>
+    <math><mtext>p</mtext></math>
+    <math><mtext>p</mtext></math>
+    <math style="text-transform: lowercase"><mtext>P</mtext></math>
+    <math><mtext style="font-size: math">X</mtext></math>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes.html b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes.html
new file mode 100644
index 0000000..3fd2ef0
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/mathml-disabled/presentation-attributes.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="../../resources/ahem.js"></script>
+<html>
+  <head>
+    <title>presentation attributes with MathML disabled</title>
+    <meta charset="utf-8"/>
+    <style>
+      math {
+          font: 25px/1 Ahem;
+      }
+      math, mtext {
+          display: inline-block;
+      }
+    </style>
+  </head>
+  <body>
+    <p>Presentation attributes should have no effects when MathML is disabled:</p>
+    <math dir="rtl"><mtext>X</mtext><mtext>p</mtext></math>
+    <math><mtext mathsize="200%">X</mtext></math>
+    <math mathbackground="red"><mtext>p</mtext></math>
+    <math mathcolor="red"><mtext>p</mtext></math>
+    <math style="text-transform: lowercase"><mtext mathvariant="normal">P</mtext></math>
+    <math><mtext style="font-size: math" scriptlevel="+1">X</mtext></math>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
index b63df77..3631e54 100644
--- a/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
+++ b/third_party/blink/web_tests/virtual/partitioned-cookies-first-party-sets/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
@@ -210,14 +210,14 @@
 
 Running test: getPartitionedCookie
 Num of cookies 2
-name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None
-name: __Host-foo, value: bar, domain: devtools.test, path: /, secure, session
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://devtools.test, secure, session, None
+name: __Host-foo, value: bar, domain: devtools.test, path: /, partitionKey: https://devtools.test, secure, session
 
 Running test: deleteAllCookies
 
 Running test: getPartitionedCookieFromOpaqueOrigin
 Num of cookies 1
-name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: <opaque>, secure, session, None
 
 Running test: deleteAllCookies
 
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt
index 2216b08..058e7fc 100644
--- a/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/partitioned-cookies/external/wpt/cookies/partitioned-cookies/partitioned-cookies.tentative.https-expected.txt
@@ -4,7 +4,7 @@
 PASS Partitioned cookies accessible on the top-level site they are created in via CookieStore
 PASS Cross-site window opened correctly
 PASS Partitioned cookies are not accessible on a different top-level site via HTTP
-FAIL Partitioned cookies are not accessible on a different top-level site via DOM assert_equals: Expected __Host-pchttp to not be available on a different top-level site expected false but got true
-FAIL Partitioned cookies are not accessible on a different top-level site via CookieStore assert_equals: Expected __Host-pchttp to not be available on a different top-level site expected false but got true
+PASS Partitioned cookies are not accessible on a different top-level site via DOM
+PASS Partitioned cookies are not accessible on a different top-level site via CookieStore
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt
index 5021f0f..5e23b5e 100644
--- a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/cookies/partitioned-cookies/clear-site-data.https-expected.txt
@@ -1,6 +1,9 @@
 CONSOLE MESSAGE: Clear-Site-Data header on 'https://cookies.not-example.test:8443/cookies/resources/clear-site-data.php': Cleared data types: "cookies". Clearing channel IDs and HTTP authentication cache is currently not supported, as it breaks active network connections.
+CONSOLE MESSAGE: Clear-Site-Data header on 'https://cookies.not-example.test:8443/cookies/resources/clear-site-data.php': Cleared data types: "cookies". Clearing channel IDs and HTTP authentication cache is currently not supported, as it breaks active network connections.
 This is a testharness.js-based test.
 PASS Clearing partitioned cookies
-FAIL Clearing partitioned cookies in another partition assert_equals: expected "" but got "__Host-pc=0"
+PASS Clearing partitioned cookies in another partition
+PASS Opened page from first partition
+PASS Clear-Site-Data does not leak across partitions
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
index 8939b78..199c2b5f 100644
--- a/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
+++ b/third_party/blink/web_tests/virtual/partitioned-cookies/http/tests/inspector-protocol/network/cookies-protocol-test-expected.txt
@@ -209,15 +209,14 @@
 name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://notinset.test, secure, session
 
 Running test: getPartitionedCookie
-Num of cookies 2
-name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None
-name: __Host-foo, value: bar, domain: devtools.test, path: /, secure, session
+Num of cookies 1
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: https://devtools.test, secure, session, None
 
 Running test: deleteAllCookies
 
 Running test: getPartitionedCookieFromOpaqueOrigin
 Num of cookies 1
-name: __Host-foo, value: bar, domain: example.test, path: /, secure, session, None
+name: __Host-foo, value: bar, domain: example.test, path: /, partitionKey: <opaque>, secure, session, None
 
 Running test: deleteAllCookies
 
diff --git a/third_party/blink/web_tests/virtual/task-tracking/README.md b/third_party/blink/web_tests/virtual/task-tracking/README.md
new file mode 100644
index 0000000..5c053acd
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/task-tracking/README.md
@@ -0,0 +1 @@
+A virtual test suite testing task tracking using a non-web-exposed API (enabled by a flag).
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-plan-b-is-not-supported-expected.txt
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/resources/iframe.html b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/iframe.html
new file mode 100644
index 0000000..dc546f33
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/resources/iframe.html
@@ -0,0 +1,6 @@
+<script>
+window.onmessage = e => {
+  const port = e.ports[0];
+  port.postMessage("Got the port");
+};
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html
new file mode 100644
index 0000000..c7ab6bc
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-posttask.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that setTimeout tasks can be properly tracked.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+promise_test(async t => {
+  const scriptId = scheduler.taskId;
+
+  await scheduler.postTask(async () => {
+    // New task; ID should have changed.
+    const taskId1 = scheduler.taskId;
+    let taskId2;
+    assert_not_equals(scriptId, taskId1);
+    assert_equals(scheduler.isAncestor(scriptId), "ancestor");
+
+    await scheduler.postTask(() => {
+      // New task; IDs should have changed again.
+      taskId2 = scheduler.taskId;
+      assert_not_equals(taskId1, taskId2);
+      assert_equals(scheduler.isAncestor(scriptId), "ancestor");
+      assert_equals(scheduler.isAncestor(taskId1), "ancestor");
+    });
+
+    assert_equals(scheduler.isAncestor(taskId1), "ancestor");
+    assert_equals(scheduler.isAncestor(taskId2), "not ancestor");
+  });
+}, "postTask chain.");
+
+promise_test(async () => {
+    const initialId = scheduler.taskId;
+    await scheduler.postTask(async () => {
+      const currentTaskId = scheduler.taskId;
+      assert_equals(scheduler.isAncestor(initialId), "ancestor",
+                    "initialId is an ancestor of the currentTaskId");
+      await fetch("/resources/blank.html");
+      assert_equals(scheduler.isAncestor(initialId), "ancestor",
+                    "initialId is ancestor of fetch continuation.");
+      assert_equals(scheduler.isAncestor(currentTaskId), "ancestor",
+                    "currentTaskId is an ancestor of fetch continuation");
+    });
+}, "Single postTask with multiple internal awaited tasks");
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html
new file mode 100644
index 0000000..01a8a89
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-promise.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that promise tasks can be properly tracked.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    await new Promise(r => setTimeout(r, 0));
+    queueMicrotask(() => {
+      try {
+          assert_equals(scheduler.isAncestor(initialId), "ancestor");
+          resolve();
+        } catch {
+          reject("Queued microtask is not a descendant");
+        }
+    });
+  });
+}, "A microtask is a descendant of the dispatching task");
+
+promise_test(() => {
+  return new Promise((resolve, reject) => {
+    const initialId = scheduler.taskId;
+    fetch("/resources/blank.html")
+    .then(response => response.text())
+    .then(body => {
+      return new Promise(interimResolve => {
+        interimResolve();
+      });
+    })
+    .then(() => {
+      try {
+        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        resolve();
+      } catch {
+        reject();
+      }
+    });
+  });
+}, "A paint task after fetching a resource using promises is a " +
+   "descendant of initiating task.");
+
+promise_test(() => {
+  return new Promise((resolve, reject) => {
+    const initialId = scheduler.taskId;
+    fetch("/resources/blank.html")
+    .then(response => response.text())
+    .then(body => {
+      return new Promise(interimResolve => {
+        interimResolve();
+      });
+    })
+    .then(() => {
+      return new Promise(interimResolve => setTimeout(interimResolve, 0));
+    })
+    .then(() => {
+      try {
+        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        resolve();
+      } catch {
+        reject();
+      }
+    });
+  });
+}, "A paint task after fetching a resource using promises, including a JS " +
+   "created promise with a timeout, is a descendant of initiating task.");
+
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    const response = await fetch("/resources/blank.html");
+    const body = await response.text();
+    try {
+      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      resolve();
+    } catch {
+      reject();
+    }
+  });
+}, "A paint task after fetching a resource using async await is a descendant " +
+   "of initiating task.");
+
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    let promiseId;
+    await new Promise(r => setTimeout(r, 10));
+    try {
+      await new Promise((r, rj) => {
+        promiseId = scheduler.taskId;
+        rj();
+      });
+    } catch {
+      // Carry on. Rejection is expected. The important thing is not to let it
+      // get to you.
+    }
+    try {
+      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+    } catch {
+      reject("InitialId is not an ancestor");
+    }
+    try {
+      assert_equals(scheduler.isAncestor(promiseId), "ancestor");
+      resolve();
+    } catch {
+      reject("PromiseId is not an ancestor");
+    }
+  });
+}, "Rejected promises properly handle ancestry.");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html
new file mode 100644
index 0000000..3d8df75
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-setinterval.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that setInterval tasks can be properly tracked.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    await new Promise(resolve => setInterval(resolve, 0));
+    queueMicrotask(() => {
+      try {
+        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        resolve();
+      } catch {
+        reject("Not an ancestor");
+      }
+    });
+  });
+}, "An immediate setInterval microtask is a descendant of the dispatching task");
+
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    await new Promise(resolve => setInterval(resolve, 100));
+    queueMicrotask(() => {
+      try {
+        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        resolve();
+      } catch {
+        reject("Not an ancestor");
+      }
+    });
+  });
+}, "A long setInterval microtask is a descendant of the dispatching task");
+
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    let counter = 0;
+    await new Promise(resolve => setInterval(() => {
+      ++counter;
+      if (counter > 3) {
+        resolve();
+      }
+    }, 100));
+    queueMicrotask(() => {
+      try {
+        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        resolve();
+      } catch {
+        reject("Not an ancestor");
+      }
+    });
+  });
+}, "A long setInterval that runs multiple times followed by a microtask is a descendant of the dispatching task");
+
+</script>
+</body>
+</html>
+
+
diff --git a/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html b/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html
new file mode 100644
index 0000000..e9681cd
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/task-tracking/track-settimeout.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that setTimeout tasks can be properly tracked.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    await new Promise(resolve => setTimeout(resolve, 0));
+    queueMicrotask(() => {
+      try {
+        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        resolve();
+      } catch {
+        reject("Not an ancestor");
+      }
+    });
+  });
+}, "An immediate setTimeout microtask is a descendant of the dispatching task");
+
+promise_test(() => {
+  return new Promise(async (resolve, reject) => {
+    const initialId = scheduler.taskId;
+    await new Promise(resolve => setTimeout(resolve, 100));
+    queueMicrotask(() => {
+      try {
+        assert_equals(scheduler.isAncestor(initialId), "ancestor");
+        resolve();
+      } catch {
+        reject("Not an ancestor");
+      }
+    });
+  });
+}, "A long setTimeout microtask is a descendant of the dispatching task");
+
+promise_test(async () => {
+  let siblingId;
+  const initialId = scheduler.taskId;
+  await new Promise((resolve, reject) => setTimeout(() => {
+    try {
+      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      resolve();
+    } catch {
+      reject("Initial task not identified as ancestor of first setTimeout");
+    }
+    resolve();
+  }, 10));
+  await new Promise((resolve, reject) => setTimeout(() => {
+    try {
+      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      resolve();
+    } catch {
+      reject("Initial task not identified as ancestor of second setTimeout");
+    }
+  }, 10));
+}, "An async chain of setTimeouts task properly track their ancestors");
+
+promise_test(async () => {
+  const initialId = scheduler.taskId;
+  return new Promise((resolve, reject) => {
+    try {
+      assert_equals(scheduler.isAncestor(initialId), "ancestor");
+      resolve();
+    } catch {
+      reject("The task is not its own ancestor");
+    }
+  });
+}, "A task is its own ancestor");
+
+</script>
+</body>
+</html>
+
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 8d1461d..7bb9882 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -22,6 +22,7 @@
   TESTING: 'testing',
   SMB: 'smb',
   SYSTEM_INTERNAL: 'system_internal',
+  GUEST_OS: 'guest_os',
 };
 
 /** @enum {string} */
diff --git a/third_party/dav1d/version/vcs_version.h b/third_party/dav1d/version/vcs_version.h
index 88548ec..2f1a458 100644
--- a/third_party/dav1d/version/vcs_version.h
+++ b/third_party/dav1d/version/vcs_version.h
@@ -1,2 +1,2 @@
 /* auto-generated, do not edit */
-#define DAV1D_VERSION "0.9.2-172-gb1a5189"
+#define DAV1D_VERSION "0.9.2-174-g56e7ffc"
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index d95df12..c4005e8 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-11-1-117-g1e2eb6504
-Revision: 1e2eb65048f75c64b68708efed6ce904c31f3b2f
+Version: VER-2-11-1-118-g53dfdcd81
+Revision: 53dfdcd8198d2b3201a23c4bad9190519ba918db
 CPEPrefix: cpe:/a:freetype:freetype:2.11.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index 0ba8944..e7db28e 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -209,6 +209,7 @@
     ":platform_base_cancellation_flag",
     ":platform_base_error_code_recorder",
     ":platform_base_util",
+    ":platform_impl_shared_file",
     ":platform_public_comm",
     ":platform_public_logging",
     ":platform_public_types",
@@ -458,6 +459,7 @@
     "src/internal/platform/input_stream.h",
     "src/internal/platform/listeners.h",
     "src/internal/platform/nsd_service_info.h",
+    "src/internal/platform/os_name.h",
     "src/internal/platform/output_stream.h",
     "src/internal/platform/payload_id.h",
     "src/internal/platform/prng.h",
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index 8cc4678..25b548c 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby-connections
-Version: 31b2aae6c5442c55d819e7a966b8dd996a0edcfa
+Version: 517d77f5aa4fea8f4437125830cfc55f84705e3d
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/rust/autocxx/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx/v0_16/crate/.cargo_vcs_info.json
deleted file mode 100644
index 6a150cc8..0000000
--- a/third_party/rust/autocxx/v0_16/crate/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74"
-  },
-  "path_in_vcs": ""
-}
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/clippy.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/clippy.yml
deleted file mode 100644
index b5fd94d..0000000
--- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/clippy.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: Clippy
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
-jobs:
-  clippy_check:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v1
-      - uses: actions-rs/toolchain@v1
-        with:
-            toolchain: nightly
-            components: clippy
-            override: true
-      - uses: actions-rs/clippy-check@v1
-        with:
-          token: ${{ secrets.GITHUB_TOKEN }}
-          args: --all-features --tests
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/examples.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/examples.yml
deleted file mode 100644
index 23f8d51..0000000
--- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/examples.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: Examples
-
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
-
-env:
-  CARGO_TERM_COLOR: always
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - name: Checkout with submodules
-      uses: actions/checkout@v2
-      with:
-        submodules: recursive
-    - name: Build s2 example
-      working-directory: ./examples/s2
-      run: cargo build --all --verbose
-    - name: Build steam example
-      working-directory: ./examples/steam-mini
-      run: cargo build --all --verbose
-    - name: Build subclass example
-      working-directory: ./examples/subclass
-      run: cargo build --all --verbose
-    - name: Build pod example
-      working-directory: ./examples/pod
-      run: cargo build --all --verbose
-    - name: Build chromium render-frame-host example
-      working-directory: ./examples/chromium-fake-render-frame-host
-      run: cargo build --all --verbose
-    - name: Build non-trivial-type-on-stack example
-      working-directory: ./examples/non-trivial-type-on-stack
-      run: cargo build --all --verbose
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rust.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/rust.yml
deleted file mode 100644
index 1435c7a..0000000
--- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rust.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: Rust
-
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
-
-env:
-  CARGO_TERM_COLOR: always
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Build
-      run: cargo build --all --verbose
-    - name: Install creduce
-      run: sudo apt-get install creduce
-    - name: Run tests
-      run: cargo test --all --verbose
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rustfmt.yml b/third_party/rust/autocxx/v0_16/crate/.github/workflows/rustfmt.yml
deleted file mode 100644
index 7b5a9f4..0000000
--- a/third_party/rust/autocxx/v0_16/crate/.github/workflows/rustfmt.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: Rustfmt
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
-jobs:
-  format:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - uses: actions-rs/toolchain@v1
-        with:
-            toolchain: stable
-            components: rustfmt
-            override: true
-      - uses: mbrobbel/rustfmt-check@master
-        with:
-          token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/third_party/rust/autocxx/v0_16/crate/.gitignore b/third_party/rust/autocxx/v0_16/crate/.gitignore
deleted file mode 100644
index fa8d85ac..0000000
--- a/third_party/rust/autocxx/v0_16/crate/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-Cargo.lock
-target
diff --git a/third_party/rust/autocxx/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx/v0_16/crate/Cargo.toml.orig
deleted file mode 100644
index d0113e6..0000000
--- a/third_party/rust/autocxx/v0_16/crate/Cargo.toml.orig
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-[package]
-name = "autocxx"
-version = "0.16.0"
-authors = ["Adrian Taylor <adetaylor@chromium.org>"]
-license = "MIT OR Apache-2.0"
-description = "Safe autogenerated interop between Rust and C++"
-repository = "https://github.com/google/autocxx"
-edition = "2021"
-keywords = ["ffi"]
-categories = ["development-tools::ffi", "api-bindings"]
-
-# This is stricter about avoiding optional features in dependencies
-# such as 'syn'. We build this way such that CI spots any mistakes
-# where we are depending on such features.
-resolver = "2"
-
-[dependencies]
-autocxx-macro = { path="macro", version="0.16.0" }
-autocxx-engine = { path="engine", version="0.16.0" } # so that
-  # we can refer to autocxx_engine::cxx. But even that isn't sufficient...
-cxx = "1.0.54" # ... also needed because expansion of type_id refers to ::cxx
-aquamarine = "0.1" # docs
-moveit = { version = "0.4", features = [ "cxx" ] }
-
-[workspace]
-members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "integration-tests"]
-exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack"]
-
-#[patch.crates-io]
-#cxx = { path="../cxx" }
-#cxx-gen = { path="../cxx/gen/lib" }
-#autocxx-bindgen = { path="../bindgen" }
diff --git a/third_party/rust/autocxx/v0_16/crate/README.md b/third_party/rust/autocxx/v0_16/crate/README.md
deleted file mode 100644
index 7073473..0000000
--- a/third_party/rust/autocxx/v0_16/crate/README.md
+++ /dev/null
@@ -1,195 +0,0 @@
-# Autocxx
-
-[![GitHub](https://img.shields.io/crates/l/autocxx)](https://github.com/google/autocxx)
-[![crates.io](https://img.shields.io/crates/d/autocxx)](https://crates.io/crates/autocxx)
-[![docs.rs](https://docs.rs/autocxx/badge.svg)](https://docs.rs/autocxx)
-
-This project is a tool for calling C++ from Rust in a heavily automated, but safe, fashion.
-
-The intention is that it has all the fluent safety from [cxx](https://cxx.rs) whilst generating interfaces automatically from existing C++ headers using a variant of [bindgen](https://docs.rs/bindgen/latest/bindgen/). Think of autocxx as glue which plugs bindgen into cxx.
-
-# Overview
-
-```rust,ignore
-autocxx::include_cpp! {
-    #include "url/origin.h"
-    generate!("url::Origin")
-    safety!(unsafe_ffi)
-}
-
-fn main() {
-    let o = ffi::url::Origin::CreateFromNormalizedTuple("https",
-        "google.com", 443);
-    let uri = o.Serialize();
-    println!("URI is {}", uri.to_str().unwrap());
-}
-```
-
-# Getting started
-
-If you're here, you want to call some C++ from Rust, right?
-
-You will need:
-
-* Some C++ header files (`.h` files)
-* The C++ "include path". That is, the set of directories containing those headers. (That's not necessarily the directory in which each header _file_ lives; C++ might contain `#include "foo/bar.h"` and so your include path would need to include the directory containing the `foo` directory).
-* A list of the APIs (types and functions) from those header files which you wish to make available in Rust.
-* Either a Cargo or non-Cargo build system.
-* To know how to link the C++ libraries into your Cargo project. This is beyond the scope of what `autocxx` helps with, but one solution is to emit a print from your [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib).
-* [LLVM to be installed](https://rust-lang.github.io/rust-bindgen/requirements.html).
-* Some patience. This is not a magic solution. C++/Rust interop is hard. Avoid it if you can!
-
-The rest of this 'getting started' section assumes Cargo - if you're using something else, see the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) documentation.
-
-First, add `autocxx` and `cxx` to your `dependencies` and `autocxx-build` to your `build-dependencies` in your `Cargo.toml`.
-
-```toml
-[dependencies]
-autocxx = "0.16.0"
-cxx = "1.0"
-
-[build-dependencies]
-autocxx-build = "0.16.0"
-```
-
-Now, add a `build.rs`. This is where you need your include path:
-
-```rust,ignore
-fn main() {
-    let path = std::path::PathBuf::from("src"); // include path
-    let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).expect_build();
-        // This assumes all your C++ bindings are in main.rs
-    b.flag_if_supported("-std=c++14").compile("autocxx-demo");
-    println!("cargo:rerun-if-changed=src/main.rs");
-    // Add instructions to link to any C++ libraries you need.
-}
-```
-
-Finally, in your `main.rs` you can use the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) macro which is the heart of `autocxx`:
-
-```rust,ignore
-use autocxx::prelude::*;
-
-include_cpp! {
-    #include "my_header.h" // your header file name
-    safety!(unsafe) // see details of unsafety policies described in include_cpp
-    generate!("MyAPIFunction") // add this line for each function or type you wish to generate
-}
-```
-
-You should then find you can call the function by referring to an `ffi` namespace:
-
-```rust,ignore
-fn main() {
-    println!("Hello, world! - answer from C++ is {}", ffi::MyAPIFunction(4));
-}
-```
-
-C++ types such as `std::string` and `std::unique_ptr` are represented using the types provided by the marvellous [cxx](https://cxx.rs) library. This provides good ergonomics and safety norms, so unlike with normal `bindgen` bindings, you won't _normally_ need to write `unsafe` code for every function call.
-
-Some caveats:
-
-* Not all C++ features are supported. You _will_ come across APIs - possibly many APIs - where autocxx doesn't work. It should emit reasonable diagnostics explaining the problem. See the section on "dealing with failure" in the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) documentation.
-* `autocxx` can be frustrating when you run up against its limitations. It's designed to allow importing of APIs from complex existing codebases. It's often a better choice to use [cxx](https://cxx.rs) directly.
-
-A full user manual can be found in the documentation for [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html). See [demo/src/main.rs](demo/src/main.rs) for a basic example, and the [examples](examples/) directory for more.
-
-# On safety
-
-This crate mostly intends to follow the lead of the `cxx` crate in where and when `unsafe` is required. But, this crate is opinionated. It believes some unsafety requires more careful review than other bits, along the following spectrum:
-
-* Rust unsafe code (requires most review)
-* Rust code calling C++ with raw pointers
-* Rust code calling C++ with shared pointers, or anything else where there can be concurrent mutation
-* Rust code calling C++ with unique pointers, where the Rust single-owner model nearly always applies (but we can't _prove_ that the C++ developer isn't doing something weird)
-* Rust safe code (requires least review)
-
-If your project is 90% Rust code, with small bits of C++, don't use this crate. You need something where all C++ interaction is marked with big red "this is terrifying" flags. This crate is aimed at cases where there's 90% C++ and small bits of Rust, and so we want the Rust code to be pragmatically reviewable without the signal:noise ratio of `unsafe` in the Rust code becoming so bad that `unsafe` loses all value.
-
-See [safety!] in the documentation for more details.
-
-# Building without cargo
-
-See instructions in the documentation for [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html). This interop inevitably involves lots of fiddly small functions. It's likely to perform far better if you can achieve cross-language LTO. [This issue](https://github.com/dtolnay/cxx/issues/371) may give some useful hints - see also all the build-related help in [the cxx manual](https://cxx.rs/) which all applies here too.
-
-# Directory structure
-
-* `demo` - a very simple demo example
-* `examples` - will gradually fill with more complex examples
-* `parser` - code which parses a single `include_cpp!` macro. Used by both the macro
-  (which doesn't do much) and the code generator (which does much more, by means of
-  `engine` below)
-* `engine` - all the core code for actual code generation.
-* `macro` - the procedural macro which expands the Rust code.
-* `gen/build` - a library to be used from `build.rs` scripts to generate .cc and .h
-  files from an `include_cxx` section.
-* `gen/cmd` - a command-line tool which does the same.
-* `src` (outermost project) - a wrapper crate which imports the procedural macro and
-  a few other things.
-
-# Where to start reading
-
-The main algorithm is in `engine/src/lib.rs`, in the function `generate()`. This asks
-`bindgen` to generate a heap of Rust code and then passes it into
-`engine/src/conversion` to convert it to be a format suitable for input
-to `cxx`.
-
-However, most of the actual code is in `engine/src/conversion/mod.rs`.
-
-At the moment we're using a slightly branched version of `bindgen` called `autocxx-bindgen`.
-It's hoped this is temporary; some of our changes are sufficiently weird that it would be
-presumptious to try to get them accepted upstream until we're sure `autocxx` has roughly the right approach.
-
-# How to develop
-
-If you're making a change, here's what you need to do to get useful diagnostics etc.
-First of all, `cargo run` in the `demo` directory. If it breaks, you don't get much
-in the way of useful diagnostics, because `stdout` is swallowed by cargo build scripts.
-So, practically speaking, you would almost always move onto running one of the tests
-in the test suite. With suitable options, you can get plenty of output. For instance:
-
-```ignore
-RUST_BACKTRACE=1 RUST_LOG=autocxx_engine=info cargo test --all test_cycle_string_full_pipeline -- --nocapture
-```
-
-This is especially valuable to see the `bindgen` output Rust code, and then the converted Rust code which we pass into cxx. Usually, most problems are due to some mis-conversion somewhere
-in `engine/src/conversion`. See [here](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.IncludeCppEngine.html) for documentation and diagrams on how the engine works.
-
-# Reporting bugs
-
-If you've found a problem, and you're reading this, *thank you*! Your diligence
-in reporting the bug is much appreciated and will make `autocxx` better. In
-order of preference here's how we would like to hear about your problem:
-
-* Raise a pull request adding a new failing integration test to
-  `engine/src/integration_tests.rs`.
-* Minimize the test using `tools/reduce`, something like this:
-  `target/debug/autocxx-reduce file -d "safety!(unsafe_ffi)" -d
-  'generate_pod!("A")' -I ~/my-include-dir -h my-header.h -p
-  problem-error-message -- --remove-pass pass_line_markers`
-  This is a wrapper for the amazing `creduce` which will take thousands of lines
-  of C++, preprocess it, and then identify the minimum required lines to
-  reproduce the same problem.
-* Use the C++ preprocessor to give a single complete C++ file which demonstrates
-  the problem, along with the `include_cpp!` directive you use.
-  Alternatively, run your build using `AUTOCXX_REPRO_CASE=repro.json` which should
-  put everything we need into `output.h`. If necessary, you can use the `CLANG_PATH`
-  or `CXX` environment variables to specify the path to the Clang compiler to use.
-* Failing all else, build using
-  `cargo clean -p <your package name> && RUST_LOG=autocxx_engine=info cargo build -vvv`
-  and send the _entire_ log to us. This will include two key bits of logging:
-  the C++ bindings as distilled by `bindgen`, and then the version which
-  we've converted and moulded to be suitable for use by `cxx`.
-
-# Credits
-
-David Tolnay did much of the hard work here, by inventing the underlying cxx crate, and in fact nearly all of the parsing infrastructure on which this crate depends. `bindgen` is also awesome. This crate stands on the shoulders of giants!
-
-#### License and usage notes
-
-This is not an officially supported Google product.
-
-<sup>
-Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
-2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
-</sup>
diff --git a/third_party/rust/autocxx/v0_16/crate/docs/contributing.md b/third_party/rust/autocxx/v0_16/crate/docs/contributing.md
deleted file mode 100644
index 654a071..0000000
--- a/third_party/rust/autocxx/v0_16/crate/docs/contributing.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# How to Contribute
-
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution;
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to <https://cla.developers.google.com/> to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Code reviews
-
-All submissions, including submissions by project members, require review. We
-use GitHub pull requests for this purpose. Consult
-[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
-information on using pull requests.
-
-## Community Guidelines
-
-This project follows [Google's Open Source Community
-Guidelines](https://opensource.google/conduct/).
diff --git a/third_party/rust/autocxx/v0_16/crate/src/lib.rs b/third_party/rust/autocxx/v0_16/crate/src/lib.rs
deleted file mode 100644
index 6537ba6d..0000000
--- a/third_party/rust/autocxx/v0_16/crate/src/lib.rs
+++ /dev/null
@@ -1,837 +0,0 @@
-#![doc = include_str!("../README.md")]
-
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The crazy macro_rules magic in this file is thanks to dtolnay@
-// and is a way of attaching rustdoc to each of the possible directives
-// within the include_cpp outer macro. None of the directives actually
-// do anything - all the magic is handled entirely by
-// autocxx_macro::include_cpp_impl.
-
-pub mod subclass;
-
-#[allow(unused_imports)] // doc cross-reference only
-use autocxx_engine::IncludeCppEngine;
-
-#[cfg_attr(doc, aquamarine::aquamarine)]
-/// Include some C++ headers in your Rust project.
-///
-/// This macro allows you to include one or more C++ headers within
-/// your Rust code, and call their functions fairly naturally.
-///
-/// # Examples
-///
-/// C++ header (`input.h`):
-/// ```cpp
-/// #include <cstdint>
-///
-/// uint32_t do_math(uint32_t a);
-/// ```
-///
-/// Rust code:
-/// ```
-/// # use autocxx_macro::include_cpp_impl as include_cpp;
-/// include_cpp!(
-/// #   parse_only!()
-///     #include "input.h"
-///     generate!("do_math")
-///     safety!(unsafe)
-/// );
-///
-/// # mod ffi { pub fn do_math(a: u32) -> u32 { a+3 } }
-/// # fn main() {
-/// ffi::do_math(3);
-/// # }
-/// ```
-///
-/// The resulting bindings will use idiomatic Rust wrappers for types from the [cxx]
-/// crate, for example [cxx::UniquePtr] or [cxx::CxxString]. Due to the care and thought
-/// that's gone into the [cxx] crate, such bindings are pleasant and idiomatic to use
-/// from Rust, and usually don't require the `unsafe` keyword.
-///
-/// # User manual - introduction
-///
-/// [`include_cpp`] tries to make it possible to include C++ headers and use declared functions
-/// and types as-is. The resulting bindings use wrappers for C++ STL types from the [cxx]
-/// crate such as [cxx::UniquePtr] or [cxx::CxxString].
-///
-/// Why, then, do you need a manual? Three reasons:
-///
-/// * This manual will describe how to include `autocxx` in your build process.
-/// * `autocxx` chooses to generate Rust bindings for C++ APIs in particular ways,
-///   over which you have _some_ control. The manual discusses what and how.
-/// * The combination of `autocxx` and [`cxx`] are not perfect. There are some STL
-///   types and some fundamental C++ features which are not yet supported. Where that occurs,
-///   you may need to create some manual bindings or otherwise workaround deficiencies.
-///   This manual tells you how to spot such circumstances and work around them.
-///
-/// # Overview
-///
-/// Here's how to approach autocxx:
-///
-/// ```mermaid
-/// flowchart TB
-///     %%{init:{'flowchart':{'nodeSpacing': 60, 'rankSpacing': 30}}}%%
-///     autocxx[Add a dependency on autocxx in your project]
-///     which-build([Do you use cargo?])
-///     autocxx--->which-build
-///     autocxx-build[Add a dev dependency on autocxx-build]
-///     build-rs[In your build.rs, tell autocxx-build about your header include path]
-///     autocxx-build--->build-rs
-///     which-build-- Yes -->autocxx-build
-///     macro[Add include_cpp! macro: list headers and allowlist]
-///     build-rs--->macro
-///     autocxx-gen[Use autocxx-gen command line tool]
-///     which-build-- No -->autocxx-gen
-///     autocxx-gen--->macro
-///     build[Build]
-///     macro--->build
-///     check[Confirm generation using cargo expand]
-///     build--->check
-///     manual[Add manual cxx::bridge for anything missing]
-///     check--->manual
-///     use[Use generated ffi mod APIs]
-///     manual--->use
-/// ```
-///
-/// # Configuring the build - if you're using cargo
-///
-/// You'll use the `autocxx-build` crate. Simply copy from the
-/// [demo example](https://github.com/google/autocxx/blob/main/demo/build.rs).
-/// You'll need to provide it:
-/// * The list of `.rs` files which will have `include_cpp!` macros present
-/// * Your C++ header include path.
-///
-/// # Configuring the build - if you're not using cargo
-///
-/// See the `autocxx-gen` crate. You'll need to:
-///
-/// * Run the `codegen` phase. You'll need to use the [autocxx-gen]
-///   tool to process the .rs code into C++ header and
-///   implementation files. This will also generate `.rs` side bindings.
-/// * Educate the procedural macro about where to find the generated `.rs` bindings. Set the
-///   `AUTOCXX_RS` environment variable to a list of directories to search.
-///   If you use `autocxx-build`, this happens automatically. (You can alternatively
-///   specify `AUTOCXX_RS_FILE` to give a precise filename as opposed to a directory to search,
-///   though this isn't recommended unless your build system specifically requires it
-///   because it allows only a single `include_cpp!` block per `.rs` file.)
-///
-/// ```mermaid
-/// flowchart TB
-///     s(Rust source with include_cpp!)
-///     c(Existing C++ headers)
-///     cg(autocxx-gen or autocxx-build)
-///     genrs(Generated .rs file)
-///     gencpp(Generated .cpp and .h files)
-///     rsb(Rust/Cargo build)
-///     cppb(C++ build)
-///     l(Linker)
-///     s --> cg
-///     c --> cg
-///     cg --> genrs
-///     cg --> gencpp
-///     m(autocxx-macro)
-///     s --> m
-///     genrs-. included .->m
-///     m --> rsb
-///     gencpp --> cppb
-///     cppb --> l
-///     rsb --> l
-/// ```
-///
-/// # The [`include_cpp`] macro
-///
-/// Within the braces of the `include_cpp!{...}` macro, you should provide
-/// a list of at least the following:
-///
-/// * `#include "cpp_header.h"`: a header filename to parse and include
-/// * `generate!("type_or_function_name")`: a type or function name whose declaration
-///   should be made available to C++. (See the section on Allowlisting, below).
-/// * Optionally, `safety!(unsafe)` - see discussion of [`safety`].
-///
-/// Other directives are possible as documented in this crate.
-///
-/// Now, try to build your Rust project. `autocxx` may fail to generate bindings
-/// for some of the items you specified with [generate] directives: remove
-/// those directives for now, then see the next section for advice.
-///
-/// # Did it work? How do I deal with failure?
-///
-/// Once you've achieved a successful build, you might wonder how to know what
-/// bindings have been generated. `cargo expand` will show you. Alternatively,
-/// you can get autocompletion within an IDE supported by Rust analyzer. You'll
-/// need to enable _both_:
-/// * Rust-analyzer: Proc Macro: Enable
-/// * Rust-analyzer: Experimental: Proc Attr Macros
-///
-/// Either way, you'll find (for sure!) that `autocxx` hasn't been able to generate
-/// bindings for all your C++ APIs. This may manifest as a hard failure or a soft
-/// failure:
-/// * If you specified such an item in a [`generate`] directive (or similar such
-///   as [`generate_pod`]) then your build will fail.
-/// * If such APIs are methods belonging to a type, `autocxx` will generate other
-///   methods for the type but ignore those.
-///
-/// In this latter case, you should see helpful messages _in the generated bindings_
-/// as rust documentation explaining what went wrong.
-///
-/// If this happens (and it will!) your options are:
-/// * Add more, simpler C++ APIs which fulfil the same need but are compatible with
-///   `autocxx`.
-/// * Write manual bindings. This is most useful if a type is supported by [cxx]
-///   but not `autocxx` (for example, at the time of writing `std::array`). See
-///   the later section on 'combinining automatic and manual bindings'.
-///
-/// # Allowlisting
-///
-/// How do you inform autocxx which bindings to generate? There are three
-/// strategies:
-///
-/// * *Recommended*: provide various [`generate`] directives in the
-///   [`include_cpp`] macro. This can specify functions or types.
-/// * *Not recommended*: in your `build.rs`, call [`Builder::auto_allowlist`].
-///   This will attempt to spot _uses_ of FFI bindings anywhere in your Rust code
-///   and build the allowlist that way. This is experimental and has known limitations.
-/// * *Strongly not recommended*: use [`generate_all`]. This will attempt to
-///   generate Rust bindings for _any_ C++ type or function discovered in the
-///   header files. This is generally a disaster if you're including any
-///   remotely complex header file: we'll try to generate bindings for all sorts
-///   of STL types. This will be slow, and some may well cause problems.
-///   Effectively this is just a debug option to discover such problems. Don't
-///   use it!
-///
-/// # The generated bindings
-///
-/// ## Pointers, references, and so-forth
-///
-/// `autocxx` knows how to deal with C++ APIs which take C++ types:
-/// * By value
-/// * By reference (const or not)
-/// * By raw pointer
-/// * By `std::unique_ptr`
-/// * By `std::shared_ptr`
-/// * By `std::weak_ptr`
-///
-/// (all of this is because the underlying [`cxx`] crate has such versatility).
-/// Some of these have some quirks in the way they're exposed in Rust, described below.
-///
-/// ### Passing between C++ and Rust by value
-///
-/// Rust is free to move data around at any time. That's _not OK_ for some C++ types
-/// which have non-trivial move constructors or destructors. Such types are common
-/// in C++ (for example, even C++ `std::string`s) and these types commonly appear
-/// in API declarations which we want to make available in Rust. Worse still, Rust
-/// has no visibility into whether a C++ type meets these criteria. What do we do?
-///
-/// You have a choice:
-/// * As standard, any C++ type passed by value will be `std::move`d on the C++ side
-///   into a `std::unique_ptr` before being passed to Rust, and similarly moved out
-///   of a `std::unique_ptr` when passed from Rust to C++.
-/// * If you know that your C++ type can be safely byte-copied, then you can
-///   override this behavior by using [`generate_pod`] instead of [`generate`].
-///
-/// There's not a significant ergonomic problem from the use of [`cxx::UniquePtr`].
-/// The main negative of the automatic boxing into [`cxx::UniquePtr`] is performance:
-/// specifically, the need to
-/// allocate heap cells on the C++ side and move data into and out of them.
-/// You don't want to be doing this inside a tight loop (but if you're calling
-/// across the C++/Rust boundary in a tight loop, perhaps reconsider that boundary
-/// anyway).
-///
-/// If you want your type to be transferred between Rust and C++ truly _by value_
-/// then use [`generate_pod`] instead of [`generate`].
-///
-/// Specifically, to be compatible with [`generate_pod`], your C++ type must either:
-/// * Lack a move constructor _and_ lack a destructor
-/// * Or contain a human promise that it's relocatable, by implementing
-///   the C++ trait `IsRelocatable` per the instructions in
-///   [cxx.h](https://github.com/dtolnay/cxx/blob/master/include/cxx.h)
-///
-/// Otherwise, your build will fail.
-///
-/// This doesn't just make a difference to the generated code for the type;
-/// it also makes a difference to any functions which take or return that type.
-/// If there's a C++ function which takes a struct by value, but that struct
-/// is not declared as POD-safe, then we'll generate wrapper functions to move
-/// that type into and out of [`cxx::UniquePtr`]s.
-///
-/// There is one other option under construction. The `moveit` crate replicates
-/// C++ value move and copying semantics in Rust. There is limited early support
-/// for `moveit` within autocxx; specifically, you can call C++ constructors
-/// to emplace even non-trivial objects on the Rust stack using `moveit`, and
-/// then call methods on them or use references to them in other function calls.
-/// At present, you can't copy or move such objects - once they're created,
-/// that's it, until it's dropped. At present therefore such objects can't be
-/// passed into C++ functions which take non-POD types by value. This facility
-/// is therefore best avoided for now until it's more complete - but see
-/// `examples/non-trivial-type-on-stack` if you want to see how to use it.
-///
-/// ### References and pointers
-///
-/// We follow [cxx] norms here. Specifically:
-/// * A C++ reference becomes a Rust reference
-/// * A C++ pointer becomes a Rust pointer.
-/// * If a reference is returned with an ambiguous lifetime, we don't generate
-///   code for the function
-/// * Pointers require use of `unsafe`, references don't necessarily.
-///
-/// That last point is key. If your C++ API takes pointers, you're going
-/// to have to use `unsafe`. Similarly, if your C++ API returns a pointer,
-/// you'll have to use `unsafe` to do anything useful with the pointer in Rust.
-/// This is intentional: a pointer from C++ might be subject to concurrent
-/// mutation, or it might have a lifetime that could disappear at any moment.
-/// As a human, you must promise that you understand the constraints around
-/// use of that pointer and that's what the `unsafe` keyword is for.
-///
-/// Exactly the same issues apply to C++ references _in theory_, but in practice,
-/// they usually don't. Therefore [cxx] has taken the view that we can "trust"
-/// a C++ reference to a higher degree than a pointer, and autocxx follows that
-/// lead. In practice, of course, references are rarely return values from C++
-/// APIs so we rarely have to navel-gaze about the trustworthiness of a
-/// reference.
-///
-/// (See also the discussion of [`safety`] - if you haven't specified
-/// an unsafety policy, _all_ C++ APIs require `unsafe` so the discussion is moot.)
-///
-/// If you're given a C++ object by pointer, and you want to interact with it,
-/// you'll need to figure out the guarantees attached to the C++ object - most
-/// notably its lifetime. To see some of the decision making process involved
-/// see the [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini/src/main.rs).
-///
-/// ### [`cxx::UniquePtr`]s
-///
-/// We use [`cxx::UniquePtr`] in completely the normal way, but there are a few
-/// quirks which you're more likely to run into with `autocxx`.
-///
-/// * Calling methods: you may need to use [`cxx::UniquePtr::pin_mut`] to get
-///   a reference on which you can call a method.
-/// * Getting a raw pointer in order to pass to some pre-existing function:
-///   at present you need to do:
-///   ```rust,ignore
-///      let mut a = ffi::A::make_unique();
-///      unsafe { ffi::TakePointerToA(std::pin::Pin::<&mut ffi::A>::into_inner_unchecked(a.pin_mut())) };
-///   ```
-///   This may be simplified in future.
-///
-/// ## Construction
-///
-/// Types gain a `make_unique` associated function. At present they only
-/// gain this if they have an explicit C++ constructor; this is a limitation
-/// which should be resolved in future.
-/// This will (of course) return a [`cxx::UniquePtr`] containing that type.
-///
-/// ## Built-in types
-///
-/// The generated code uses `cxx` for interop: see that crate for many important
-/// considerations including safety and the list of built-in types, for example
-/// [`cxx::UniquePtr`] and [`cxx::CxxString`].
-///
-/// There are almost no `autocxx`-specific types. At present, we do have
-/// [`c_int`] and similar, to wrap the integer types whose length
-/// varies in C++. It's hoped to contribute full support here to [cxx]
-/// in a future change.
-///
-/// ## Strings
-///
-/// `autocxx` uses [cxx::CxxString]. However, as noted above, we can't
-/// just pass a C++ string by value, so we'll box and unbox it automatically
-/// such that you're really dealing with `UniquePtr<CxxString>` on the Rust
-/// side, even if the API just took or returned a plain old `std::string`.
-///
-/// However, to ease ergonomics, functions that accept a `std::string` will
-/// actually accept anything that
-/// implements a trait called `ffi::ToCppString`. That may either be a
-/// `UniquePtr<CxxString>` or just a plain old Rust string - which will be
-/// converted transparently to a C++ string.
-///
-/// This trait, and its implementations, are not present in the `autocxx`
-/// documentation because they're dynamically generated in _your_ code
-/// so that they can call through to a `make_string` implementation in
-/// the C++ that we're injecting into your C++ build system.
-///
-/// (None of that happens if you use [exclude_utilities], so don't do that.)
-///
-/// If you need to create a blank `UniquePtr<CxxString>` in Rust, such that
-/// (for example) you can pass its mutable reference or pointer into some
-/// pre-existing C++ API, call `ffi::make_string("")` which will return
-/// a blank `UniquePtr<CxxString>`.
-///
-/// Don't attempt to use [cxx::let_cpp_string] which will allocate the
-/// string on the stack, and is generally incompatible with the
-/// [cxx::UniquePtr]-based approaches we use here.
-///
-/// ## Preprocessor symbols
-///
-/// `#define` and other preprocessor symbols will appear as constants.
-/// At present there is no way to do compile-time disablement of code
-/// (equivalent of `#ifdef`).
-///
-/// ## Integer types
-///
-/// For C++ types with a defined size, just go ahead and use `u64`, `i32` etc.
-/// For types such as `int` or `unsigned long`, the hope is that you can
-/// eventually use `std::os::raw::c_int` oor `std::os::raw::c_ulong` etc.
-/// For now, this doesn't quite work: instead you need to wrap these values
-/// in a newtype wrapper such as [c_int] or [c_ulong] in this crate.
-///
-/// ## String constants
-///
-/// Whether from a preprocessor symbol or from a C++ `char*` constant,
-/// strings appear as `[u8]` with a null terminator. To get a Rust string,
-/// do this:
-///
-/// ```cpp
-/// #define BOB "Hello"
-/// ```
-///
-/// ```
-/// # mod ffi { pub static BOB: [u8; 6] = [72u8, 101u8, 108u8, 108u8, 111u8, 0u8]; }
-/// assert_eq!(std::str::from_utf8(&ffi::BOB).unwrap().trim_end_matches(char::from(0)), "Hello");
-/// ```
-///
-/// ## Namespaces
-///
-/// The C++ namespace structure is reflected in mods within the generated
-/// ffi mod. However, at present there is an internal limitation that
-/// autocxx can't handle multiple symbols with the same identifier, even
-/// if they're in different namespaces. This will be fixed in future.
-///
-/// ## Overloads - and identifiers ending in digits
-///
-/// C++ allows function overloads; Rust doesn't. `autocxx` follows the lead
-/// of `bindgen` here and generating overloads as `func`, `func1`, `func2` etc.
-/// This is essentially awful without `rust-analyzer` IDE support, which isn't
-/// quite there yet.
-///
-/// `autocxx` doesn't yet support default paramters.
-///
-/// It's fairly likely we'll change the model here in the future, such that
-/// we can pass tuples of different parameter types into a single function
-/// implementation.
-///
-/// ## Forward declarations
-///
-/// A type which is incomplete in the C++ headers (i.e. represented only by a forward
-/// declaration) can't be held in a `UniquePtr` within Rust (because Rust can't know
-/// if it has a destructor that will need to be called if the object is `Drop`ped.)
-/// Naturally, such an object can't be passed by value either; it can still be
-/// referenced in Rust references.
-///
-/// ## Generic types
-///
-/// If you're using one of the generic types which is supported natively by cxx,
-/// e.g. `std::unique_ptr`, it should work as you expect. For other generic types,
-/// we synthesize a concrete Rust type, corresponding to a C++ typedef, for each
-/// concrete instantiation of the type. Such generated types are always opaque,
-/// and never have methods attached. That's therefore enough to pass them
-/// between return types and parameters of other functions within [`cxx::UniquePtr`]s
-/// but not really enough to do anything else with these types just yet. Hopefully,
-/// this will be improved in future. At present such types have a name
-/// `AutocxxConcrete{n}` but this may change in future.
-///
-/// ## Exceptions
-///
-/// Exceptions are not supported. If your C++ code is compiled with exceptions,
-/// you can expect serious runtime explosions. The underlying [cxx] crate has
-/// exception support, so it would be possible to add them.
-///
-/// # Subclasses
-///
-/// There is limited and experimental support for creating Rust subclasses of
-/// C++ classes. (Yes, even more experimental than all the rest of this!)
-/// See [`subclass::CppSubclass`] for information about how you do this.
-/// This is useful primarily if you want to listen out for messages broadcast
-/// using the C++ observer/listener pattern.
-///
-/// # Mixing manual and automated bindings
-///
-/// `autocxx` uses [cxx] underneath, and its build process will happily spot and
-/// process and manually-crafted [`cxx::bridge`] mods which you include in your
-/// Rust source code. A common pattern good be to use `autocxx` to generate
-/// all the bindings possible, then hand-craft a [`cxx::bridge`] mod for the
-/// remainder where `autocxx` falls short.
-///
-/// To do this, you'll need to use the [ability of one cxx::bridge mod to refer to types from another](https://cxx.rs/extern-c++.html#reusing-existing-binding-types),
-/// for example:
-///
-/// ```rust,ignore
-/// autocxx::include_cpp! {
-///     #include "foo.h"
-///     safety!(unsafe_ffi)
-///     generate!("take_A")
-///     generate!("A")
-/// }
-/// #[cxx::bridge]
-/// mod ffi2 {
-///     unsafe extern "C++" {
-///         include!("foo.h");
-///         type A = crate::ffi::A;
-///         fn give_A() -> UniquePtr<A>; // in practice, autocxx could happily do this
-///     }
-/// }
-/// fn main() {
-///     let a = ffi2::give_A();
-///     assert_eq!(ffi::take_A(&a), autocxx::c_int(5));
-/// }
-/// ```
-///
-/// # Safety
-///
-/// # Examples
-///
-/// * [Demo](https://github.com/google/autocxx/tree/main/demo) - simplest possible demo
-/// * [S2 example](https://github.com/google/autocxx/tree/main/examples/s2) - example using S2 geometry library
-/// * [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini) - example using (something like) the Steam client library
-/// * [Subclass example](https://github.com/google/autocxx/tree/main/examples/subclass) - example using subclasses
-/// * [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/src/tests.rs)
-///   - hundreds of small snippets
-///
-/// Contributions of more examples to the `examples` directory are much appreciated!
-///
-/// # Internals
-///
-/// For documentation on how this all actually _works_, see
-/// [IncludeCppEngine].
-#[macro_export]
-macro_rules! include_cpp {
-    (
-        $(#$include:ident $lit:literal)*
-        $($mac:ident!($($arg:tt)*))*
-    ) => {
-        $($crate::$include!{__docs})*
-        $($crate::$mac!{__docs})*
-        $crate::include_cpp_impl! {
-            $(#include $lit)*
-            $($mac!($($arg)*))*
-        }
-    };
-}
-
-/// Include a C++ header. A directive to be included inside
-/// [include_cpp] - see [include_cpp] for details
-#[macro_export]
-macro_rules! include {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Generate Rust bindings for the given C++ type or function.
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-/// See also [generate_pod].
-#[macro_export]
-macro_rules! generate {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Generate as "plain old data" and add to allowlist.
-/// Generate Rust bindings for the given C++ type such that
-/// it can be passed and owned by value in Rust. This only works
-/// for C++ types which have trivial move constructors and no
-/// destructor - you'll encounter a compile error otherwise.
-/// If your type doesn't match that description, use [generate]
-/// instead, and own the type using [UniquePtr][autocxx_engine::cxx::UniquePtr].
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-#[macro_export]
-macro_rules! generate_pod {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Generate Rust bindings for all C++ types and functions
-/// found. Highly experimental and not recommended.
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-/// See also [generate].
-#[macro_export]
-macro_rules! generate_all {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Generate as "plain old data". For use with [generate_all]
-/// and similarly experimental.
-#[macro_export]
-macro_rules! pod {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Skip the normal generation of a `make_string` function
-/// and other utilities which we might generate normally.
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-#[macro_export]
-macro_rules! exclude_utilities {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Entirely block some type from appearing in the generated
-/// code. This can be useful if there is a type which is not
-/// understood by bindgen or autocxx, and incorrect code is
-/// otherwise generated.
-/// This is 'greedy' in the sense that any functions/methods
-/// which take or return such a type will _also_ be blocked.
-///
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-#[macro_export]
-macro_rules! block {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Avoid generating implicit constructors for this type.
-/// The rules for when to generate C++ implicit constructors
-/// are complex, and if autocxx gets it wrong, you can block
-/// such constructors using this.
-///
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-#[macro_export]
-macro_rules! block_constructors {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// The name of the mod to be generated with the FFI code.
-/// The default is `ffi`.
-///
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-#[macro_export]
-macro_rules! name {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Specifies a global safety policy for functions generated
-/// from these headers. By default (without such a `safety!`
-/// directive) all such functions are marked as `unsafe` and
-/// therefore can only be called within an `unsafe {}` block
-/// or some `unsafe` function which you create.
-///
-/// Alternatively, by specifying a `safety!` block you can
-/// declare that most generated functions are in fact safe.
-/// Specifically, you'd specify:
-/// `safety!(unsafe)`
-/// or
-/// `safety!(unsafe_ffi)`
-/// These two options are functionally identical. If you're
-/// unsure, simply use `unsafe`. The reason for the
-/// latter option is if you have code review policies which
-/// might want to give a different level of scrutiny to
-/// C++ interop as opposed to other types of unsafe Rust code.
-/// Maybe in your organization, C++ interop is less scary than
-/// a low-level Rust data structure using pointer manipulation.
-/// Or maybe it's more scary. Either way, using `unsafe` for
-/// the data structure and using `unsafe_ffi` for the C++
-/// interop allows you to apply different linting tools and
-/// policies to the different options.
-///
-/// Irrespective, C++ code is of course unsafe. It's worth
-/// noting that use of C++ can cause unexpected unsafety at
-/// a distance in faraway Rust code. As with any use of the
-/// `unsafe` keyword in Rust, *you the human* are declaring
-/// that you've analyzed all possible ways that the code
-/// can be used and you are guaranteeing to the compiler that
-/// no badness can occur. Good luck.
-///
-/// Generated C++ APIs which use raw pointers remain `unsafe`
-/// no matter what policy you choose.
-#[macro_export]
-macro_rules! safety {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Whether to avoid generating [`cxx::UniquePtr`] and [`cxx::Vector`]
-/// implementations. This is primarily useful for reducing test cases and
-/// shouldn't be used in normal operation.
-///
-/// A directive to be included inside
-/// [include_cpp] - see [include_cpp] for general information.
-#[macro_export]
-macro_rules! exclude_impls {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// Deprecated - use [`extern_rust_type`] instead.
-#[macro_export]
-#[deprecated]
-macro_rules! rust_type {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// See [`extern_rust::extern_rust_type`].
-#[macro_export]
-macro_rules! extern_rust_type {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-/// See [`subclass::subclass`].
-#[macro_export]
-macro_rules! subclass {
-    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! usage {
-    (__docs) => {};
-    ($($tt:tt)*) => {
-        compile_error! {r#"usage:  include_cpp! {
-                   #include "path/to/header.h"
-                   generate!(...)
-                   generate_pod!(...)
-               }
-"#}
-    };
-}
-
-#[doc(hidden)]
-pub use autocxx_macro::include_cpp_impl;
-
-#[doc(hidden)]
-pub use autocxx_macro::cpp_semantics;
-
-macro_rules! ctype_wrapper {
-    ($r:ident, $c:expr, $d:expr) => {
-        #[doc=$d]
-        #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash)]
-        #[allow(non_camel_case_types)]
-        #[repr(transparent)]
-        pub struct $r(pub ::std::os::raw::$r);
-
-        unsafe impl autocxx_engine::cxx::ExternType for $r {
-            type Id = autocxx_engine::cxx::type_id!($c);
-            type Kind = autocxx_engine::cxx::kind::Trivial;
-        }
-
-        impl From<::std::os::raw::$r> for $r {
-            fn from(val: ::std::os::raw::$r) -> Self {
-                Self(val)
-            }
-        }
-
-        impl From<$r> for ::std::os::raw::$r {
-            fn from(val: $r) -> Self {
-                val.0
-            }
-        }
-    };
-}
-
-ctype_wrapper!(
-    c_ulonglong,
-    "c_ulonglong",
-    "Newtype wrapper for an unsigned long long"
-);
-ctype_wrapper!(c_longlong, "c_longlong", "Newtype wrapper for a long long");
-ctype_wrapper!(c_ulong, "c_ulong", "Newtype wrapper for an unsigned long");
-ctype_wrapper!(c_long, "c_long", "Newtype wrapper for a long");
-ctype_wrapper!(
-    c_ushort,
-    "c_ushort",
-    "Newtype wrapper for an unsigned short"
-);
-ctype_wrapper!(c_short, "c_short", "Newtype wrapper for an short");
-ctype_wrapper!(c_uint, "c_uint", "Newtype wrapper for an unsigned int");
-ctype_wrapper!(c_int, "c_int", "Newtype wrapper for an int");
-ctype_wrapper!(c_uchar, "c_uchar", "Newtype wrapper for an unsigned char");
-
-/// Newtype wrapper for a C void. Only useful as a `*c_void`
-#[allow(non_camel_case_types)]
-#[repr(transparent)]
-pub struct c_void(pub ::std::os::raw::c_void);
-
-unsafe impl autocxx_engine::cxx::ExternType for c_void {
-    type Id = autocxx_engine::cxx::type_id!(c_void);
-    type Kind = autocxx_engine::cxx::kind::Trivial;
-}
-
-/// autocxx couldn't generate these bindings.
-/// If you come across a method, type or function which refers to this type,
-/// it indicates that autocxx couldn't generate that binding. A documentation
-/// comment should be attached indicating the reason.
-pub struct BindingGenerationFailure {
-    _unallocatable: [*const u8; 0],
-    _pinned: core::marker::PhantomData<core::marker::PhantomPinned>,
-}
-
-/// Tools to export Rust code to C++.
-// These are in a mod to avoid shadowing the definitions of the
-// directives above, which, being macro_rules, are unavoidably
-// in the crate root but must be function-style macros to keep
-// the include_cpp impl happy.
-pub mod extern_rust {
-
-    /// Declare that this is a Rust type which is to be exported to C++.
-    /// You can use this in two ways:
-    /// * as an attribute macro on a Rust type, for instance:
-    ///   ```
-    ///   # use autocxx_macro::extern_rust_type as extern_rust_type;
-    ///   #[extern_rust_type]
-    ///   struct Bar;
-    ///   ```
-    /// * as a directive within the [include_cpp] macro, in which case
-    ///   provide the type path in brackets:
-    ///   ```
-    ///   # use autocxx_macro::include_cpp_impl as include_cpp;
-    ///   include_cpp!(
-    ///   #   parse_only!()
-    ///       #include "input.h"
-    ///       extern_rust_type!(Bar)
-    ///       safety!(unsafe)
-    ///   );
-    ///   struct Bar;
-    ///   ```
-    /// These may be used within references in the signatures of C++ functions,
-    /// for instance. This will contribute to an `extern "Rust"` section of the
-    /// generated `cxx` bindings, and this type will appear in the C++ header
-    /// generated for use in C++.
-    pub use autocxx_macro::extern_rust_type;
-
-    /// Declare that a given function is a Rust function which is to be exported
-    /// to C++. This is used as an attribute macro on a Rust function, for instance:
-    /// ```
-    /// # use autocxx_macro::extern_rust_function as extern_rust_function;
-    /// #[extern_rust_function]
-    /// pub fn call_me_from_cpp() { }
-    /// ```
-    pub use autocxx_macro::extern_rust_function;
-}
-
-/// Equivalent to [`std::convert::AsMut`], but returns a pinned mutable reference
-/// such that cxx methods can be called on it.
-pub trait PinMut<T>: AsRef<T> {
-    /// Return a pinned mutable reference to a type.
-    fn pin_mut(&mut self) -> std::pin::Pin<&mut T>;
-}
-
-/// Imports which you're likely to want to use.
-pub mod prelude {
-    pub use crate::c_int;
-    pub use crate::c_long;
-    pub use crate::c_longlong;
-    pub use crate::c_short;
-    pub use crate::c_uchar;
-    pub use crate::c_uint;
-    pub use crate::c_ulong;
-    pub use crate::c_ulonglong;
-    pub use crate::c_ushort;
-    pub use crate::c_void;
-    pub use crate::cpp_semantics;
-    pub use crate::include_cpp;
-    pub use crate::PinMut;
-    pub use moveit::moveit;
-    pub use moveit::new::New;
-}
-
-/// Re-export moveit for ease of consumers.
-pub use moveit;
diff --git a/third_party/rust/autocxx/v0_16/crate/tools/upgrade-version.sh b/third_party/rust/autocxx/v0_16/crate/tools/upgrade-version.sh
deleted file mode 100755
index 066a2ce..0000000
--- a/third_party/rust/autocxx/v0_16/crate/tools/upgrade-version.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-
-OLD=$1
-NEW=$2
-find . -type f -name "Cargo.toml" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g"
-find . -type f -name "README.md" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g"
diff --git a/third_party/rust/autocxx/v0_16/BUILD.gn b/third_party/rust/autocxx/v0_17/BUILD.gn
similarity index 83%
rename from third_party/rust/autocxx/v0_16/BUILD.gn
rename to third_party/rust/autocxx/v0_17/BUILD.gn
index 30a4da64..b7ceb6de 100644
--- a/third_party/rust/autocxx/v0_16/BUILD.gn
+++ b/third_party/rust/autocxx/v0_17/BUILD.gn
@@ -6,7 +6,7 @@
 
 cargo_crate("lib") {
   crate_name = "autocxx"
-  epoch = "0.16"
+  epoch = "0.17"
   crate_type = "rlib"
   crate_root = "crate/src/lib.rs"
 
@@ -16,8 +16,7 @@
   edition = "2021"
   deps = [
     "//third_party/rust/aquamarine/v0_1:lib",
-    "//third_party/rust/autocxx_engine/v0_16:lib",
-    "//third_party/rust/autocxx_macro/v0_16:lib",
+    "//third_party/rust/autocxx_macro/v0_17:lib",
     "//third_party/rust/cxx/v1:lib",
     "//third_party/rust/moveit/v0_4:lib",
   ]
diff --git a/third_party/rust/autocxx/v0_16/README.chromium b/third_party/rust/autocxx/v0_17/README.chromium
similarity index 90%
rename from third_party/rust/autocxx/v0_16/README.chromium
rename to third_party/rust/autocxx/v0_17/README.chromium
index 05a02e6..13be0a55 100644
--- a/third_party/rust/autocxx/v0_16/README.chromium
+++ b/third_party/rust/autocxx/v0_17/README.chromium
@@ -1,6 +1,6 @@
 Name: autocxx
 URL: https://crates.io/crates/autocxx
 Description: Safe autogenerated interop between Rust and C++
-Version: 0.16.0
+Version: 0.17.2
 Security Critical: yes
 License: Apache 2.0
diff --git a/third_party/rust/autocxx/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx/v0_17/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..0e2733c
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/ISSUE_TEMPLATE.md b/third_party/rust/autocxx/v0_17/crate/.github/ISSUE_TEMPLATE.md
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/.github/ISSUE_TEMPLATE.md
rename to third_party/rust/autocxx/v0_17/crate/.github/ISSUE_TEMPLATE.md
diff --git a/third_party/rust/autocxx/v0_16/crate/.github/PULL_REQUEST_TEMPLATE.md b/third_party/rust/autocxx/v0_17/crate/.github/PULL_REQUEST_TEMPLATE.md
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/.github/PULL_REQUEST_TEMPLATE.md
rename to third_party/rust/autocxx/v0_17/crate/.github/PULL_REQUEST_TEMPLATE.md
diff --git a/third_party/rust/autocxx/v0_17/crate/.github/workflows/ci.yml b/third_party/rust/autocxx/v0_17/crate/.github/workflows/ci.yml
new file mode 100644
index 0000000..45c8723
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/.github/workflows/ci.yml
@@ -0,0 +1,215 @@
+name: CI
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+  schedule: [cron: "40 1 * * *"]
+  workflow_dispatch:
+
+env:
+  RUST_BACKTRACE: short
+  # CI builds don't benefit very much from this and it has bugs
+  CARGO_INCREMENTAL: 0
+  # We can't use a debugger in CI, and this makes builds faster and the cache
+  # smaller. (TODO: use -Cdebuginfo=0 if it doesn't make backtraces useless)
+  RUSTFLAGS: -Cdebuginfo=1
+  CARGO_TERM_COLOR: always
+
+jobs:
+  test:
+    name: Test ${{matrix.name || format('Rust {0}', matrix.rust)}}
+    runs-on: ${{matrix.os || 'ubuntu'}}-latest
+
+    strategy:
+      fail-fast: false
+
+      matrix:
+        include:
+          - rust: nightly
+          - rust: beta
+          - rust: stable
+          - name: macOS
+            rust: nightly
+            os: macos
+          - name: Windows (gnu)
+            rust: nightly-x86_64-pc-windows-gnu
+            os: windows
+          - name: Windows (msvc)
+            rust: nightly-x86_64-pc-windows-msvc
+            os: windows
+            flags: /EHsc
+    env:
+      CXXFLAGS: ${{matrix.flags}}
+      RUSTFLAGS: --cfg deny_warnings -Dwarnings
+
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          submodules: recursive
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{matrix.rust}}
+          components: rustfmt
+      # The `{ sharedKey: ... }` allows different actions to share the cache.
+      - uses: Swatinem/rust-cache@v1
+        with: { sharedKey: fullBuild }
+      # For operating systems that have it packaged, install creduce
+      - name: Install creduce (Linux)
+        if: matrix.os == ''
+        run: sudo apt-get install creduce
+      - name: Install creduce (MacOS)
+        if: matrix.os == 'macOS'
+        run: brew install creduce
+      - name: Set LIBCLANG_PATH (Windows)
+        # Windows github action doesn't set the path for clang, so set it
+        # See https://github.com/rust-lang/rust-bindgen/issues/1797
+        if: matrix.os == 'windows'
+        run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
+      - name: Exclude failing targets and tests
+        # no creduce on Windows, so exclude tests needing creduce there
+        run: |
+          echo RUSTFLAGS=$RUSTFLAGS >> $GITHUB_ENV
+          echo ::set-output name=exclude::${{runner.os == 'Windows' && '--exclude autocxx-reduce --exclude autocxx-gen' || ''}}
+        env:
+          # non-linux failures https://github.com/google/autocxx/issues/819
+          # beta failing tests https://github.com/google/autocxx/issues/818
+          RUSTFLAGS: ${{matrix.name == 'Windows (msvc)' && '--cfg skip_windows_msvc_failing_tests' || ''}} ${{matrix.name == 'Windows (gnu)' && '--cfg skip_windows_gnu_failing_tests' || ''}}
+        id: testsuite
+        shell: bash
+      - run: cargo test --workspace ${{steps.testsuite.outputs.exclude}}
+
+  examples:
+    name: Examples ${{matrix.name || format('Rust {0}', matrix.rust)}}
+    runs-on: ${{matrix.os || 'ubuntu'}}-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - rust: nightly
+          - rust: beta
+          - rust: stable
+          - name: macOS
+            rust: nightly
+            os: macos
+          - name: Windows (gnu)
+            rust: nightly-x86_64-pc-windows-gnu
+            os: windows
+          - name: Windows (msvc)
+            rust: nightly-x86_64-pc-windows-msvc
+            os: windows
+            flags: /EHsc
+    env:
+      CXXFLAGS: ${{matrix.flags}}
+      RUSTFLAGS: --cfg deny_warnings -Dwarnings
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          submodules: recursive
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{matrix.rust}}
+          components: rustfmt
+      - uses: Swatinem/rust-cache@v1
+        with: { sharedKey: fullBuild }
+      - name: Set LIBCLANG_PATH (Windows)
+        # Windows github action doesn't set the path for clang, so set it
+        # See https://github.com/rust-lang/rust-bindgen/issues/1797
+        if: matrix.os == 'windows'
+        run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
+      - name: Build s2 example
+        working-directory: ./examples/s2
+        # s2 doesn't link on Windows
+        if: matrix.os != 'windows'
+        run: cargo build
+      - name: Build steam example
+        working-directory: ./examples/steam-mini
+        run: cargo build
+      - name: Build subclass example
+        working-directory: ./examples/subclass
+        run: cargo build
+      - name: Build pod example
+        working-directory: ./examples/pod
+        run: cargo build
+      - name: Build chromium render-frame-host example
+        working-directory: ./examples/chromium-fake-render-frame-host
+        # chromium-fake-render-frame-host doesn't link on Windows
+        if: matrix.os != 'windows'
+        run: cargo build
+      - name: Build non-trivial-type-on-stack example
+        working-directory: ./examples/non-trivial-type-on-stack
+        run: cargo build
+
+  sanitizer:
+    name: Address Sanitizer
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      # Need nightly rust.
+      - uses: hecrj/setup-rust-action@v1
+        with:
+          rust-version: nightly
+          components: rust-src
+      - uses: Swatinem/rust-cache@v1
+      - name: Tests with asan
+        env:
+          RUSTFLAGS: -Zsanitizer=address -Cdebuginfo=0
+          RUSTDOCFLAGS: -Zsanitizer=address
+          ASAN_OPTIONS: "detect_stack_use_after_return=1:detect_leaks=0"
+          # Work around https://github.com/rust-lang/rust/issues/59125 by
+          # disabling backtraces. In an ideal world we'd probably suppress the
+          # leak sanitization, but we don't care about backtraces here, so long
+          # as the other tests have them.
+          RUST_BACKTRACE: "0"
+        run: cargo -Z build-std test --workspace --target x86_64-unknown-linux-gnu
+
+  # Clippy check
+  clippy:
+    name: Clippy
+    runs-on: ubuntu-latest
+    env:
+      CARGO_TERM_COLOR: always
+    steps:
+      - uses: actions/checkout@v2
+      - uses: hecrj/setup-rust-action@v1
+        with:
+          components: clippy
+      - uses: Swatinem/rust-cache@v1
+      - run: cargo clippy --workspace --tests -- -Dclippy::all
+
+  # Mention outdated dependencies
+  outdated:
+    name: Outdated
+    runs-on: ubuntu-latest
+    env:
+      CARGO_TERM_COLOR: always
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/install@cargo-outdated
+      - run: cargo outdated -R -w
+
+  # Check rustfmt is good
+  fmt:
+    name: Format
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: hecrj/setup-rust-action@v1
+        with:
+          components: rustfmt
+      - run: cargo fmt --all -- --check
+
+  # Detect cases where documentation links don't resolve and such.
+  doc:
+    name: Docs
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: hecrj/setup-rust-action@v1
+      - uses: Swatinem/rust-cache@v1
+        with: { sharedKey: fullBuild }
+      - run: |
+          for package in $(cargo metadata --no-deps --format-version=1 | jq -r '.packages[] | .name'); do
+            cargo rustdoc --color always -p "$package" -- -D warnings
+          done
+        env: { RUSTDOCFLAGS: -Dwarnings }
diff --git a/third_party/rust/autocxx/v0_17/crate/.github/workflows/site.yml b/third_party/rust/autocxx/v0_17/crate/.github/workflows/site.yml
new file mode 100644
index 0000000..0dd4237
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/.github/workflows/site.yml
@@ -0,0 +1,43 @@
+name: github pages
+
+on:
+  push:
+    branches:
+      - main
+  pull_request:
+    branches:
+      - main
+
+jobs:
+  deploy:
+    runs-on: ubuntu-20.04
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Build preprocessor
+        run: cargo build
+        working-directory: tools/mdbook-preprocessor
+
+      - name: Setup mdBook
+        uses: peaceiris/actions-mdbook@v1
+        with:
+          mdbook-version: 'latest'
+
+      - name: Install mdbook-linkcheck
+        run: cargo install mdbook-linkcheck
+    
+      - name: Install mdbook-mermaid
+        run: |
+          curl -LSfs https://japaric.github.io/trust/install.sh | \
+            sh -s -- --git badboy/mdbook-mermaid
+    
+      - run: mdbook build
+        working-directory: book
+
+      - name: Deploy
+        uses: peaceiris/actions-gh-pages@v3
+        if: ${{ github.ref == 'refs/heads/main' }}
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          publish_dir: ./book/build/html
+          #cname: autocxx.rs
diff --git a/third_party/rust/autocxx/v0_17/crate/.gitignore b/third_party/rust/autocxx/v0_17/crate/.gitignore
new file mode 100644
index 0000000..af6bc430
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/.gitignore
@@ -0,0 +1,2 @@
+target
+.vscode/
diff --git a/third_party/rust/autocxx/v0_16/crate/.gitmodules b/third_party/rust/autocxx/v0_17/crate/.gitmodules
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/.gitmodules
rename to third_party/rust/autocxx/v0_17/crate/.gitmodules
diff --git a/third_party/rust/autocxx/v0_16/crate/Cargo.toml b/third_party/rust/autocxx/v0_17/crate/Cargo.toml
similarity index 91%
rename from third_party/rust/autocxx/v0_16/crate/Cargo.toml
rename to third_party/rust/autocxx/v0_17/crate/Cargo.toml
index 5b0d94a..a10da5a2 100644
--- a/third_party/rust/autocxx/v0_16/crate/Cargo.toml
+++ b/third_party/rust/autocxx/v0_17/crate/Cargo.toml
@@ -12,9 +12,10 @@
 [package]
 edition = "2021"
 name = "autocxx"
-version = "0.16.0"
+version = "0.17.2"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 description = "Safe autogenerated interop between Rust and C++"
+homepage = "https://autocxx.rs"
 keywords = ["ffi"]
 categories = [
     "development-tools::ffi",
@@ -27,11 +28,8 @@
 [dependencies.aquamarine]
 version = "0.1"
 
-[dependencies.autocxx-engine]
-version = "0.16.0"
-
 [dependencies.autocxx-macro]
-version = "0.16.0"
+version = "0.17.2"
 
 [dependencies.cxx]
 version = "1.0.54"
diff --git a/third_party/rust/autocxx/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx/v0_17/crate/Cargo.toml.orig
new file mode 100644
index 0000000..8ea82ad5
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,40 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx"
+version = "0.17.2"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+license = "MIT OR Apache-2.0"
+description = "Safe autogenerated interop between Rust and C++"
+repository = "https://github.com/google/autocxx"
+homepage = "https://autocxx.rs"
+edition = "2021"
+keywords = ["ffi"]
+categories = ["development-tools::ffi", "api-bindings"]
+
+# This is stricter about avoiding optional features in dependencies
+# such as 'syn'. We build this way such that CI spots any mistakes
+# where we are depending on such features.
+resolver = "2"
+
+[dependencies]
+autocxx-macro = { path="macro", version="0.17.2" }
+cxx = "1.0.54" # ... also needed because expansion of type_id refers to ::cxx
+aquamarine = "0.1" # docs
+moveit = { version = "0.4", features = [ "cxx" ] }
+
+[workspace]
+members = ["parser", "engine", "gen/cmd", "gen/build", "macro", "demo", "tools/reduce", "tools/mdbook-preprocessor", "integration-tests"]
+exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/chromium-fake-render-frame-host", "examples/pod", "examples/non-trivial-type-on-stack"]
+
+#[patch.crates-io]
+#cxx = { path="../cxx" }
+#cxx-gen = { path="../cxx/gen/lib" }
+#autocxx-bindgen = { path="../bindgen" }
+#moveit = { path="../moveit" }
diff --git a/third_party/rust/autocxx/v0_16/crate/LICENSE-APACHE b/third_party/rust/autocxx/v0_17/crate/LICENSE-APACHE
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/LICENSE-APACHE
rename to third_party/rust/autocxx/v0_17/crate/LICENSE-APACHE
diff --git a/third_party/rust/autocxx/v0_16/crate/LICENSE-MIT b/third_party/rust/autocxx/v0_17/crate/LICENSE-MIT
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/LICENSE-MIT
rename to third_party/rust/autocxx/v0_17/crate/LICENSE-MIT
diff --git a/third_party/rust/autocxx/v0_17/crate/README.md b/third_party/rust/autocxx/v0_17/crate/README.md
new file mode 100644
index 0000000..99a68b4
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/README.md
@@ -0,0 +1,37 @@
+# Autocxx
+
+[![GitHub](https://img.shields.io/crates/l/autocxx)](https://github.com/google/autocxx)
+[![crates.io](https://img.shields.io/crates/d/autocxx)](https://crates.io/crates/autocxx)
+[![docs.rs](https://docs.rs/autocxx/badge.svg)](https://docs.rs/autocxx)
+
+This project is a tool for calling C++ from Rust in a heavily automated, but safe, fashion.
+
+The intention is that it has all the fluent safety from [cxx](https://cxx.rs) whilst generating interfaces automatically from existing C++ headers using a variant of [bindgen](https://docs.rs/bindgen/latest/bindgen/). Think of autocxx as glue which plugs bindgen into cxx.
+
+For full documentation, see [the manual](https://google.github.io/autocxx/).
+
+# Overview
+
+```rust,ignore
+autocxx::include_cpp! {
+    #include "url/origin.h"
+    generate!("url::Origin")
+    safety!(unsafe_ffi)
+}
+
+fn main() {
+    let o = ffi::url::Origin::CreateFromNormalizedTuple("https",
+        "google.com", 443);
+    let uri = o.Serialize();
+    println!("URI is {}", uri.to_str().unwrap());
+}
+```
+
+#### License and usage notes
+
+This is not an officially supported Google product.
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
diff --git a/third_party/rust/autocxx/v0_17/crate/book/.gitignore b/third_party/rust/autocxx/v0_17/crate/book/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/third_party/rust/autocxx/v0_17/crate/book/README.md b/third_party/rust/autocxx/v0_17/crate/book/README.md
new file mode 100644
index 0000000..c883a45
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/README.md
@@ -0,0 +1,10 @@
+Published automatically to https://autocxx.rs from master branch.
+
+To build and view locally:
+
+- Install [mdBook] and some preprocessors: `cargo install mdbook mdbook-mermaid mdbook-linkcheck`.
+- Build our custom preprocessor: run `cargo build` in `tools/mdbook-preprocessor`
+- Run `mdbook build` in this directory.
+- Open the generated *build/html/index.html*.
+
+[mdBook]: https://github.com/rust-lang/mdBook
diff --git a/third_party/rust/autocxx/v0_17/crate/book/book.toml b/third_party/rust/autocxx/v0_17/crate/book/book.toml
new file mode 100644
index 0000000..b557cc6
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/book.toml
@@ -0,0 +1,25 @@
+[book]
+title = "Rust ♡ Existing C++"
+authors = ["Adrian Taylor"]
+description = "autocxx — safe interop between Rust and existing C++"
+
+[rust]
+edition = "2018"
+
+[build]
+build-dir = "build"
+create-missing = false
+
+[preprocessor.autocxx]
+command = "../target/debug/autocxx-mdbook-preprocessor"
+
+[preprocessor.mermaid]
+command = "mdbook-mermaid"
+
+[output.html]
+cname = "google.github.io/autocxx"
+git-repository-url = "https://github.com/google/autocxx"
+additional-js = ["mermaid.min.js", "mermaid-init.js"]
+
+[output.linkcheck]
+warning-policy = "error"
diff --git a/third_party/rust/autocxx/v0_17/crate/book/mermaid-init.js b/third_party/rust/autocxx/v0_17/crate/book/mermaid-init.js
new file mode 100644
index 0000000..313a6e8
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/mermaid-init.js
@@ -0,0 +1 @@
+mermaid.initialize({startOnLoad:true});
diff --git a/third_party/rust/autocxx/v0_17/crate/book/mermaid.min.js b/third_party/rust/autocxx/v0_17/crate/book/mermaid.min.js
new file mode 100644
index 0000000..d45942f3
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/mermaid.min.js
@@ -0,0 +1,4 @@
+/* MIT Licensed. Copyright (c) 2014 - 2021 Knut Sveidqvist */
+/*! For license information please see https://github.com/mermaid-js/mermaid/blob/8.13.10/LICENSE */
+!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.mermaid=e():t.mermaid=e()}("undefined"!=typeof self?self:this,(function(){return(()=>{var t={1362:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,6],n=[1,7],r=[1,8],i=[1,9],a=[1,12],o=[1,11],s=[1,15,24],c=[1,19],u=[1,31],l=[1,34],h=[1,32],f=[1,33],d=[1,35],p=[1,36],y=[1,37],g=[1,38],m=[1,41],v=[1,42],b=[1,43],_=[1,44],x=[15,24],w=[1,56],k=[1,57],T=[1,58],E=[1,59],C=[1,60],S=[1,61],A=[15,24,31,38,39,47,50,51,52,53,54,55,60,62],M=[15,24,29,31,38,39,43,47,50,51,52,53,54,55,60,62,77,78,79,80],N=[7,8,9,10,15,18,22,24],D=[47,77,78,79,80],O=[47,54,55,77,78,79,80],B=[47,50,51,52,53,77,78,79,80],L=[15,24,31],I=[1,93],R={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,direction:5,directive:6,direction_tb:7,direction_bt:8,direction_rl:9,direction_lr:10,graphConfig:11,openDirective:12,typeDirective:13,closeDirective:14,NEWLINE:15,":":16,argDirective:17,open_directive:18,type_directive:19,arg_directive:20,close_directive:21,CLASS_DIAGRAM:22,statements:23,EOF:24,statement:25,className:26,alphaNumToken:27,classLiteralName:28,GENERICTYPE:29,relationStatement:30,LABEL:31,classStatement:32,methodStatement:33,annotationStatement:34,clickStatement:35,cssClassStatement:36,CLASS:37,STYLE_SEPARATOR:38,STRUCT_START:39,members:40,STRUCT_STOP:41,ANNOTATION_START:42,ANNOTATION_END:43,MEMBER:44,SEPARATOR:45,relation:46,STR:47,relationType:48,lineType:49,AGGREGATION:50,EXTENSION:51,COMPOSITION:52,DEPENDENCY:53,LINE:54,DOTTED_LINE:55,CALLBACK:56,LINK:57,LINK_TARGET:58,CLICK:59,CALLBACK_NAME:60,CALLBACK_ARGS:61,HREF:62,CSSCLASS:63,commentToken:64,textToken:65,graphCodeTokens:66,textNoTagsToken:67,TAGSTART:68,TAGEND:69,"==":70,"--":71,PCT:72,DEFAULT:73,SPACE:74,MINUS:75,keywords:76,UNICODE_TEXT:77,NUM:78,ALPHA:79,BQUOTE_STR:80,$accept:0,$end:1},terminals_:{2:"error",7:"direction_tb",8:"direction_bt",9:"direction_rl",10:"direction_lr",15:"NEWLINE",16:":",18:"open_directive",19:"type_directive",20:"arg_directive",21:"close_directive",22:"CLASS_DIAGRAM",24:"EOF",29:"GENERICTYPE",31:"LABEL",37:"CLASS",38:"STYLE_SEPARATOR",39:"STRUCT_START",41:"STRUCT_STOP",42:"ANNOTATION_START",43:"ANNOTATION_END",44:"MEMBER",45:"SEPARATOR",47:"STR",50:"AGGREGATION",51:"EXTENSION",52:"COMPOSITION",53:"DEPENDENCY",54:"LINE",55:"DOTTED_LINE",56:"CALLBACK",57:"LINK",58:"LINK_TARGET",59:"CLICK",60:"CALLBACK_NAME",61:"CALLBACK_ARGS",62:"HREF",63:"CSSCLASS",66:"graphCodeTokens",68:"TAGSTART",69:"TAGEND",70:"==",71:"--",72:"PCT",73:"DEFAULT",74:"SPACE",75:"MINUS",76:"keywords",77:"UNICODE_TEXT",78:"NUM",79:"ALPHA",80:"BQUOTE_STR"},productions_:[0,[3,1],[3,1],[3,2],[5,1],[5,1],[5,1],[5,1],[4,1],[6,4],[6,6],[12,1],[13,1],[17,1],[14,1],[11,4],[23,1],[23,2],[23,3],[26,1],[26,1],[26,2],[26,2],[26,2],[25,1],[25,2],[25,1],[25,1],[25,1],[25,1],[25,1],[25,1],[25,1],[32,2],[32,4],[32,5],[32,7],[34,4],[40,1],[40,2],[33,1],[33,2],[33,1],[33,1],[30,3],[30,4],[30,4],[30,5],[46,3],[46,2],[46,2],[46,1],[48,1],[48,1],[48,1],[48,1],[49,1],[49,1],[35,3],[35,4],[35,3],[35,4],[35,4],[35,5],[35,3],[35,4],[35,4],[35,5],[35,3],[35,4],[35,4],[35,5],[36,3],[64,1],[64,1],[65,1],[65,1],[65,1],[65,1],[65,1],[65,1],[65,1],[67,1],[67,1],[67,1],[67,1],[27,1],[27,1],[27,1],[28,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:r.setDirection("TB");break;case 5:r.setDirection("BT");break;case 6:r.setDirection("RL");break;case 7:r.setDirection("LR");break;case 11:r.parseDirective("%%{","open_directive");break;case 12:r.parseDirective(a[s],"type_directive");break;case 13:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 14:r.parseDirective("}%%","close_directive","class");break;case 19:case 20:this.$=a[s];break;case 21:this.$=a[s-1]+a[s];break;case 22:case 23:this.$=a[s-1]+"~"+a[s];break;case 24:r.addRelation(a[s]);break;case 25:a[s-1].title=r.cleanupLabel(a[s]),r.addRelation(a[s-1]);break;case 33:r.addClass(a[s]);break;case 34:r.addClass(a[s-2]),r.setCssClass(a[s-2],a[s]);break;case 35:r.addClass(a[s-3]),r.addMembers(a[s-3],a[s-1]);break;case 36:r.addClass(a[s-5]),r.setCssClass(a[s-5],a[s-3]),r.addMembers(a[s-5],a[s-1]);break;case 37:r.addAnnotation(a[s],a[s-2]);break;case 38:this.$=[a[s]];break;case 39:a[s].push(a[s-1]),this.$=a[s];break;case 40:case 42:case 43:break;case 41:r.addMember(a[s-1],r.cleanupLabel(a[s]));break;case 44:this.$={id1:a[s-2],id2:a[s],relation:a[s-1],relationTitle1:"none",relationTitle2:"none"};break;case 45:this.$={id1:a[s-3],id2:a[s],relation:a[s-1],relationTitle1:a[s-2],relationTitle2:"none"};break;case 46:this.$={id1:a[s-3],id2:a[s],relation:a[s-2],relationTitle1:"none",relationTitle2:a[s-1]};break;case 47:this.$={id1:a[s-4],id2:a[s],relation:a[s-2],relationTitle1:a[s-3],relationTitle2:a[s-1]};break;case 48:this.$={type1:a[s-2],type2:a[s],lineType:a[s-1]};break;case 49:this.$={type1:"none",type2:a[s],lineType:a[s-1]};break;case 50:this.$={type1:a[s-1],type2:"none",lineType:a[s]};break;case 51:this.$={type1:"none",type2:"none",lineType:a[s]};break;case 52:this.$=r.relationType.AGGREGATION;break;case 53:this.$=r.relationType.EXTENSION;break;case 54:this.$=r.relationType.COMPOSITION;break;case 55:this.$=r.relationType.DEPENDENCY;break;case 56:this.$=r.lineType.LINE;break;case 57:this.$=r.lineType.DOTTED_LINE;break;case 58:case 64:this.$=a[s-2],r.setClickEvent(a[s-1],a[s]);break;case 59:case 65:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 60:case 68:this.$=a[s-2],r.setLink(a[s-1],a[s]);break;case 61:case 69:this.$=a[s-3],r.setLink(a[s-2],a[s-1],a[s]);break;case 62:case 70:this.$=a[s-3],r.setLink(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 63:case 71:this.$=a[s-4],r.setLink(a[s-3],a[s-2],a[s]),r.setTooltip(a[s-3],a[s-1]);break;case 66:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 67:this.$=a[s-4],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setTooltip(a[s-3],a[s]);break;case 72:r.setCssClass(a[s-1],a[s])}},table:[{3:1,4:2,5:3,6:4,7:e,8:n,9:r,10:i,11:5,12:10,18:a,22:o},{1:[3]},{1:[2,1]},{1:[2,2]},{3:13,4:2,5:3,6:4,7:e,8:n,9:r,10:i,11:5,12:10,18:a,22:o},{1:[2,8]},t(s,[2,4]),t(s,[2,5]),t(s,[2,6]),t(s,[2,7]),{13:14,19:[1,15]},{15:[1,16]},{19:[2,11]},{1:[2,3]},{14:17,16:[1,18],21:c},t([16,21],[2,12]),{5:29,6:28,7:e,8:n,9:r,10:i,12:10,18:a,23:20,25:21,26:30,27:39,28:40,30:22,32:23,33:24,34:25,35:26,36:27,37:u,42:l,44:h,45:f,56:d,57:p,59:y,63:g,77:m,78:v,79:b,80:_},{15:[1,45]},{17:46,20:[1,47]},{15:[2,14]},{24:[1,48]},{15:[1,49],24:[2,16]},t(x,[2,24],{31:[1,50]}),t(x,[2,26]),t(x,[2,27]),t(x,[2,28]),t(x,[2,29]),t(x,[2,30]),t(x,[2,31]),t(x,[2,32]),t(x,[2,40],{46:51,48:54,49:55,31:[1,53],47:[1,52],50:w,51:k,52:T,53:E,54:C,55:S}),{26:62,27:39,28:40,77:m,78:v,79:b,80:_},t(x,[2,42]),t(x,[2,43]),{27:63,77:m,78:v,79:b},{26:64,27:39,28:40,77:m,78:v,79:b,80:_},{26:65,27:39,28:40,77:m,78:v,79:b,80:_},{26:66,27:39,28:40,77:m,78:v,79:b,80:_},{47:[1,67]},t(A,[2,19],{27:39,28:40,26:68,29:[1,69],77:m,78:v,79:b,80:_}),t(A,[2,20],{29:[1,70]}),t(M,[2,86]),t(M,[2,87]),t(M,[2,88]),t([15,24,29,31,38,39,47,50,51,52,53,54,55,60,62],[2,89]),t(N,[2,9]),{14:71,21:c},{21:[2,13]},{1:[2,15]},{5:29,6:28,7:e,8:n,9:r,10:i,12:10,18:a,23:72,24:[2,17],25:21,26:30,27:39,28:40,30:22,32:23,33:24,34:25,35:26,36:27,37:u,42:l,44:h,45:f,56:d,57:p,59:y,63:g,77:m,78:v,79:b,80:_},t(x,[2,25]),{26:73,27:39,28:40,47:[1,74],77:m,78:v,79:b,80:_},{46:75,48:54,49:55,50:w,51:k,52:T,53:E,54:C,55:S},t(x,[2,41]),{49:76,54:C,55:S},t(D,[2,51],{48:77,50:w,51:k,52:T,53:E}),t(O,[2,52]),t(O,[2,53]),t(O,[2,54]),t(O,[2,55]),t(B,[2,56]),t(B,[2,57]),t(x,[2,33],{38:[1,78],39:[1,79]}),{43:[1,80]},{47:[1,81]},{47:[1,82]},{60:[1,83],62:[1,84]},{27:85,77:m,78:v,79:b},t(A,[2,21]),t(A,[2,22]),t(A,[2,23]),{15:[1,86]},{24:[2,18]},t(L,[2,44]),{26:87,27:39,28:40,77:m,78:v,79:b,80:_},{26:88,27:39,28:40,47:[1,89],77:m,78:v,79:b,80:_},t(D,[2,50],{48:90,50:w,51:k,52:T,53:E}),t(D,[2,49]),{27:91,77:m,78:v,79:b},{40:92,44:I},{26:94,27:39,28:40,77:m,78:v,79:b,80:_},t(x,[2,58],{47:[1,95]}),t(x,[2,60],{47:[1,97],58:[1,96]}),t(x,[2,64],{47:[1,98],61:[1,99]}),t(x,[2,68],{47:[1,101],58:[1,100]}),t(x,[2,72]),t(N,[2,10]),t(L,[2,46]),t(L,[2,45]),{26:102,27:39,28:40,77:m,78:v,79:b,80:_},t(D,[2,48]),t(x,[2,34],{39:[1,103]}),{41:[1,104]},{40:105,41:[2,38],44:I},t(x,[2,37]),t(x,[2,59]),t(x,[2,61]),t(x,[2,62],{58:[1,106]}),t(x,[2,65]),t(x,[2,66],{47:[1,107]}),t(x,[2,69]),t(x,[2,70],{58:[1,108]}),t(L,[2,47]),{40:109,44:I},t(x,[2,35]),{41:[2,39]},t(x,[2,63]),t(x,[2,67]),t(x,[2,71]),{41:[1,110]},t(x,[2,36])],defaultActions:{2:[2,1],3:[2,2],5:[2,8],12:[2,11],13:[2,3],19:[2,14],47:[2,13],48:[2,15],72:[2,18],105:[2,39]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},F={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),18;case 1:return 7;case 2:return 8;case 3:return 9;case 4:return 10;case 5:return this.begin("type_directive"),19;case 6:return this.popState(),this.begin("arg_directive"),16;case 7:return this.popState(),this.popState(),21;case 8:return 20;case 9:case 10:case 12:case 19:break;case 11:return 15;case 13:case 14:return 22;case 15:return this.begin("struct"),39;case 16:return"EOF_IN_STRUCT";case 17:return"OPEN_IN_STRUCT";case 18:return this.popState(),41;case 20:return"MEMBER";case 21:return 37;case 22:return 63;case 23:return 56;case 24:return 57;case 25:return 59;case 26:return 42;case 27:return 43;case 28:this.begin("generic");break;case 29:case 32:case 35:case 38:case 41:case 44:this.popState();break;case 30:return"GENERICTYPE";case 31:this.begin("string");break;case 33:return"STR";case 34:this.begin("bqstring");break;case 36:return"BQUOTE_STR";case 37:this.begin("href");break;case 39:return 62;case 40:this.begin("callback_name");break;case 42:this.popState(),this.begin("callback_args");break;case 43:return 60;case 45:return 61;case 46:case 47:case 48:case 49:return 58;case 50:case 51:return 51;case 52:case 53:return 53;case 54:return 52;case 55:return 50;case 56:return 54;case 57:return 55;case 58:return 31;case 59:return 38;case 60:return 75;case 61:return"DOT";case 62:return"PLUS";case 63:return 72;case 64:case 65:return"EQUALS";case 66:return 79;case 67:return"PUNCTUATION";case 68:return 78;case 69:return 77;case 70:return 74;case 71:return 24}},rules:[/^(?:%%\{)/,/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:\s*(\r?\n)+)/,/^(?:\s+)/,/^(?:classDiagram-v2\b)/,/^(?:classDiagram\b)/,/^(?:[{])/,/^(?:$)/,/^(?:[{])/,/^(?:[}])/,/^(?:[\n])/,/^(?:[^{}\n]*)/,/^(?:class\b)/,/^(?:cssClass\b)/,/^(?:callback\b)/,/^(?:link\b)/,/^(?:click\b)/,/^(?:<<)/,/^(?:>>)/,/^(?:[~])/,/^(?:[~])/,/^(?:[^~]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:[`])/,/^(?:[`])/,/^(?:[^`]+)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:--)/,/^(?:\.\.)/,/^(?::{1}[^:\n;]+)/,/^(?::{3})/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:\w+)/,/^(?:[!"#$%&'*+,-.`?\\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\s)/,/^(?:$)/],conditions:{arg_directive:{rules:[7,8],inclusive:!1},type_directive:{rules:[6,7],inclusive:!1},open_directive:{rules:[5],inclusive:!1},callback_args:{rules:[44,45],inclusive:!1},callback_name:{rules:[41,42,43],inclusive:!1},href:{rules:[38,39],inclusive:!1},struct:{rules:[16,17,18,19,20],inclusive:!1},generic:{rules:[29,30],inclusive:!1},bqstring:{rules:[35,36],inclusive:!1},string:{rules:[32,33],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,9,10,11,12,13,14,15,21,22,23,24,25,26,27,28,31,34,37,40,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71],inclusive:!0}}};function P(){this.yy={}}return R.lexer=F,P.prototype=R,R.Parser=P,new P}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8218).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},5890:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,23,41],i=[1,17],a=[1,20],o=[1,25],s=[1,26],c=[1,27],u=[1,28],l=[1,37],h=[23,38,39],f=[4,6,9,11,23,41],d=[34,35,36,37],p=[22,29],y=[1,55],g={trace:function(){},yy:{},symbols_:{error:2,start:3,ER_DIAGRAM:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,entityName:17,relSpec:18,role:19,BLOCK_START:20,attributes:21,BLOCK_STOP:22,ALPHANUM:23,attribute:24,attributeType:25,attributeName:26,attributeKeyType:27,attributeComment:28,ATTRIBUTE_WORD:29,ATTRIBUTE_KEY:30,COMMENT:31,cardinality:32,relType:33,ZERO_OR_ONE:34,ZERO_OR_MORE:35,ONE_OR_MORE:36,ONLY_ONE:37,NON_IDENTIFYING:38,IDENTIFYING:39,WORD:40,open_directive:41,type_directive:42,arg_directive:43,close_directive:44,$accept:0,$end:1},terminals_:{2:"error",4:"ER_DIAGRAM",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",20:"BLOCK_START",22:"BLOCK_STOP",23:"ALPHANUM",29:"ATTRIBUTE_WORD",30:"ATTRIBUTE_KEY",31:"COMMENT",34:"ZERO_OR_ONE",35:"ZERO_OR_MORE",36:"ONE_OR_MORE",37:"ONLY_ONE",38:"NON_IDENTIFYING",39:"IDENTIFYING",40:"WORD",41:"open_directive",42:"type_directive",43:"arg_directive",44:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,5],[10,4],[10,3],[10,1],[17,1],[21,1],[21,2],[24,2],[24,3],[24,3],[24,4],[25,1],[26,1],[27,1],[28,1],[18,3],[32,1],[32,1],[32,1],[32,1],[33,1],[33,1],[19,1],[19,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:break;case 3:case 7:case 8:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:case 16:case 23:case 24:case 25:case 35:this.$=a[s];break;case 12:r.addEntity(a[s-4]),r.addEntity(a[s-2]),r.addRelationship(a[s-4],a[s],a[s-2],a[s-3]);break;case 13:r.addEntity(a[s-3]),r.addAttributes(a[s-3],a[s-1]);break;case 14:r.addEntity(a[s-2]);break;case 15:r.addEntity(a[s]);break;case 17:this.$=[a[s]];break;case 18:a[s].push(a[s-1]),this.$=a[s];break;case 19:this.$={attributeType:a[s-1],attributeName:a[s]};break;case 20:this.$={attributeType:a[s-2],attributeName:a[s-1],attributeKeyType:a[s]};break;case 21:this.$={attributeType:a[s-2],attributeName:a[s-1],attributeComment:a[s]};break;case 22:this.$={attributeType:a[s-3],attributeName:a[s-2],attributeKeyType:a[s-1],attributeComment:a[s]};break;case 26:case 34:this.$=a[s].replace(/"/g,"");break;case 27:this.$={cardA:a[s],relType:a[s-1],cardB:a[s-2]};break;case 28:this.$=r.Cardinality.ZERO_OR_ONE;break;case 29:this.$=r.Cardinality.ZERO_OR_MORE;break;case 30:this.$=r.Cardinality.ONE_OR_MORE;break;case 31:this.$=r.Cardinality.ONLY_ONE;break;case 32:this.$=r.Identification.NON_IDENTIFYING;break;case 33:this.$=r.Identification.IDENTIFYING;break;case 36:r.parseDirective("%%{","open_directive");break;case 37:r.parseDirective(a[s],"type_directive");break;case 38:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 39:r.parseDirective("}%%","close_directive","er")}},table:[{3:1,4:e,7:3,12:4,41:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,41:n},{13:8,42:[1,9]},{42:[2,36]},{6:[1,10],7:15,8:11,9:[1,12],10:13,11:[1,14],12:4,17:16,23:i,41:n},{1:[2,2]},{14:18,15:[1,19],44:a},t([15,44],[2,37]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:15,10:21,12:4,17:16,23:i,41:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,15],{18:22,32:24,20:[1,23],34:o,35:s,36:c,37:u}),t([6,9,11,15,20,23,34,35,36,37,41],[2,16]),{11:[1,29]},{16:30,43:[1,31]},{11:[2,39]},t(r,[2,5]),{17:32,23:i},{21:33,22:[1,34],24:35,25:36,29:l},{33:38,38:[1,39],39:[1,40]},t(h,[2,28]),t(h,[2,29]),t(h,[2,30]),t(h,[2,31]),t(f,[2,9]),{14:41,44:a},{44:[2,38]},{15:[1,42]},{22:[1,43]},t(r,[2,14]),{21:44,22:[2,17],24:35,25:36,29:l},{26:45,29:[1,46]},{29:[2,23]},{32:47,34:o,35:s,36:c,37:u},t(d,[2,32]),t(d,[2,33]),{11:[1,48]},{19:49,23:[1,51],40:[1,50]},t(r,[2,13]),{22:[2,18]},t(p,[2,19],{27:52,28:53,30:[1,54],31:y}),t([22,29,30,31],[2,24]),{23:[2,27]},t(f,[2,10]),t(r,[2,12]),t(r,[2,34]),t(r,[2,35]),t(p,[2,20],{28:56,31:y}),t(p,[2,21]),t([22,29,31],[2,25]),t(p,[2,26]),t(p,[2,22])],defaultActions:{5:[2,36],7:[2,2],20:[2,39],31:[2,38],37:[2,23],44:[2,18],47:[2,27]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},m={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),41;case 1:return this.begin("type_directive"),42;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),44;case 4:return 43;case 5:case 6:case 8:case 13:case 17:break;case 7:return 11;case 9:return 9;case 10:return 40;case 11:return 4;case 12:return this.begin("block"),20;case 14:return 30;case 15:return 29;case 16:return 31;case 18:return this.popState(),22;case 19:case 32:return e.yytext[0];case 20:case 24:return 34;case 21:case 25:return 35;case 22:case 26:return 36;case 23:return 37;case 27:case 29:case 30:return 38;case 28:return 39;case 31:return 23;case 33:return 6}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:[\s]+)/i,/^(?:"[^"]*")/i,/^(?:erDiagram\b)/i,/^(?:\{)/i,/^(?:\s+)/i,/^(?:(?:PK)|(?:FK))/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:"[^"]*")/i,/^(?:[\n]+)/i,/^(?:\})/i,/^(?:.)/i,/^(?:\|o\b)/i,/^(?:\}o\b)/i,/^(?:\}\|)/i,/^(?:\|\|)/i,/^(?:o\|)/i,/^(?:o\{)/i,/^(?:\|\{)/i,/^(?:\.\.)/i,/^(?:--)/i,/^(?:\.-)/i,/^(?:-\.)/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:.)/i,/^(?:$)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},block:{rules:[13,14,15,16,17,18,19],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,20,21,22,23,24,25,26,27,28,29,30,31,32,33],inclusive:!0}}};function v(){this.yy={}}return g.lexer=m,v.prototype=g,g.Parser=v,new v}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8009).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},3602:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,9],n=[1,7],r=[1,6],i=[1,8],a=[1,20,21,22,23,38,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],o=[2,10],s=[1,20],c=[1,21],u=[1,22],l=[1,23],h=[1,30],f=[1,59],d=[1,45],p=[1,49],y=[1,33],g=[1,34],m=[1,35],v=[1,36],b=[1,37],_=[1,53],x=[1,60],w=[1,48],k=[1,50],T=[1,52],E=[1,56],C=[1,57],S=[1,38],A=[1,39],M=[1,40],N=[1,41],D=[1,58],O=[1,47],B=[1,51],L=[1,54],I=[1,55],R=[1,46],F=[1,63],P=[1,68],j=[1,20,21,22,23,38,42,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],Y=[1,72],z=[1,71],U=[1,73],q=[20,21,23,74,75],H=[1,94],$=[1,99],W=[1,102],V=[1,103],G=[1,96],X=[1,101],Z=[1,104],Q=[1,97],K=[1,109],J=[1,108],tt=[1,98],et=[1,100],nt=[1,105],rt=[1,106],it=[1,107],at=[1,110],ot=[20,21,22,23,74,75],st=[20,21,22,23,48,74,75],ct=[20,21,22,23,40,47,48,50,52,54,56,58,59,60,62,64,66,67,69,74,75,84,88,98,99,102,104,105,115,116,117,118,119,120],ut=[20,21,23],lt=[20,21,23,47,59,60,74,75,84,88,98,99,102,104,105,115,116,117,118,119,120],ht=[1,12,20,21,22,23,24,38,42,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],ft=[47,59,60,84,88,98,99,102,104,105,115,116,117,118,119,120],dt=[1,143],pt=[1,151],yt=[1,152],gt=[1,153],mt=[1,154],vt=[1,138],bt=[1,139],_t=[1,135],xt=[1,146],wt=[1,147],kt=[1,148],Tt=[1,149],Et=[1,150],Ct=[1,155],St=[1,156],At=[1,141],Mt=[1,144],Nt=[1,140],Dt=[1,137],Ot=[20,21,22,23,38,42,47,59,60,79,80,81,82,83,84,88,98,99,102,104,105,111,112,113,114,115,116,117,118,119,120],Bt=[1,159],Lt=[20,21,22,23,26,47,59,60,84,98,99,102,104,105,115,116,117,118,119,120],It=[20,21,22,23,24,26,38,40,41,42,47,51,53,55,57,59,60,61,63,65,66,68,70,74,75,79,80,81,82,83,84,85,88,98,99,102,104,105,106,107,115,116,117,118,119,120],Rt=[12,21,22,24],Ft=[22,99],Pt=[1,242],jt=[1,237],Yt=[1,238],zt=[1,246],Ut=[1,243],qt=[1,240],Ht=[1,239],$t=[1,241],Wt=[1,244],Vt=[1,245],Gt=[1,247],Xt=[1,265],Zt=[20,21,23,99],Qt=[20,21,22,23,59,60,79,95,98,99,102,103,104,105,106],Kt={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,directive:5,openDirective:6,typeDirective:7,closeDirective:8,separator:9,":":10,argDirective:11,open_directive:12,type_directive:13,arg_directive:14,close_directive:15,graphConfig:16,document:17,line:18,statement:19,SEMI:20,NEWLINE:21,SPACE:22,EOF:23,GRAPH:24,NODIR:25,DIR:26,FirstStmtSeperator:27,ending:28,endToken:29,spaceList:30,spaceListNewline:31,verticeStatement:32,styleStatement:33,linkStyleStatement:34,classDefStatement:35,classStatement:36,clickStatement:37,subgraph:38,text:39,SQS:40,SQE:41,end:42,direction:43,link:44,node:45,vertex:46,AMP:47,STYLE_SEPARATOR:48,idString:49,PS:50,PE:51,"(-":52,"-)":53,STADIUMSTART:54,STADIUMEND:55,SUBROUTINESTART:56,SUBROUTINEEND:57,VERTEX_WITH_PROPS_START:58,ALPHA:59,COLON:60,PIPE:61,CYLINDERSTART:62,CYLINDEREND:63,DIAMOND_START:64,DIAMOND_STOP:65,TAGEND:66,TRAPSTART:67,TRAPEND:68,INVTRAPSTART:69,INVTRAPEND:70,linkStatement:71,arrowText:72,TESTSTR:73,START_LINK:74,LINK:75,textToken:76,STR:77,keywords:78,STYLE:79,LINKSTYLE:80,CLASSDEF:81,CLASS:82,CLICK:83,DOWN:84,UP:85,textNoTags:86,textNoTagsToken:87,DEFAULT:88,stylesOpt:89,alphaNum:90,CALLBACKNAME:91,CALLBACKARGS:92,HREF:93,LINK_TARGET:94,HEX:95,numList:96,INTERPOLATE:97,NUM:98,COMMA:99,style:100,styleComponent:101,MINUS:102,UNIT:103,BRKT:104,DOT:105,PCT:106,TAGSTART:107,alphaNumToken:108,idStringToken:109,alphaNumStatement:110,direction_tb:111,direction_bt:112,direction_rl:113,direction_lr:114,PUNCTUATION:115,UNICODE_TEXT:116,PLUS:117,EQUALS:118,MULT:119,UNDERSCORE:120,graphCodeTokens:121,ARROW_CROSS:122,ARROW_POINT:123,ARROW_CIRCLE:124,ARROW_OPEN:125,QUOTE:126,$accept:0,$end:1},terminals_:{2:"error",10:":",12:"open_directive",13:"type_directive",14:"arg_directive",15:"close_directive",20:"SEMI",21:"NEWLINE",22:"SPACE",23:"EOF",24:"GRAPH",25:"NODIR",26:"DIR",38:"subgraph",40:"SQS",41:"SQE",42:"end",47:"AMP",48:"STYLE_SEPARATOR",50:"PS",51:"PE",52:"(-",53:"-)",54:"STADIUMSTART",55:"STADIUMEND",56:"SUBROUTINESTART",57:"SUBROUTINEEND",58:"VERTEX_WITH_PROPS_START",59:"ALPHA",60:"COLON",61:"PIPE",62:"CYLINDERSTART",63:"CYLINDEREND",64:"DIAMOND_START",65:"DIAMOND_STOP",66:"TAGEND",67:"TRAPSTART",68:"TRAPEND",69:"INVTRAPSTART",70:"INVTRAPEND",73:"TESTSTR",74:"START_LINK",75:"LINK",77:"STR",79:"STYLE",80:"LINKSTYLE",81:"CLASSDEF",82:"CLASS",83:"CLICK",84:"DOWN",85:"UP",88:"DEFAULT",91:"CALLBACKNAME",92:"CALLBACKARGS",93:"HREF",94:"LINK_TARGET",95:"HEX",97:"INTERPOLATE",98:"NUM",99:"COMMA",102:"MINUS",103:"UNIT",104:"BRKT",105:"DOT",106:"PCT",107:"TAGSTART",111:"direction_tb",112:"direction_bt",113:"direction_rl",114:"direction_lr",115:"PUNCTUATION",116:"UNICODE_TEXT",117:"PLUS",118:"EQUALS",119:"MULT",120:"UNDERSCORE",122:"ARROW_CROSS",123:"ARROW_POINT",124:"ARROW_CIRCLE",125:"ARROW_OPEN",126:"QUOTE"},productions_:[0,[3,1],[3,2],[5,4],[5,6],[6,1],[7,1],[11,1],[8,1],[4,2],[17,0],[17,2],[18,1],[18,1],[18,1],[18,1],[18,1],[16,2],[16,2],[16,2],[16,3],[28,2],[28,1],[29,1],[29,1],[29,1],[27,1],[27,1],[27,2],[31,2],[31,2],[31,1],[31,1],[30,2],[30,1],[19,2],[19,2],[19,2],[19,2],[19,2],[19,2],[19,9],[19,6],[19,4],[19,1],[9,1],[9,1],[9,1],[32,3],[32,4],[32,2],[32,1],[45,1],[45,5],[45,3],[46,4],[46,6],[46,4],[46,4],[46,4],[46,8],[46,4],[46,4],[46,4],[46,6],[46,4],[46,4],[46,4],[46,4],[46,4],[46,1],[44,2],[44,3],[44,3],[44,1],[44,3],[71,1],[72,3],[39,1],[39,2],[39,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[78,1],[86,1],[86,2],[35,5],[35,5],[36,5],[37,2],[37,4],[37,3],[37,5],[37,2],[37,4],[37,4],[37,6],[37,2],[37,4],[37,2],[37,4],[37,4],[37,6],[33,5],[33,5],[34,5],[34,5],[34,9],[34,9],[34,7],[34,7],[96,1],[96,3],[89,1],[89,3],[100,1],[100,2],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[101,1],[76,1],[76,1],[76,1],[76,1],[76,1],[76,1],[87,1],[87,1],[87,1],[87,1],[49,1],[49,2],[90,1],[90,2],[110,1],[110,1],[110,1],[110,1],[43,1],[43,1],[43,1],[43,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[108,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[109,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1],[121,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 5:r.parseDirective("%%{","open_directive");break;case 6:r.parseDirective(a[s],"type_directive");break;case 7:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 8:r.parseDirective("}%%","close_directive","flowchart");break;case 10:case 36:case 37:case 38:case 39:case 40:this.$=[];break;case 11:a[s]!==[]&&a[s-1].push(a[s]),this.$=a[s-1];break;case 12:case 78:case 80:case 92:case 148:case 150:case 151:case 74:case 146:this.$=a[s];break;case 19:r.setDirection("TB"),this.$="TB";break;case 20:r.setDirection(a[s-1]),this.$=a[s-1];break;case 35:this.$=a[s-1].nodes;break;case 41:this.$=r.addSubGraph(a[s-6],a[s-1],a[s-4]);break;case 42:this.$=r.addSubGraph(a[s-3],a[s-1],a[s-3]);break;case 43:this.$=r.addSubGraph(void 0,a[s-1],void 0);break;case 48:r.addLink(a[s-2].stmt,a[s],a[s-1]),this.$={stmt:a[s],nodes:a[s].concat(a[s-2].nodes)};break;case 49:r.addLink(a[s-3].stmt,a[s-1],a[s-2]),this.$={stmt:a[s-1],nodes:a[s-1].concat(a[s-3].nodes)};break;case 50:this.$={stmt:a[s-1],nodes:a[s-1]};break;case 51:this.$={stmt:a[s],nodes:a[s]};break;case 52:case 119:case 121:this.$=[a[s]];break;case 53:this.$=a[s-4].concat(a[s]);break;case 54:this.$=[a[s-2]],r.setClass(a[s-2],a[s]);break;case 55:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"square");break;case 56:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"circle");break;case 57:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"ellipse");break;case 58:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"stadium");break;case 59:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"subroutine");break;case 60:this.$=a[s-7],r.addVertex(a[s-7],a[s-1],"rect",void 0,void 0,void 0,Object.fromEntries([[a[s-5],a[s-3]]]));break;case 61:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"cylinder");break;case 62:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"round");break;case 63:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"diamond");break;case 64:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"hexagon");break;case 65:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"odd");break;case 66:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"trapezoid");break;case 67:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"inv_trapezoid");break;case 68:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_right");break;case 69:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_left");break;case 70:this.$=a[s],r.addVertex(a[s]);break;case 71:a[s-1].text=a[s],this.$=a[s-1];break;case 72:case 73:a[s-2].text=a[s-1],this.$=a[s-2];break;case 75:var c=r.destructLink(a[s],a[s-2]);this.$={type:c.type,stroke:c.stroke,length:c.length,text:a[s-1]};break;case 76:c=r.destructLink(a[s]),this.$={type:c.type,stroke:c.stroke,length:c.length};break;case 77:this.$=a[s-1];break;case 79:case 93:case 149:case 147:this.$=a[s-1]+""+a[s];break;case 94:case 95:this.$=a[s-4],r.addClass(a[s-2],a[s]);break;case 96:this.$=a[s-4],r.setClass(a[s-2],a[s]);break;case 97:case 105:this.$=a[s-1],r.setClickEvent(a[s-1],a[s]);break;case 98:case 106:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 99:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 100:this.$=a[s-4],r.setClickEvent(a[s-4],a[s-3],a[s-2]),r.setTooltip(a[s-4],a[s]);break;case 101:case 107:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 102:case 108:this.$=a[s-3],r.setLink(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 103:case 109:this.$=a[s-3],r.setLink(a[s-3],a[s-2],a[s]);break;case 104:case 110:this.$=a[s-5],r.setLink(a[s-5],a[s-4],a[s]),r.setTooltip(a[s-5],a[s-2]);break;case 111:this.$=a[s-4],r.addVertex(a[s-2],void 0,void 0,a[s]);break;case 112:case 114:this.$=a[s-4],r.updateLink(a[s-2],a[s]);break;case 113:this.$=a[s-4],r.updateLink([a[s-2]],a[s]);break;case 115:this.$=a[s-8],r.updateLinkInterpolate([a[s-6]],a[s-2]),r.updateLink([a[s-6]],a[s]);break;case 116:this.$=a[s-8],r.updateLinkInterpolate(a[s-6],a[s-2]),r.updateLink(a[s-6],a[s]);break;case 117:this.$=a[s-6],r.updateLinkInterpolate([a[s-4]],a[s]);break;case 118:this.$=a[s-6],r.updateLinkInterpolate(a[s-4],a[s]);break;case 120:case 122:a[s-2].push(a[s]),this.$=a[s-2];break;case 124:this.$=a[s-1]+a[s];break;case 152:this.$="v";break;case 153:this.$="-";break;case 154:this.$={stmt:"dir",value:"TB"};break;case 155:this.$={stmt:"dir",value:"BT"};break;case 156:this.$={stmt:"dir",value:"RL"};break;case 157:this.$={stmt:"dir",value:"LR"}}},table:[{3:1,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},{1:[3]},{1:[2,1]},{3:10,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},t(a,o,{17:11}),{7:12,13:[1,13]},{16:14,21:n,22:r,24:i},{16:15,21:n,22:r,24:i},{25:[1,16],26:[1,17]},{13:[2,5]},{1:[2,2]},{1:[2,9],18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},{8:61,10:[1,62],15:F},t([10,15],[2,6]),t(a,[2,17]),t(a,[2,18]),t(a,[2,19]),{20:[1,65],21:[1,66],22:P,27:64,30:67},t(j,[2,11]),t(j,[2,12]),t(j,[2,13]),t(j,[2,14]),t(j,[2,15]),t(j,[2,16]),{9:69,20:Y,21:z,23:U,44:70,71:74,74:[1,75],75:[1,76]},{9:77,20:Y,21:z,23:U},{9:78,20:Y,21:z,23:U},{9:79,20:Y,21:z,23:U},{9:80,20:Y,21:z,23:U},{9:81,20:Y,21:z,23:U},{9:83,20:Y,21:z,22:[1,82],23:U},t(j,[2,44]),t(q,[2,51],{30:84,22:P}),{22:[1,85]},{22:[1,86]},{22:[1,87]},{22:[1,88]},{26:H,47:$,59:W,60:V,77:[1,92],84:G,90:91,91:[1,89],93:[1,90],98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(j,[2,154]),t(j,[2,155]),t(j,[2,156]),t(j,[2,157]),t(ot,[2,52],{48:[1,111]}),t(st,[2,70],{109:123,40:[1,112],47:f,50:[1,113],52:[1,114],54:[1,115],56:[1,116],58:[1,117],59:d,60:p,62:[1,118],64:[1,119],66:[1,120],67:[1,121],69:[1,122],84:_,88:x,98:w,99:k,102:T,104:E,105:C,115:D,116:O,117:B,118:L,119:I,120:R}),t(ct,[2,146]),t(ct,[2,171]),t(ct,[2,172]),t(ct,[2,173]),t(ct,[2,174]),t(ct,[2,175]),t(ct,[2,176]),t(ct,[2,177]),t(ct,[2,178]),t(ct,[2,179]),t(ct,[2,180]),t(ct,[2,181]),t(ct,[2,182]),t(ct,[2,183]),t(ct,[2,184]),t(ct,[2,185]),t(ct,[2,186]),{9:124,20:Y,21:z,23:U},{11:125,14:[1,126]},t(ut,[2,8]),t(a,[2,20]),t(a,[2,26]),t(a,[2,27]),{21:[1,127]},t(lt,[2,34],{30:128,22:P}),t(j,[2,35]),{45:129,46:42,47:f,49:43,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,115:D,116:O,117:B,118:L,119:I,120:R},t(ht,[2,45]),t(ht,[2,46]),t(ht,[2,47]),t(ft,[2,74],{72:130,61:[1,132],73:[1,131]}),{22:dt,24:pt,26:yt,38:gt,39:133,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t([47,59,60,61,73,84,88,98,99,102,104,105,115,116,117,118,119,120],[2,76]),t(j,[2,36]),t(j,[2,37]),t(j,[2,38]),t(j,[2,39]),t(j,[2,40]),{22:dt,24:pt,26:yt,38:gt,39:157,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(Ot,o,{17:158}),t(q,[2,50],{47:Bt}),{26:H,47:$,59:W,60:V,84:G,90:160,95:[1,161],98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},{88:[1,162],96:163,98:[1,164]},{26:H,47:$,59:W,60:V,84:G,88:[1,165],90:166,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},{26:H,47:$,59:W,60:V,84:G,90:167,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ut,[2,97],{22:[1,168],92:[1,169]}),t(ut,[2,101],{22:[1,170]}),t(ut,[2,105],{108:95,110:172,22:[1,171],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),t(ut,[2,107],{22:[1,173]}),t(Lt,[2,148]),t(Lt,[2,150]),t(Lt,[2,151]),t(Lt,[2,152]),t(Lt,[2,153]),t(It,[2,158]),t(It,[2,159]),t(It,[2,160]),t(It,[2,161]),t(It,[2,162]),t(It,[2,163]),t(It,[2,164]),t(It,[2,165]),t(It,[2,166]),t(It,[2,167]),t(It,[2,168]),t(It,[2,169]),t(It,[2,170]),{47:f,49:174,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,115:D,116:O,117:B,118:L,119:I,120:R},{22:dt,24:pt,26:yt,38:gt,39:175,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:177,42:mt,47:$,50:[1,176],59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:178,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:179,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:180,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{59:[1,181]},{22:dt,24:pt,26:yt,38:gt,39:182,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:183,42:mt,47:$,59:W,60:V,64:[1,184],66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:185,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:186,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:187,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ct,[2,147]),t(Rt,[2,3]),{8:188,15:F},{15:[2,7]},t(a,[2,28]),t(lt,[2,33]),t(q,[2,48],{30:189,22:P}),t(ft,[2,71],{22:[1,190]}),{22:[1,191]},{22:dt,24:pt,26:yt,38:gt,39:192,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,66:vt,74:bt,75:[1,193],76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(It,[2,78]),t(It,[2,80]),t(It,[2,136]),t(It,[2,137]),t(It,[2,138]),t(It,[2,139]),t(It,[2,140]),t(It,[2,141]),t(It,[2,142]),t(It,[2,143]),t(It,[2,144]),t(It,[2,145]),t(It,[2,81]),t(It,[2,82]),t(It,[2,83]),t(It,[2,84]),t(It,[2,85]),t(It,[2,86]),t(It,[2,87]),t(It,[2,88]),t(It,[2,89]),t(It,[2,90]),t(It,[2,91]),{9:196,20:Y,21:z,22:dt,23:U,24:pt,26:yt,38:gt,40:[1,195],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,197],43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},{22:P,30:198},{22:[1,199],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,108:95,110:172,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:[1,200]},{22:[1,201]},{22:[1,202],99:[1,203]},t(Ft,[2,119]),{22:[1,204]},{22:[1,205],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,108:95,110:172,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:[1,206],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,108:95,110:172,115:tt,116:et,117:nt,118:rt,119:it,120:at},{77:[1,207]},t(ut,[2,99],{22:[1,208]}),{77:[1,209],94:[1,210]},{77:[1,211]},t(Lt,[2,149]),{77:[1,212],94:[1,213]},t(ot,[2,54],{109:123,47:f,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,115:D,116:O,117:B,118:L,119:I,120:R}),{22:dt,24:pt,26:yt,38:gt,41:[1,214],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:215,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,51:[1,216],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,53:[1,217],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,55:[1,218],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,57:[1,219],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{60:[1,220]},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,63:[1,221],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,65:[1,222],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,39:223,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,41:[1,224],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,66:vt,68:[1,225],70:[1,226],74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,66:vt,68:[1,228],70:[1,227],74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{9:229,20:Y,21:z,23:U},t(q,[2,49],{47:Bt}),t(ft,[2,73]),t(ft,[2,72]),{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,61:[1,230],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ft,[2,75]),t(It,[2,79]),{22:dt,24:pt,26:yt,38:gt,39:231,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(Ot,o,{17:232}),t(j,[2,43]),{46:233,47:f,49:43,59:d,60:p,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,115:D,116:O,117:B,118:L,119:I,120:R},{22:Pt,59:jt,60:Yt,79:zt,89:234,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:248,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:249,95:Ut,97:[1,250],98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:251,95:Ut,97:[1,252],98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{98:[1,253]},{22:Pt,59:jt,60:Yt,79:zt,89:254,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:255,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{26:H,47:$,59:W,60:V,84:G,90:256,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ut,[2,98]),{77:[1,257]},t(ut,[2,102],{22:[1,258]}),t(ut,[2,103]),t(ut,[2,106]),t(ut,[2,108],{22:[1,259]}),t(ut,[2,109]),t(st,[2,55]),{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,51:[1,260],59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(st,[2,62]),t(st,[2,57]),t(st,[2,58]),t(st,[2,59]),{59:[1,261]},t(st,[2,61]),t(st,[2,63]),{22:dt,24:pt,26:yt,38:gt,42:mt,47:$,59:W,60:V,65:[1,262],66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(st,[2,65]),t(st,[2,66]),t(st,[2,68]),t(st,[2,67]),t(st,[2,69]),t(Rt,[2,4]),t([22,47,59,60,84,88,98,99,102,104,105,115,116,117,118,119,120],[2,77]),{22:dt,24:pt,26:yt,38:gt,41:[1,263],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,264],43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},t(ot,[2,53]),t(ut,[2,111],{99:Xt}),t(Zt,[2,121],{101:266,22:Pt,59:jt,60:Yt,79:zt,95:Ut,98:qt,102:Ht,103:$t,104:Wt,105:Vt,106:Gt}),t(Qt,[2,123]),t(Qt,[2,125]),t(Qt,[2,126]),t(Qt,[2,127]),t(Qt,[2,128]),t(Qt,[2,129]),t(Qt,[2,130]),t(Qt,[2,131]),t(Qt,[2,132]),t(Qt,[2,133]),t(Qt,[2,134]),t(Qt,[2,135]),t(ut,[2,112],{99:Xt}),t(ut,[2,113],{99:Xt}),{22:[1,267]},t(ut,[2,114],{99:Xt}),{22:[1,268]},t(Ft,[2,120]),t(ut,[2,94],{99:Xt}),t(ut,[2,95],{99:Xt}),t(ut,[2,96],{108:95,110:172,26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),t(ut,[2,100]),{94:[1,269]},{94:[1,270]},{51:[1,271]},{61:[1,272]},{65:[1,273]},{9:274,20:Y,21:z,23:U},t(j,[2,42]),{22:Pt,59:jt,60:Yt,79:zt,95:Ut,98:qt,100:275,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},t(Qt,[2,124]),{26:H,47:$,59:W,60:V,84:G,90:276,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},{26:H,47:$,59:W,60:V,84:G,90:277,98:X,99:Z,102:Q,104:K,105:J,108:95,110:93,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(ut,[2,104]),t(ut,[2,110]),t(st,[2,56]),{22:dt,24:pt,26:yt,38:gt,39:278,42:mt,47:$,59:W,60:V,66:vt,74:bt,76:134,77:_t,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},t(st,[2,64]),t(Ot,o,{17:279}),t(Zt,[2,122],{101:266,22:Pt,59:jt,60:Yt,79:zt,95:Ut,98:qt,102:Ht,103:$t,104:Wt,105:Vt,106:Gt}),t(ut,[2,117],{108:95,110:172,22:[1,280],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),t(ut,[2,118],{108:95,110:172,22:[1,281],26:H,47:$,59:W,60:V,84:G,98:X,99:Z,102:Q,104:K,105:J,115:tt,116:et,117:nt,118:rt,119:it,120:at}),{22:dt,24:pt,26:yt,38:gt,41:[1,282],42:mt,47:$,59:W,60:V,66:vt,74:bt,76:194,78:145,79:xt,80:wt,81:kt,82:Tt,83:Et,84:Ct,85:St,87:136,88:At,98:X,99:Z,102:Mt,104:K,105:J,106:Nt,107:Dt,108:142,115:tt,116:et,117:nt,118:rt,119:it,120:at},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,283],43:31,45:32,46:42,47:f,49:43,59:d,60:p,79:y,80:g,81:m,82:v,83:b,84:_,88:x,98:w,99:k,102:T,104:E,105:C,109:44,111:S,112:A,113:M,114:N,115:D,116:O,117:B,118:L,119:I,120:R},{22:Pt,59:jt,60:Yt,79:zt,89:284,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},{22:Pt,59:jt,60:Yt,79:zt,89:285,95:Ut,98:qt,100:235,101:236,102:Ht,103:$t,104:Wt,105:Vt,106:Gt},t(st,[2,60]),t(j,[2,41]),t(ut,[2,115],{99:Xt}),t(ut,[2,116],{99:Xt})],defaultActions:{2:[2,1],9:[2,5],10:[2,2],126:[2,7]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},Jt={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),12;case 1:return this.begin("type_directive"),13;case 2:return this.popState(),this.begin("arg_directive"),10;case 3:return this.popState(),this.popState(),15;case 4:return 14;case 5:case 6:break;case 7:this.begin("string");break;case 8:case 17:case 20:case 23:case 26:this.popState();break;case 9:return"STR";case 10:return 79;case 11:return 88;case 12:return 80;case 13:return 97;case 14:return 81;case 15:return 82;case 16:this.begin("href");break;case 18:return 93;case 19:this.begin("callbackname");break;case 21:this.popState(),this.begin("callbackargs");break;case 22:return 91;case 24:return 92;case 25:this.begin("click");break;case 27:return 83;case 28:case 29:return t.lex.firstGraph()&&this.begin("dir"),24;case 30:return 38;case 31:return 42;case 32:case 33:case 34:case 35:return 94;case 36:return this.popState(),25;case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:return this.popState(),26;case 47:return 111;case 48:return 112;case 49:return 113;case 50:return 114;case 51:return 98;case 52:return 104;case 53:return 48;case 54:return 60;case 55:return 47;case 56:return 20;case 57:return 99;case 58:return 119;case 59:case 60:case 61:return 75;case 62:case 63:case 64:return 74;case 65:return 52;case 66:return 53;case 67:return 54;case 68:return 55;case 69:return 56;case 70:return 57;case 71:return 58;case 72:return 62;case 73:return 63;case 74:return 102;case 75:return 105;case 76:return 120;case 77:return 117;case 78:return 106;case 79:case 80:return 118;case 81:return 107;case 82:return 66;case 83:return 85;case 84:return"SEP";case 85:return 84;case 86:return 59;case 87:return 68;case 88:return 67;case 89:return 70;case 90:return 69;case 91:return 115;case 92:return 116;case 93:return 61;case 94:return 50;case 95:return 51;case 96:return 40;case 97:return 41;case 98:return 64;case 99:return 65;case 100:return 126;case 101:return 21;case 102:return 22;case 103:return 23}},rules:[/^(?:%%\{)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)[^\n]*)/,/^(?:[^\}]%%[^\n]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:click[\s]+)/,/^(?:[\s\n])/,/^(?:[^\s\n]*)/,/^(?:graph\b)/,/^(?:flowchart\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:(\r?\n)*\s*\n)/,/^(?:\s*LR\b)/,/^(?:\s*RL\b)/,/^(?:\s*TB\b)/,/^(?:\s*BT\b)/,/^(?:\s*TD\b)/,/^(?:\s*BR\b)/,/^(?:\s*<)/,/^(?:\s*>)/,/^(?:\s*\^)/,/^(?:\s*v\b)/,/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direction\s+LR[^\n]*)/,/^(?:[0-9]+)/,/^(?:#)/,/^(?::::)/,/^(?::)/,/^(?:&)/,/^(?:;)/,/^(?:,)/,/^(?:\*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:\(-)/,/^(?:-\))/,/^(?:\(\[)/,/^(?:\]\))/,/^(?:\[\[)/,/^(?:\]\])/,/^(?:\[\|)/,/^(?:\[\()/,/^(?:\)\])/,/^(?:-)/,/^(?:\.)/,/^(?:[\_])/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:<)/,/^(?:>)/,/^(?:\^)/,/^(?:\\\|)/,/^(?:v\b)/,/^(?:[A-Za-z]+)/,/^(?:\\\])/,/^(?:\[\/)/,/^(?:\/\])/,/^(?:\[\\)/,/^(?:[!"#$%&'*+,-.`?\\_/])/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\|)/,/^(?:\()/,/^(?:\))/,/^(?:\[)/,/^(?:\])/,/^(?:\{)/,/^(?:\})/,/^(?:")/,/^(?:(\r?\n)+)/,/^(?:\s)/,/^(?:$)/],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[23,24],inclusive:!1},callbackname:{rules:[20,21,22],inclusive:!1},href:{rules:[17,18],inclusive:!1},click:{rules:[26,27],inclusive:!1},vertex:{rules:[],inclusive:!1},dir:{rules:[36,37,38,39,40,41,42,43,44,45,46],inclusive:!1},string:{rules:[8,9],inclusive:!1},INITIAL:{rules:[0,5,6,7,10,11,12,13,14,15,16,19,25,28,29,30,31,32,33,34,35,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103],inclusive:!0}}};function te(){this.yy={}}return Kt.lexer=Jt,te.prototype=Kt,Kt.Parser=te,new te}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(5354).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},9959:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,3],n=[1,5],r=[7,9,11,12,13,14,15,16,17,18,19,20,22,29,34],i=[1,15],a=[1,16],o=[1,17],s=[1,18],c=[1,19],u=[1,20],l=[1,21],h=[1,22],f=[1,23],d=[1,25],p=[1,27],y=[1,30],g=[5,7,9,11,12,13,14,15,16,17,18,19,20,22,29,34],m={trace:function(){},yy:{},symbols_:{error:2,start:3,directive:4,gantt:5,document:6,EOF:7,line:8,SPACE:9,statement:10,NL:11,dateFormat:12,inclusiveEndDates:13,topAxis:14,axisFormat:15,excludes:16,includes:17,todayMarker:18,title:19,section:20,clickStatement:21,taskTxt:22,taskData:23,openDirective:24,typeDirective:25,closeDirective:26,":":27,argDirective:28,click:29,callbackname:30,callbackargs:31,href:32,clickStatementDebug:33,open_directive:34,type_directive:35,arg_directive:36,close_directive:37,$accept:0,$end:1},terminals_:{2:"error",5:"gantt",7:"EOF",9:"SPACE",11:"NL",12:"dateFormat",13:"inclusiveEndDates",14:"topAxis",15:"axisFormat",16:"excludes",17:"includes",18:"todayMarker",19:"title",20:"section",22:"taskTxt",23:"taskData",27:":",29:"click",30:"callbackname",31:"callbackargs",32:"href",34:"open_directive",35:"type_directive",36:"arg_directive",37:"close_directive"},productions_:[0,[3,2],[3,3],[6,0],[6,2],[8,2],[8,1],[8,1],[8,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,2],[10,1],[4,4],[4,6],[21,2],[21,3],[21,3],[21,4],[21,3],[21,4],[21,2],[33,2],[33,3],[33,3],[33,4],[33,3],[33,4],[33,2],[24,1],[25,1],[28,1],[26,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 2:return a[s-1];case 3:case 7:case 8:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 9:r.setDateFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 10:r.enableInclusiveEndDates(),this.$=a[s].substr(18);break;case 11:r.TopAxis(),this.$=a[s].substr(8);break;case 12:r.setAxisFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 13:r.setExcludes(a[s].substr(9)),this.$=a[s].substr(9);break;case 14:r.setIncludes(a[s].substr(9)),this.$=a[s].substr(9);break;case 15:r.setTodayMarker(a[s].substr(12)),this.$=a[s].substr(12);break;case 16:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 17:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 19:r.addTask(a[s-1],a[s]),this.$="task";break;case 23:this.$=a[s-1],r.setClickEvent(a[s-1],a[s],null);break;case 24:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 25:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],null),r.setLink(a[s-2],a[s]);break;case 26:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setLink(a[s-3],a[s]);break;case 27:this.$=a[s-2],r.setClickEvent(a[s-2],a[s],null),r.setLink(a[s-2],a[s-1]);break;case 28:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-1],a[s]),r.setLink(a[s-3],a[s-2]);break;case 29:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 30:case 36:this.$=a[s-1]+" "+a[s];break;case 31:case 32:case 34:this.$=a[s-2]+" "+a[s-1]+" "+a[s];break;case 33:case 35:this.$=a[s-3]+" "+a[s-2]+" "+a[s-1]+" "+a[s];break;case 37:r.parseDirective("%%{","open_directive");break;case 38:r.parseDirective(a[s],"type_directive");break;case 39:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 40:r.parseDirective("}%%","close_directive","gantt")}},table:[{3:1,4:2,5:e,24:4,34:n},{1:[3]},{3:6,4:2,5:e,24:4,34:n},t(r,[2,3],{6:7}),{25:8,35:[1,9]},{35:[2,37]},{1:[2,1]},{4:26,7:[1,10],8:11,9:[1,12],10:13,11:[1,14],12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:h,20:f,21:24,22:d,24:4,29:p,34:n},{26:28,27:[1,29],37:y},t([27,37],[2,38]),t(r,[2,8],{1:[2,2]}),t(r,[2,4]),{4:26,10:31,12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:h,20:f,21:24,22:d,24:4,29:p,34:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,9]),t(r,[2,10]),t(r,[2,11]),t(r,[2,12]),t(r,[2,13]),t(r,[2,14]),t(r,[2,15]),t(r,[2,16]),t(r,[2,17]),t(r,[2,18]),{23:[1,32]},t(r,[2,20]),{30:[1,33],32:[1,34]},{11:[1,35]},{28:36,36:[1,37]},{11:[2,40]},t(r,[2,5]),t(r,[2,19]),t(r,[2,23],{31:[1,38],32:[1,39]}),t(r,[2,29],{30:[1,40]}),t(g,[2,21]),{26:41,37:y},{37:[2,39]},t(r,[2,24],{32:[1,42]}),t(r,[2,25]),t(r,[2,27],{31:[1,43]}),{11:[1,44]},t(r,[2,26]),t(r,[2,28]),t(g,[2,22])],defaultActions:{5:[2,37],6:[2,1],30:[2,40],37:[2,39]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},v={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),34;case 1:return this.begin("type_directive"),35;case 2:return this.popState(),this.begin("arg_directive"),27;case 3:return this.popState(),this.popState(),37;case 4:return 36;case 5:case 6:case 7:case 9:case 10:case 11:break;case 8:return 11;case 12:this.begin("href");break;case 13:case 16:case 19:case 22:this.popState();break;case 14:return 32;case 15:this.begin("callbackname");break;case 17:this.popState(),this.begin("callbackargs");break;case 18:return 30;case 20:return 31;case 21:this.begin("click");break;case 23:return 29;case 24:return 5;case 25:return 12;case 26:return 13;case 27:return 14;case 28:return 15;case 29:return 17;case 30:return 16;case 31:return 18;case 32:return"date";case 33:return 19;case 34:return 20;case 35:return 22;case 36:return 23;case 37:return 27;case 38:return 7;case 39:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)*[^\n]*)/i,/^(?:[^\}]%%*[^\n]*)/i,/^(?:%%*[^\n]*[\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:inclusiveEndDates\b)/i,/^(?:topAxis\b)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:includes\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:todayMarker\s[^\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[19,20],inclusive:!1},callbackname:{rules:[16,17,18],inclusive:!1},href:{rules:[13,14],inclusive:!1},click:{rules:[22,23],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,15,21,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39],inclusive:!0}}};function b(){this.yy={}}return m.lexer=v,b.prototype=m,m.Parser=b,new b}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(6878).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},2553:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[2,3],n=[1,7],r=[7,12,15,17,19,20,21],i=[7,11,12,15,17,19,20,21],a=[2,20],o=[1,32],s={trace:function(){},yy:{},symbols_:{error:2,start:3,GG:4,":":5,document:6,EOF:7,DIR:8,options:9,body:10,OPT:11,NL:12,line:13,statement:14,COMMIT:15,commit_arg:16,BRANCH:17,ID:18,CHECKOUT:19,MERGE:20,RESET:21,reset_arg:22,STR:23,HEAD:24,reset_parents:25,CARET:26,$accept:0,$end:1},terminals_:{2:"error",4:"GG",5:":",7:"EOF",8:"DIR",11:"OPT",12:"NL",15:"COMMIT",17:"BRANCH",18:"ID",19:"CHECKOUT",20:"MERGE",21:"RESET",23:"STR",24:"HEAD",26:"CARET"},productions_:[0,[3,4],[3,5],[6,0],[6,2],[9,2],[9,1],[10,0],[10,2],[13,2],[13,1],[14,2],[14,2],[14,2],[14,2],[14,2],[16,0],[16,1],[22,2],[22,2],[25,0],[25,2]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 2:return r.setDirection(a[s-3]),a[s-1];case 4:r.setOptions(a[s-1]),this.$=a[s];break;case 5:a[s-1]+=a[s],this.$=a[s-1];break;case 7:this.$=[];break;case 8:a[s-1].push(a[s]),this.$=a[s-1];break;case 9:this.$=a[s-1];break;case 11:r.commit(a[s]);break;case 12:r.branch(a[s]);break;case 13:r.checkout(a[s]);break;case 14:r.merge(a[s]);break;case 15:r.reset(a[s]);break;case 16:this.$="";break;case 17:this.$=a[s];break;case 18:this.$=a[s-1]+":"+a[s];break;case 19:this.$=a[s-1]+":"+r.count,r.count=0;break;case 20:r.count=0;break;case 21:r.count+=1}},table:[{3:1,4:[1,2]},{1:[3]},{5:[1,3],8:[1,4]},{6:5,7:e,9:6,12:n},{5:[1,8]},{7:[1,9]},t(r,[2,7],{10:10,11:[1,11]}),t(i,[2,6]),{6:12,7:e,9:6,12:n},{1:[2,1]},{7:[2,4],12:[1,15],13:13,14:14,15:[1,16],17:[1,17],19:[1,18],20:[1,19],21:[1,20]},t(i,[2,5]),{7:[1,21]},t(r,[2,8]),{12:[1,22]},t(r,[2,10]),{12:[2,16],16:23,23:[1,24]},{18:[1,25]},{18:[1,26]},{18:[1,27]},{18:[1,30],22:28,24:[1,29]},{1:[2,2]},t(r,[2,9]),{12:[2,11]},{12:[2,17]},{12:[2,12]},{12:[2,13]},{12:[2,14]},{12:[2,15]},{12:a,25:31,26:o},{12:a,25:33,26:o},{12:[2,18]},{12:a,25:34,26:o},{12:[2,19]},{12:[2,21]}],defaultActions:{9:[2,1],21:[2,2],23:[2,11],24:[2,17],25:[2,12],26:[2,13],27:[2,14],28:[2,15],31:[2,18],33:[2,19],34:[2,21]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},c={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 12;case 1:case 2:case 3:break;case 4:return 4;case 5:return 15;case 6:return 17;case 7:return 20;case 8:return 21;case 9:return 19;case 10:case 11:return 8;case 12:return 5;case 13:return 26;case 14:this.begin("options");break;case 15:case 18:this.popState();break;case 16:return 11;case 17:this.begin("string");break;case 19:return 23;case 20:return 18;case 21:return 7}},rules:[/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gitGraph\b)/i,/^(?:commit\b)/i,/^(?:branch\b)/i,/^(?:merge\b)/i,/^(?:reset\b)/i,/^(?:checkout\b)/i,/^(?:LR\b)/i,/^(?:BT\b)/i,/^(?::)/i,/^(?:\^)/i,/^(?:options\r?\n)/i,/^(?:end\r?\n)/i,/^(?:[^\n]+\r?\n)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[a-zA-Z][-_\.a-zA-Z0-9]*[-_a-zA-Z0-9])/i,/^(?:$)/i],conditions:{options:{rules:[15,16],inclusive:!1},string:{rules:[18,19],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,20,21],inclusive:!0}}};function u(){this.yy={}}return s.lexer=c,u.prototype=s,s.Parser=u,new u}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8183).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},6765:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[6,9,10],n={trace:function(){},yy:{},symbols_:{error:2,start:3,info:4,document:5,EOF:6,line:7,statement:8,NL:9,showInfo:10,$accept:0,$end:1},terminals_:{2:"error",4:"info",6:"EOF",9:"NL",10:"showInfo"},productions_:[0,[3,3],[5,0],[5,2],[7,1],[7,1],[8,1]],performAction:function(t,e,n,r,i,a,o){switch(a.length,i){case 1:return r;case 4:break;case 6:r.setInfo(!0)}},table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:6,9:[1,7],10:[1,8]},{1:[2,1]},t(e,[2,3]),t(e,[2,4]),t(e,[2,5]),t(e,[2,6])],defaultActions:{4:[2,1]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},r={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 4;case 1:return 9;case 2:return"space";case 3:return 10;case 4:return 6;case 5:return"TXT"}},rules:[/^(?:info\b)/i,/^(?:[\s\n\r]+)/i,/^(?:[\s]+)/i,/^(?:showInfo\b)/i,/^(?:$)/i,/^(?:.)/i],conditions:{INITIAL:{rules:[0,1,2,3,4,5],inclusive:!0}}};function i(){this.yy={}}return n.lexer=r,i.prototype=n,n.Parser=i,new i}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(1428).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},7062:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,4],n=[1,5],r=[1,6],i=[1,7],a=[1,9],o=[1,11,13,20,21,22,23],s=[2,5],c=[1,6,11,13,20,21,22,23],u=[20,21,22],l=[2,8],h=[1,18],f=[1,19],d=[1,24],p=[6,20,21,22,23],y={trace:function(){},yy:{},symbols_:{error:2,start:3,eol:4,directive:5,PIE:6,document:7,showData:8,line:9,statement:10,txt:11,value:12,title:13,title_value:14,openDirective:15,typeDirective:16,closeDirective:17,":":18,argDirective:19,NEWLINE:20,";":21,EOF:22,open_directive:23,type_directive:24,arg_directive:25,close_directive:26,$accept:0,$end:1},terminals_:{2:"error",6:"PIE",8:"showData",11:"txt",12:"value",13:"title",14:"title_value",18:":",20:"NEWLINE",21:";",22:"EOF",23:"open_directive",24:"type_directive",25:"arg_directive",26:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,3],[7,0],[7,2],[9,2],[10,0],[10,2],[10,2],[10,1],[5,3],[5,5],[4,1],[4,1],[4,1],[15,1],[16,1],[19,1],[17,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:r.setShowData(!0);break;case 7:this.$=a[s-1];break;case 9:r.addSection(a[s-1],r.cleanupValue(a[s]));break;case 10:this.$=a[s].trim(),r.setTitle(this.$);break;case 17:r.parseDirective("%%{","open_directive");break;case 18:r.parseDirective(a[s],"type_directive");break;case 19:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 20:r.parseDirective("}%%","close_directive","pie")}},table:[{3:1,4:2,5:3,6:e,15:8,20:n,21:r,22:i,23:a},{1:[3]},{3:10,4:2,5:3,6:e,15:8,20:n,21:r,22:i,23:a},{3:11,4:2,5:3,6:e,15:8,20:n,21:r,22:i,23:a},t(o,s,{7:12,8:[1,13]}),t(c,[2,14]),t(c,[2,15]),t(c,[2,16]),{16:14,24:[1,15]},{24:[2,17]},{1:[2,1]},{1:[2,2]},t(u,l,{15:8,9:16,10:17,5:20,1:[2,3],11:h,13:f,23:a}),t(o,s,{7:21}),{17:22,18:[1,23],26:d},t([18,26],[2,18]),t(o,[2,6]),{4:25,20:n,21:r,22:i},{12:[1,26]},{14:[1,27]},t(u,[2,11]),t(u,l,{15:8,9:16,10:17,5:20,1:[2,4],11:h,13:f,23:a}),t(p,[2,12]),{19:28,25:[1,29]},t(p,[2,20]),t(o,[2,7]),t(u,[2,9]),t(u,[2,10]),{17:30,26:d},{26:[2,19]},t(p,[2,13])],defaultActions:{9:[2,17],10:[2,1],11:[2,2],29:[2,19]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},g={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),23;case 1:return this.begin("type_directive"),24;case 2:return this.popState(),this.begin("arg_directive"),18;case 3:return this.popState(),this.popState(),26;case 4:return 25;case 5:case 6:case 8:case 9:break;case 7:return 20;case 10:return this.begin("title"),13;case 11:return this.popState(),"title_value";case 12:this.begin("string");break;case 13:this.popState();break;case 14:return"txt";case 15:return 6;case 16:return 8;case 17:return"value";case 18:return 22}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:[\s]+)/i,/^(?:title\b)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:pie\b)/i,/^(?:showData\b)/i,/^(?::[\s]*[\d]+(?:\.[\d]+)?)/i,/^(?:$)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},title:{rules:[11],inclusive:!1},string:{rules:[13,14],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,12,15,16,17,18],inclusive:!0}}};function m(){this.yy={}}return y.lexer=g,m.prototype=y,y.Parser=m,new m}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(4551).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},3176:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,3],n=[1,5],r=[1,17],i=[2,10],a=[1,21],o=[1,22],s=[1,23],c=[1,24],u=[1,25],l=[1,26],h=[1,19],f=[1,27],d=[1,28],p=[1,31],y=[66,67],g=[5,8,14,35,36,37,38,39,40,48,55,57,66,67],m=[5,6,8,14,35,36,37,38,39,40,48,66,67],v=[1,51],b=[1,52],_=[1,53],x=[1,54],w=[1,55],k=[1,56],T=[1,57],E=[57,58],C=[1,69],S=[1,65],A=[1,66],M=[1,67],N=[1,68],D=[1,70],O=[1,74],B=[1,75],L=[1,72],I=[1,73],R=[5,8,14,35,36,37,38,39,40,48,66,67],F={trace:function(){},yy:{},symbols_:{error:2,start:3,directive:4,NEWLINE:5,RD:6,diagram:7,EOF:8,openDirective:9,typeDirective:10,closeDirective:11,":":12,argDirective:13,open_directive:14,type_directive:15,arg_directive:16,close_directive:17,requirementDef:18,elementDef:19,relationshipDef:20,requirementType:21,requirementName:22,STRUCT_START:23,requirementBody:24,ID:25,COLONSEP:26,id:27,TEXT:28,text:29,RISK:30,riskLevel:31,VERIFYMTHD:32,verifyType:33,STRUCT_STOP:34,REQUIREMENT:35,FUNCTIONAL_REQUIREMENT:36,INTERFACE_REQUIREMENT:37,PERFORMANCE_REQUIREMENT:38,PHYSICAL_REQUIREMENT:39,DESIGN_CONSTRAINT:40,LOW_RISK:41,MED_RISK:42,HIGH_RISK:43,VERIFY_ANALYSIS:44,VERIFY_DEMONSTRATION:45,VERIFY_INSPECTION:46,VERIFY_TEST:47,ELEMENT:48,elementName:49,elementBody:50,TYPE:51,type:52,DOCREF:53,ref:54,END_ARROW_L:55,relationship:56,LINE:57,END_ARROW_R:58,CONTAINS:59,COPIES:60,DERIVES:61,SATISFIES:62,VERIFIES:63,REFINES:64,TRACES:65,unqString:66,qString:67,$accept:0,$end:1},terminals_:{2:"error",5:"NEWLINE",6:"RD",8:"EOF",12:":",14:"open_directive",15:"type_directive",16:"arg_directive",17:"close_directive",23:"STRUCT_START",25:"ID",26:"COLONSEP",28:"TEXT",30:"RISK",32:"VERIFYMTHD",34:"STRUCT_STOP",35:"REQUIREMENT",36:"FUNCTIONAL_REQUIREMENT",37:"INTERFACE_REQUIREMENT",38:"PERFORMANCE_REQUIREMENT",39:"PHYSICAL_REQUIREMENT",40:"DESIGN_CONSTRAINT",41:"LOW_RISK",42:"MED_RISK",43:"HIGH_RISK",44:"VERIFY_ANALYSIS",45:"VERIFY_DEMONSTRATION",46:"VERIFY_INSPECTION",47:"VERIFY_TEST",48:"ELEMENT",51:"TYPE",53:"DOCREF",55:"END_ARROW_L",57:"LINE",58:"END_ARROW_R",59:"CONTAINS",60:"COPIES",61:"DERIVES",62:"SATISFIES",63:"VERIFIES",64:"REFINES",65:"TRACES",66:"unqString",67:"qString"},productions_:[0,[3,3],[3,2],[3,4],[4,3],[4,5],[9,1],[10,1],[13,1],[11,1],[7,0],[7,2],[7,2],[7,2],[7,2],[7,2],[18,5],[24,5],[24,5],[24,5],[24,5],[24,2],[24,1],[21,1],[21,1],[21,1],[21,1],[21,1],[21,1],[31,1],[31,1],[31,1],[33,1],[33,1],[33,1],[33,1],[19,5],[50,5],[50,5],[50,2],[50,1],[20,5],[20,5],[56,1],[56,1],[56,1],[56,1],[56,1],[56,1],[56,1],[22,1],[22,1],[27,1],[27,1],[29,1],[29,1],[49,1],[49,1],[52,1],[52,1],[54,1],[54,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 6:r.parseDirective("%%{","open_directive");break;case 7:r.parseDirective(a[s],"type_directive");break;case 8:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 9:r.parseDirective("}%%","close_directive","pie");break;case 10:this.$=[];break;case 16:r.addRequirement(a[s-3],a[s-4]);break;case 17:r.setNewReqId(a[s-2]);break;case 18:r.setNewReqText(a[s-2]);break;case 19:r.setNewReqRisk(a[s-2]);break;case 20:r.setNewReqVerifyMethod(a[s-2]);break;case 23:this.$=r.RequirementType.REQUIREMENT;break;case 24:this.$=r.RequirementType.FUNCTIONAL_REQUIREMENT;break;case 25:this.$=r.RequirementType.INTERFACE_REQUIREMENT;break;case 26:this.$=r.RequirementType.PERFORMANCE_REQUIREMENT;break;case 27:this.$=r.RequirementType.PHYSICAL_REQUIREMENT;break;case 28:this.$=r.RequirementType.DESIGN_CONSTRAINT;break;case 29:this.$=r.RiskLevel.LOW_RISK;break;case 30:this.$=r.RiskLevel.MED_RISK;break;case 31:this.$=r.RiskLevel.HIGH_RISK;break;case 32:this.$=r.VerifyType.VERIFY_ANALYSIS;break;case 33:this.$=r.VerifyType.VERIFY_DEMONSTRATION;break;case 34:this.$=r.VerifyType.VERIFY_INSPECTION;break;case 35:this.$=r.VerifyType.VERIFY_TEST;break;case 36:r.addElement(a[s-3]);break;case 37:r.setNewElementType(a[s-2]);break;case 38:r.setNewElementDocRef(a[s-2]);break;case 41:r.addRelationship(a[s-2],a[s],a[s-4]);break;case 42:r.addRelationship(a[s-2],a[s-4],a[s]);break;case 43:this.$=r.Relationships.CONTAINS;break;case 44:this.$=r.Relationships.COPIES;break;case 45:this.$=r.Relationships.DERIVES;break;case 46:this.$=r.Relationships.SATISFIES;break;case 47:this.$=r.Relationships.VERIFIES;break;case 48:this.$=r.Relationships.REFINES;break;case 49:this.$=r.Relationships.TRACES}},table:[{3:1,4:2,6:e,9:4,14:n},{1:[3]},{3:7,4:2,5:[1,6],6:e,9:4,14:n},{5:[1,8]},{10:9,15:[1,10]},{15:[2,6]},{3:11,4:2,6:e,9:4,14:n},{1:[2,2]},{4:16,5:r,7:12,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{11:29,12:[1,30],17:p},t([12,17],[2,7]),{1:[2,1]},{8:[1,32]},{4:16,5:r,7:33,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:34,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:35,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:36,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{4:16,5:r,7:37,8:i,9:4,14:n,18:13,19:14,20:15,21:18,27:20,35:a,36:o,37:s,38:c,39:u,40:l,48:h,66:f,67:d},{22:38,66:[1,39],67:[1,40]},{49:41,66:[1,42],67:[1,43]},{55:[1,44],57:[1,45]},t(y,[2,23]),t(y,[2,24]),t(y,[2,25]),t(y,[2,26]),t(y,[2,27]),t(y,[2,28]),t(g,[2,52]),t(g,[2,53]),t(m,[2,4]),{13:46,16:[1,47]},t(m,[2,9]),{1:[2,3]},{8:[2,11]},{8:[2,12]},{8:[2,13]},{8:[2,14]},{8:[2,15]},{23:[1,48]},{23:[2,50]},{23:[2,51]},{23:[1,49]},{23:[2,56]},{23:[2,57]},{56:50,59:v,60:b,61:_,62:x,63:w,64:k,65:T},{56:58,59:v,60:b,61:_,62:x,63:w,64:k,65:T},{11:59,17:p},{17:[2,8]},{5:[1,60]},{5:[1,61]},{57:[1,62]},t(E,[2,43]),t(E,[2,44]),t(E,[2,45]),t(E,[2,46]),t(E,[2,47]),t(E,[2,48]),t(E,[2,49]),{58:[1,63]},t(m,[2,5]),{5:C,24:64,25:S,28:A,30:M,32:N,34:D},{5:O,34:B,50:71,51:L,53:I},{27:76,66:f,67:d},{27:77,66:f,67:d},t(R,[2,16]),{26:[1,78]},{26:[1,79]},{26:[1,80]},{26:[1,81]},{5:C,24:82,25:S,28:A,30:M,32:N,34:D},t(R,[2,22]),t(R,[2,36]),{26:[1,83]},{26:[1,84]},{5:O,34:B,50:85,51:L,53:I},t(R,[2,40]),t(R,[2,41]),t(R,[2,42]),{27:86,66:f,67:d},{29:87,66:[1,88],67:[1,89]},{31:90,41:[1,91],42:[1,92],43:[1,93]},{33:94,44:[1,95],45:[1,96],46:[1,97],47:[1,98]},t(R,[2,21]),{52:99,66:[1,100],67:[1,101]},{54:102,66:[1,103],67:[1,104]},t(R,[2,39]),{5:[1,105]},{5:[1,106]},{5:[2,54]},{5:[2,55]},{5:[1,107]},{5:[2,29]},{5:[2,30]},{5:[2,31]},{5:[1,108]},{5:[2,32]},{5:[2,33]},{5:[2,34]},{5:[2,35]},{5:[1,109]},{5:[2,58]},{5:[2,59]},{5:[1,110]},{5:[2,60]},{5:[2,61]},{5:C,24:111,25:S,28:A,30:M,32:N,34:D},{5:C,24:112,25:S,28:A,30:M,32:N,34:D},{5:C,24:113,25:S,28:A,30:M,32:N,34:D},{5:C,24:114,25:S,28:A,30:M,32:N,34:D},{5:O,34:B,50:115,51:L,53:I},{5:O,34:B,50:116,51:L,53:I},t(R,[2,17]),t(R,[2,18]),t(R,[2,19]),t(R,[2,20]),t(R,[2,37]),t(R,[2,38])],defaultActions:{5:[2,6],7:[2,2],11:[2,1],32:[2,3],33:[2,11],34:[2,12],35:[2,13],36:[2,14],37:[2,15],39:[2,50],40:[2,51],42:[2,56],43:[2,57],47:[2,8],88:[2,54],89:[2,55],91:[2,29],92:[2,30],93:[2,31],95:[2,32],96:[2,33],97:[2,34],98:[2,35],100:[2,58],101:[2,59],103:[2,60],104:[2,61]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},P={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),14;case 1:return this.begin("type_directive"),15;case 2:return this.popState(),this.begin("arg_directive"),12;case 3:return this.popState(),this.popState(),17;case 4:return 16;case 5:return 5;case 6:case 7:case 8:break;case 9:return 8;case 10:return 6;case 11:return 23;case 12:return 34;case 13:return 26;case 14:return 25;case 15:return 28;case 16:return 30;case 17:return 32;case 18:return 35;case 19:return 36;case 20:return 37;case 21:return 38;case 22:return 39;case 23:return 40;case 24:return 41;case 25:return 42;case 26:return 43;case 27:return 44;case 28:return 45;case 29:return 46;case 30:return 47;case 31:return 48;case 32:return 59;case 33:return 60;case 34:return 61;case 35:return 62;case 36:return 63;case 37:return 64;case 38:return 65;case 39:return 51;case 40:return 53;case 41:return 55;case 42:return 58;case 43:return 57;case 44:this.begin("string");break;case 45:this.popState();break;case 46:return"qString";case 47:return e.yytext=e.yytext.trim(),66}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:$)/i,/^(?:requirementDiagram\b)/i,/^(?:\{)/i,/^(?:\})/i,/^(?::)/i,/^(?:id\b)/i,/^(?:text\b)/i,/^(?:risk\b)/i,/^(?:verifyMethod\b)/i,/^(?:requirement\b)/i,/^(?:functionalRequirement\b)/i,/^(?:interfaceRequirement\b)/i,/^(?:performanceRequirement\b)/i,/^(?:physicalRequirement\b)/i,/^(?:designConstraint\b)/i,/^(?:low\b)/i,/^(?:medium\b)/i,/^(?:high\b)/i,/^(?:analysis\b)/i,/^(?:demonstration\b)/i,/^(?:inspection\b)/i,/^(?:test\b)/i,/^(?:element\b)/i,/^(?:contains\b)/i,/^(?:copies\b)/i,/^(?:derives\b)/i,/^(?:satisfies\b)/i,/^(?:verifies\b)/i,/^(?:refines\b)/i,/^(?:traces\b)/i,/^(?:type\b)/i,/^(?:docref\b)/i,/^(?:<-)/i,/^(?:->)/i,/^(?:-)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[\w][^\r\n\{\<\>\-\=]*)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},unqString:{rules:[],inclusive:!1},token:{rules:[],inclusive:!1},string:{rules:[45,46],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,47],inclusive:!0}}};function j(){this.yy={}}return F.lexer=P,j.prototype=F,F.Parser=j,new j}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(8800).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},6876:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,18],u=[1,19],l=[1,21],h=[1,22],f=[1,23],d=[1,29],p=[1,30],y=[1,31],g=[1,32],m=[1,33],v=[1,34],b=[1,37],_=[1,38],x=[1,39],w=[1,40],k=[1,41],T=[1,42],E=[1,45],C=[1,4,5,16,20,22,23,24,30,32,33,34,35,36,38,40,41,42,46,47,48,49,57,67],S=[1,58],A=[4,5,16,20,22,23,24,30,32,33,34,35,36,38,42,46,47,48,49,57,67],M=[4,5,16,20,22,23,24,30,32,33,34,35,36,38,41,42,46,47,48,49,57,67],N=[4,5,16,20,22,23,24,30,32,33,34,35,36,38,40,42,46,47,48,49,57,67],D=[55,56,57],O=[1,4,5,7,16,20,22,23,24,30,32,33,34,35,36,38,40,41,42,46,47,48,49,57,67],B={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NEWLINE:5,directive:6,SD:7,document:8,line:9,statement:10,openDirective:11,typeDirective:12,closeDirective:13,":":14,argDirective:15,participant:16,actor:17,AS:18,restOfLine:19,participant_actor:20,signal:21,autonumber:22,activate:23,deactivate:24,note_statement:25,links_statement:26,link_statement:27,properties_statement:28,details_statement:29,title:30,text2:31,loop:32,end:33,rect:34,opt:35,alt:36,else_sections:37,par:38,par_sections:39,and:40,else:41,note:42,placement:43,over:44,actor_pair:45,links:46,link:47,properties:48,details:49,spaceList:50,",":51,left_of:52,right_of:53,signaltype:54,"+":55,"-":56,ACTOR:57,SOLID_OPEN_ARROW:58,DOTTED_OPEN_ARROW:59,SOLID_ARROW:60,DOTTED_ARROW:61,SOLID_CROSS:62,DOTTED_CROSS:63,SOLID_POINT:64,DOTTED_POINT:65,TXT:66,open_directive:67,type_directive:68,arg_directive:69,close_directive:70,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NEWLINE",7:"SD",14:":",16:"participant",18:"AS",19:"restOfLine",20:"participant_actor",22:"autonumber",23:"activate",24:"deactivate",30:"title",32:"loop",33:"end",34:"rect",35:"opt",36:"alt",38:"par",40:"and",41:"else",42:"note",44:"over",46:"links",47:"link",48:"properties",49:"details",51:",",52:"left_of",53:"right_of",55:"+",56:"-",57:"ACTOR",58:"SOLID_OPEN_ARROW",59:"DOTTED_OPEN_ARROW",60:"SOLID_ARROW",61:"DOTTED_ARROW",62:"SOLID_CROSS",63:"DOTTED_CROSS",64:"SOLID_POINT",65:"DOTTED_POINT",66:"TXT",67:"open_directive",68:"type_directive",69:"arg_directive",70:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[6,4],[6,6],[10,5],[10,3],[10,5],[10,3],[10,2],[10,1],[10,3],[10,3],[10,2],[10,2],[10,2],[10,2],[10,2],[10,3],[10,4],[10,4],[10,4],[10,4],[10,4],[10,1],[39,1],[39,4],[37,1],[37,4],[25,4],[25,4],[26,3],[27,3],[28,3],[29,3],[50,2],[50,1],[45,3],[45,1],[43,1],[43,1],[21,5],[21,5],[21,4],[17,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[31,1],[11,1],[12,1],[15,1],[13,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.apply(a[s]),a[s];case 5:case 9:this.$=[];break;case 6:a[s-1].push(a[s]),this.$=a[s-1];break;case 7:case 8:case 45:this.$=a[s];break;case 12:a[s-3].type="addParticipant",a[s-3].description=r.parseMessage(a[s-1]),this.$=a[s-3];break;case 13:a[s-1].type="addParticipant",this.$=a[s-1];break;case 14:a[s-3].type="addActor",a[s-3].description=r.parseMessage(a[s-1]),this.$=a[s-3];break;case 15:a[s-1].type="addActor",this.$=a[s-1];break;case 17:r.enableSequenceNumbers();break;case 18:this.$={type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]};break;case 19:this.$={type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-1]};break;case 25:this.$=[{type:"setTitle",text:a[s-1]}];break;case 26:a[s-1].unshift({type:"loopStart",loopText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.LOOP_START}),a[s-1].push({type:"loopEnd",loopText:a[s-2],signalType:r.LINETYPE.LOOP_END}),this.$=a[s-1];break;case 27:a[s-1].unshift({type:"rectStart",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_START}),a[s-1].push({type:"rectEnd",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_END}),this.$=a[s-1];break;case 28:a[s-1].unshift({type:"optStart",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_START}),a[s-1].push({type:"optEnd",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_END}),this.$=a[s-1];break;case 29:a[s-1].unshift({type:"altStart",altText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.ALT_START}),a[s-1].push({type:"altEnd",signalType:r.LINETYPE.ALT_END}),this.$=a[s-1];break;case 30:a[s-1].unshift({type:"parStart",parText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.PAR_START}),a[s-1].push({type:"parEnd",signalType:r.LINETYPE.PAR_END}),this.$=a[s-1];break;case 33:this.$=a[s-3].concat([{type:"and",parText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.PAR_AND},a[s]]);break;case 35:this.$=a[s-3].concat([{type:"else",altText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.ALT_ELSE},a[s]]);break;case 36:this.$=[a[s-1],{type:"addNote",placement:a[s-2],actor:a[s-1].actor,text:a[s]}];break;case 37:a[s-2]=[].concat(a[s-1],a[s-1]).slice(0,2),a[s-2][0]=a[s-2][0].actor,a[s-2][1]=a[s-2][1].actor,this.$=[a[s-1],{type:"addNote",placement:r.PLACEMENT.OVER,actor:a[s-2].slice(0,2),text:a[s]}];break;case 38:this.$=[a[s-1],{type:"addLinks",actor:a[s-1].actor,text:a[s]}];break;case 39:this.$=[a[s-1],{type:"addALink",actor:a[s-1].actor,text:a[s]}];break;case 40:this.$=[a[s-1],{type:"addProperties",actor:a[s-1].actor,text:a[s]}];break;case 41:this.$=[a[s-1],{type:"addDetails",actor:a[s-1].actor,text:a[s]}];break;case 44:this.$=[a[s-2],a[s]];break;case 46:this.$=r.PLACEMENT.LEFTOF;break;case 47:this.$=r.PLACEMENT.RIGHTOF;break;case 48:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]}];break;case 49:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-4]}];break;case 50:this.$=[a[s-3],a[s-1],{type:"addMessage",from:a[s-3].actor,to:a[s-1].actor,signalType:a[s-2],msg:a[s]}];break;case 51:this.$={type:"addParticipant",actor:a[s]};break;case 52:this.$=r.LINETYPE.SOLID_OPEN;break;case 53:this.$=r.LINETYPE.DOTTED_OPEN;break;case 54:this.$=r.LINETYPE.SOLID;break;case 55:this.$=r.LINETYPE.DOTTED;break;case 56:this.$=r.LINETYPE.SOLID_CROSS;break;case 57:this.$=r.LINETYPE.DOTTED_CROSS;break;case 58:this.$=r.LINETYPE.SOLID_POINT;break;case 59:this.$=r.LINETYPE.DOTTED_POINT;break;case 60:this.$=r.parseMessage(a[s].trim().substring(1));break;case 61:r.parseDirective("%%{","open_directive");break;case 62:r.parseDirective(a[s],"type_directive");break;case 63:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 64:r.parseDirective("}%%","close_directive","sequence")}},table:[{3:1,4:e,5:n,6:4,7:r,11:6,67:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,11:6,67:i},{3:9,4:e,5:n,6:4,7:r,11:6,67:i},{3:10,4:e,5:n,6:4,7:r,11:6,67:i},t([1,4,5,16,20,22,23,24,30,32,34,35,36,38,42,46,47,48,49,57,67],a,{8:11}),{12:12,68:[1,13]},{68:[2,61]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{13:43,14:[1,44],70:E},t([14,70],[2,62]),t(C,[2,6]),{6:35,10:46,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},t(C,[2,8]),t(C,[2,9]),{17:47,57:T},{17:48,57:T},{5:[1,49]},t(C,[2,17]),{17:50,57:T},{17:51,57:T},{5:[1,52]},{5:[1,53]},{5:[1,54]},{5:[1,55]},{5:[1,56]},{31:57,66:S},{19:[1,59]},{19:[1,60]},{19:[1,61]},{19:[1,62]},{19:[1,63]},t(C,[2,31]),{54:64,58:[1,65],59:[1,66],60:[1,67],61:[1,68],62:[1,69],63:[1,70],64:[1,71],65:[1,72]},{43:73,44:[1,74],52:[1,75],53:[1,76]},{17:77,57:T},{17:78,57:T},{17:79,57:T},{17:80,57:T},t([5,18,51,58,59,60,61,62,63,64,65,66],[2,51]),{5:[1,81]},{15:82,69:[1,83]},{5:[2,64]},t(C,[2,7]),{5:[1,85],18:[1,84]},{5:[1,87],18:[1,86]},t(C,[2,16]),{5:[1,88]},{5:[1,89]},t(C,[2,20]),t(C,[2,21]),t(C,[2,22]),t(C,[2,23]),t(C,[2,24]),{5:[1,90]},{5:[2,60]},t(A,a,{8:91}),t(A,a,{8:92}),t(A,a,{8:93}),t(M,a,{37:94,8:95}),t(N,a,{39:96,8:97}),{17:100,55:[1,98],56:[1,99],57:T},t(D,[2,52]),t(D,[2,53]),t(D,[2,54]),t(D,[2,55]),t(D,[2,56]),t(D,[2,57]),t(D,[2,58]),t(D,[2,59]),{17:101,57:T},{17:103,45:102,57:T},{57:[2,46]},{57:[2,47]},{31:104,66:S},{31:105,66:S},{31:106,66:S},{31:107,66:S},t(O,[2,10]),{13:108,70:E},{70:[2,63]},{19:[1,109]},t(C,[2,13]),{19:[1,110]},t(C,[2,15]),t(C,[2,18]),t(C,[2,19]),t(C,[2,25]),{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[1,111],34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[1,112],34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[1,113],34:y,35:g,36:m,38:v,42:b,46:_,47:x,48:w,49:k,57:T,67:i},{33:[1,114]},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[2,34],34:y,35:g,36:m,38:v,41:[1,115],42:b,46:_,47:x,48:w,49:k,57:T,67:i},{33:[1,116]},{4:o,5:s,6:35,9:14,10:16,11:6,16:c,17:36,20:u,21:20,22:l,23:h,24:f,25:24,26:25,27:26,28:27,29:28,30:d,32:p,33:[2,32],34:y,35:g,36:m,38:v,40:[1,117],42:b,46:_,47:x,48:w,49:k,57:T,67:i},{17:118,57:T},{17:119,57:T},{31:120,66:S},{31:121,66:S},{31:122,66:S},{51:[1,123],66:[2,45]},{5:[2,38]},{5:[2,39]},{5:[2,40]},{5:[2,41]},{5:[1,124]},{5:[1,125]},{5:[1,126]},t(C,[2,26]),t(C,[2,27]),t(C,[2,28]),t(C,[2,29]),{19:[1,127]},t(C,[2,30]),{19:[1,128]},{31:129,66:S},{31:130,66:S},{5:[2,50]},{5:[2,36]},{5:[2,37]},{17:131,57:T},t(O,[2,11]),t(C,[2,12]),t(C,[2,14]),t(M,a,{8:95,37:132}),t(N,a,{8:97,39:133}),{5:[2,48]},{5:[2,49]},{66:[2,44]},{33:[2,35]},{33:[2,33]}],defaultActions:{7:[2,61],8:[2,1],9:[2,2],10:[2,3],45:[2,64],58:[2,60],75:[2,46],76:[2,47],83:[2,63],104:[2,38],105:[2,39],106:[2,40],107:[2,41],120:[2,50],121:[2,36],122:[2,37],129:[2,48],130:[2,49],131:[2,44],132:[2,35],133:[2,33]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},L={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),67;case 1:return this.begin("type_directive"),68;case 2:return this.popState(),this.begin("arg_directive"),14;case 3:return this.popState(),this.popState(),70;case 4:return 69;case 5:case 39:case 52:return 5;case 6:case 7:case 8:case 9:case 10:break;case 11:return this.begin("ID"),16;case 12:return this.begin("ID"),20;case 13:return e.yytext=e.yytext.trim(),this.begin("ALIAS"),57;case 14:return this.popState(),this.popState(),this.begin("LINE"),18;case 15:return this.popState(),this.popState(),5;case 16:return this.begin("LINE"),32;case 17:return this.begin("LINE"),34;case 18:return this.begin("LINE"),35;case 19:return this.begin("LINE"),36;case 20:return this.begin("LINE"),41;case 21:return this.begin("LINE"),38;case 22:return this.begin("LINE"),40;case 23:return this.popState(),19;case 24:return 33;case 25:return 52;case 26:return 53;case 27:return 46;case 28:return 47;case 29:return 48;case 30:return 49;case 31:return 44;case 32:return 42;case 33:return this.begin("ID"),23;case 34:return this.begin("ID"),24;case 35:return 30;case 36:return 7;case 37:return 22;case 38:return 51;case 40:return e.yytext=e.yytext.trim(),57;case 41:return 60;case 42:return 61;case 43:return 58;case 44:return 59;case 45:return 62;case 46:return 63;case 47:return 64;case 48:return 65;case 49:return 66;case 50:return 55;case 51:return 56;case 53:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:participant\b)/i,/^(?:actor\b)/i,/^(?:[^\->:\n,;]+?(?=((?!\n)\s)+as(?!\n)\s|[#\n;]|$))/i,/^(?:as\b)/i,/^(?:(?:))/i,/^(?:loop\b)/i,/^(?:rect\b)/i,/^(?:opt\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:par\b)/i,/^(?:and\b)/i,/^(?:(?:[:]?(?:no)?wrap)?[^#\n;]*)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:links\b)/i,/^(?:link\b)/i,/^(?:properties\b)/i,/^(?:details\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:activate\b)/i,/^(?:deactivate\b)/i,/^(?:title\b)/i,/^(?:sequenceDiagram\b)/i,/^(?:autonumber\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\+\->:\n,;]+((?!(-x|--x|-\)|--\)))[\-]*[^\+\->:\n,;]+)*)/i,/^(?:->>)/i,/^(?:-->>)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:-[x])/i,/^(?:--[x])/i,/^(?:-[\)])/i,/^(?:--[\)])/i,/^(?::(?:(?:no)?wrap)?[^#\n;]+)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1,8],inclusive:!1},type_directive:{rules:[2,3,8],inclusive:!1},arg_directive:{rules:[3,4,8],inclusive:!1},ID:{rules:[7,8,13],inclusive:!1},ALIAS:{rules:[7,8,14,15],inclusive:!1},LINE:{rules:[7,8,23],inclusive:!1},INITIAL:{rules:[0,5,6,8,9,10,11,12,16,17,18,19,20,21,22,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53],inclusive:!0}}};function I(){this.yy={}}return B.lexer=L,I.prototype=B,B.Parser=I,new I}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(1993).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},3584:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,19],u=[1,20],l=[1,21],h=[1,22],f=[1,30],d=[1,23],p=[1,24],y=[1,25],g=[1,26],m=[1,27],v=[1,32],b=[1,33],_=[1,34],x=[1,35],w=[1,31],k=[1,38],T=[1,4,5,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],E=[1,4,5,12,13,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],C=[1,4,5,7,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],S=[4,5,14,15,17,19,20,22,23,24,25,26,27,36,37,38,39,42,45],A={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NL:5,directive:6,SD:7,document:8,line:9,statement:10,idStatement:11,DESCR:12,"--\x3e":13,HIDE_EMPTY:14,scale:15,WIDTH:16,COMPOSIT_STATE:17,STRUCT_START:18,STRUCT_STOP:19,STATE_DESCR:20,AS:21,ID:22,FORK:23,JOIN:24,CHOICE:25,CONCURRENT:26,note:27,notePosition:28,NOTE_TEXT:29,direction:30,openDirective:31,typeDirective:32,closeDirective:33,":":34,argDirective:35,direction_tb:36,direction_bt:37,direction_rl:38,direction_lr:39,eol:40,";":41,EDGE_STATE:42,left_of:43,right_of:44,open_directive:45,type_directive:46,arg_directive:47,close_directive:48,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NL",7:"SD",12:"DESCR",13:"--\x3e",14:"HIDE_EMPTY",15:"scale",16:"WIDTH",17:"COMPOSIT_STATE",18:"STRUCT_START",19:"STRUCT_STOP",20:"STATE_DESCR",21:"AS",22:"ID",23:"FORK",24:"JOIN",25:"CHOICE",26:"CONCURRENT",27:"note",29:"NOTE_TEXT",34:":",36:"direction_tb",37:"direction_bt",38:"direction_rl",39:"direction_lr",41:";",42:"EDGE_STATE",43:"left_of",44:"right_of",45:"open_directive",46:"type_directive",47:"arg_directive",48:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[10,1],[10,2],[10,3],[10,4],[10,1],[10,2],[10,1],[10,4],[10,3],[10,6],[10,1],[10,1],[10,1],[10,1],[10,4],[10,4],[10,1],[10,1],[6,3],[6,5],[30,1],[30,1],[30,1],[30,1],[40,1],[40,1],[11,1],[11,1],[28,1],[28,1],[31,1],[32,1],[35,1],[33,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.setRootDoc(a[s]),a[s];case 5:this.$=[];break;case 6:"nl"!=a[s]&&(a[s-1].push(a[s]),this.$=a[s-1]);break;case 7:case 8:case 36:case 37:this.$=a[s];break;case 9:this.$="nl";break;case 10:this.$={stmt:"state",id:a[s],type:"default",description:""};break;case 11:this.$={stmt:"state",id:a[s-1],type:"default",description:r.trimColon(a[s])};break;case 12:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-2],type:"default",description:""},state2:{stmt:"state",id:a[s],type:"default",description:""}};break;case 13:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-3],type:"default",description:""},state2:{stmt:"state",id:a[s-1],type:"default",description:""},description:a[s].substr(1).trim()};break;case 17:this.$={stmt:"state",id:a[s-3],type:"default",description:"",doc:a[s-1]};break;case 18:var c=a[s],u=a[s-2].trim();if(a[s].match(":")){var l=a[s].split(":");c=l[0],u=[u,l[1]]}this.$={stmt:"state",id:c,type:"default",description:u};break;case 19:this.$={stmt:"state",id:a[s-3],type:"default",description:a[s-5],doc:a[s-1]};break;case 20:this.$={stmt:"state",id:a[s],type:"fork"};break;case 21:this.$={stmt:"state",id:a[s],type:"join"};break;case 22:this.$={stmt:"state",id:a[s],type:"choice"};break;case 23:this.$={stmt:"state",id:r.getDividerId(),type:"divider"};break;case 24:this.$={stmt:"state",id:a[s-1].trim(),note:{position:a[s-2].trim(),text:a[s].trim()}};break;case 30:r.setDirection("TB"),this.$={stmt:"dir",value:"TB"};break;case 31:r.setDirection("BT"),this.$={stmt:"dir",value:"BT"};break;case 32:r.setDirection("RL"),this.$={stmt:"dir",value:"RL"};break;case 33:r.setDirection("LR"),this.$={stmt:"dir",value:"LR"};break;case 40:r.parseDirective("%%{","open_directive");break;case 41:r.parseDirective(a[s],"type_directive");break;case 42:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 43:r.parseDirective("}%%","close_directive","state")}},table:[{3:1,4:e,5:n,6:4,7:r,31:6,45:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,31:6,45:i},{3:9,4:e,5:n,6:4,7:r,31:6,45:i},{3:10,4:e,5:n,6:4,7:r,31:6,45:i},t([1,4,5,14,15,17,20,22,23,24,25,26,27,36,37,38,39,42,45],a,{8:11}),{32:12,46:[1,13]},{46:[2,40]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:28,9:14,10:16,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},{33:36,34:[1,37],48:k},t([34,48],[2,41]),t(T,[2,6]),{6:28,10:39,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},t(T,[2,8]),t(T,[2,9]),t(T,[2,10],{12:[1,40],13:[1,41]}),t(T,[2,14]),{16:[1,42]},t(T,[2,16],{18:[1,43]}),{21:[1,44]},t(T,[2,20]),t(T,[2,21]),t(T,[2,22]),t(T,[2,23]),{28:45,29:[1,46],43:[1,47],44:[1,48]},t(T,[2,26]),t(T,[2,27]),t(E,[2,36]),t(E,[2,37]),t(T,[2,30]),t(T,[2,31]),t(T,[2,32]),t(T,[2,33]),t(C,[2,28]),{35:49,47:[1,50]},t(C,[2,43]),t(T,[2,7]),t(T,[2,11]),{11:51,22:f,42:w},t(T,[2,15]),t(S,a,{8:52}),{22:[1,53]},{22:[1,54]},{21:[1,55]},{22:[2,38]},{22:[2,39]},{33:56,48:k},{48:[2,42]},t(T,[2,12],{12:[1,57]}),{4:o,5:s,6:28,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,58],20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},t(T,[2,18],{18:[1,59]}),{29:[1,60]},{22:[1,61]},t(C,[2,29]),t(T,[2,13]),t(T,[2,17]),t(S,a,{8:62}),t(T,[2,24]),t(T,[2,25]),{4:o,5:s,6:28,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,63],20:h,22:f,23:d,24:p,25:y,26:g,27:m,30:29,31:6,36:v,37:b,38:_,39:x,42:w,45:i},t(T,[2,19])],defaultActions:{7:[2,40],8:[2,1],9:[2,2],10:[2,3],47:[2,38],48:[2,39],50:[2,42]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},M={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:case 26:return 36;case 1:case 27:return 37;case 2:case 28:return 38;case 3:case 29:return 39;case 4:return this.begin("open_directive"),45;case 5:return this.begin("type_directive"),46;case 6:return this.popState(),this.begin("arg_directive"),34;case 7:return this.popState(),this.popState(),48;case 8:return 47;case 9:case 10:case 12:case 13:case 14:case 15:case 39:case 45:break;case 11:case 59:return 5;case 16:return this.pushState("SCALE"),15;case 17:return 16;case 18:case 33:case 36:this.popState();break;case 19:this.pushState("STATE");break;case 20:case 23:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),23;case 21:case 24:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),24;case 22:case 25:return this.popState(),e.yytext=e.yytext.slice(0,-10).trim(),25;case 30:this.begin("STATE_STRING");break;case 31:return this.popState(),this.pushState("STATE_ID"),"AS";case 32:case 47:return this.popState(),"ID";case 34:return"STATE_DESCR";case 35:return 17;case 37:return this.popState(),this.pushState("struct"),18;case 38:return this.popState(),19;case 40:return this.begin("NOTE"),27;case 41:return this.popState(),this.pushState("NOTE_ID"),43;case 42:return this.popState(),this.pushState("NOTE_ID"),44;case 43:this.popState(),this.pushState("FLOATING_NOTE");break;case 44:return this.popState(),this.pushState("FLOATING_NOTE_ID"),"AS";case 46:return"NOTE_TEXT";case 48:return this.popState(),this.pushState("NOTE_TEXT"),22;case 49:return this.popState(),e.yytext=e.yytext.substr(2).trim(),29;case 50:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),29;case 51:case 52:return 7;case 53:return 14;case 54:return 42;case 55:return 22;case 56:return e.yytext=e.yytext.trim(),12;case 57:return 13;case 58:return 26;case 60:return"INVALID"}},rules:[/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:state\s+)/i,/^(?:.*<<fork>>)/i,/^(?:.*<<join>>)/i,/^(?:.*<<choice>>)/i,/^(?:.*\[\[fork\]\])/i,/^(?:.*\[\[join\]\])/i,/^(?:.*\[\[choice\]\])/i,/^(?:.*direction\s+TB[^\n]*)/i,/^(?:.*direction\s+BT[^\n]*)/i,/^(?:.*direction\s+RL[^\n]*)/i,/^(?:.*direction\s+LR[^\n]*)/i,/^(?:["])/i,/^(?:\s*as\s+)/i,/^(?:[^\n\{]*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n\s\{]+)/i,/^(?:\n)/i,/^(?:\{)/i,/^(?:\})/i,/^(?:[\n])/i,/^(?:note\s+)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:")/i,/^(?:\s*as\s*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n]*)/i,/^(?:\s*[^:\n\s\-]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:[\s\S]*?end note\b)/i,/^(?:stateDiagram\s+)/i,/^(?:stateDiagram-v2\s+)/i,/^(?:hide empty description\b)/i,/^(?:\[\*\])/i,/^(?:[^:\n\s\-\{]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:-->)/i,/^(?:--)/i,/^(?:$)/i,/^(?:.)/i],conditions:{LINE:{rules:[13,14],inclusive:!1},close_directive:{rules:[13,14],inclusive:!1},arg_directive:{rules:[7,8,13,14],inclusive:!1},type_directive:{rules:[6,7,13,14],inclusive:!1},open_directive:{rules:[5,13,14],inclusive:!1},struct:{rules:[13,14,19,26,27,28,29,38,39,40,54,55,56,57,58],inclusive:!1},FLOATING_NOTE_ID:{rules:[47],inclusive:!1},FLOATING_NOTE:{rules:[44,45,46],inclusive:!1},NOTE_TEXT:{rules:[49,50],inclusive:!1},NOTE_ID:{rules:[48],inclusive:!1},NOTE:{rules:[41,42,43],inclusive:!1},SCALE:{rules:[17,18],inclusive:!1},ALIAS:{rules:[],inclusive:!1},STATE_ID:{rules:[32],inclusive:!1},STATE_STRING:{rules:[33,34],inclusive:!1},FORK_STATE:{rules:[],inclusive:!1},STATE:{rules:[13,14,20,21,22,23,24,25,30,31,35,36,37],inclusive:!1},ID:{rules:[13,14],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,9,10,11,12,14,15,16,19,37,40,51,52,53,54,55,56,57,59,60],inclusive:!0}}};function N(){this.yy={}}return A.lexer=M,N.prototype=A,A.Parser=N,new N}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(3069).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},9763:(t,e,n)=>{t=n.nmd(t);var r=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,17,18,19,21],i=[1,15],a=[1,16],o=[1,17],s=[1,21],c=[4,6,9,11,17,18,19,21],u={trace:function(){},yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,title:17,section:18,taskName:19,taskData:20,open_directive:21,type_directive:22,arg_directive:23,close_directive:24,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",17:"title",18:"section",19:"taskName",20:"taskData",21:"open_directive",22:"type_directive",23:"arg_directive",24:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,1],[10,2],[10,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 3:case 7:case 8:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 11:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 12:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 13:r.addTask(a[s-1],a[s]),this.$="task";break;case 15:r.parseDirective("%%{","open_directive");break;case 16:r.parseDirective(a[s],"type_directive");break;case 17:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 18:r.parseDirective("}%%","close_directive","journey")}},table:[{3:1,4:e,7:3,12:4,21:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,21:n},{13:8,22:[1,9]},{22:[2,15]},{6:[1,10],7:18,8:11,9:[1,12],10:13,11:[1,14],12:4,17:i,18:a,19:o,21:n},{1:[2,2]},{14:19,15:[1,20],24:s},t([15,24],[2,16]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:18,10:22,12:4,17:i,18:a,19:o,21:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,12]),{20:[1,23]},t(r,[2,14]),{11:[1,24]},{16:25,23:[1,26]},{11:[2,18]},t(r,[2,5]),t(r,[2,13]),t(c,[2,9]),{14:27,24:s},{24:[2,17]},{11:[1,28]},t(c,[2,10])],defaultActions:{5:[2,15],7:[2,2],21:[2,18],26:[2,17]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),y={yy:{}};for(var g in this.yy)Object.prototype.hasOwnProperty.call(this.yy,g)&&(y.yy[g]=this.yy[g]);p.setInput(t,y.yy),y.yy.lexer=p,y.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var m=p.yylloc;a.push(m);var v=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof y.yy.parseError?this.parseError=y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var _,x,w,k,T,E,C,S,A,M={};;){if(w=n[n.length-1],this.defaultActions[w]?k=this.defaultActions[w]:(null==_&&(_=b()),k=o[w]&&o[w][_]),void 0===k||!k.length||!k[0]){var N="";for(E in A=[],o[w])this.terminals_[E]&&E>h&&A.push("'"+this.terminals_[E]+"'");N=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+A.join(", ")+", got '"+(this.terminals_[_]||_)+"'":"Parse error on line "+(c+1)+": Unexpected "+(_==f?"end of input":"'"+(this.terminals_[_]||_)+"'"),this.parseError(N,{text:p.match,token:this.terminals_[_]||_,line:p.yylineno,loc:m,expected:A})}if(k[0]instanceof Array&&k.length>1)throw new Error("Parse Error: multiple actions possible at state: "+w+", token: "+_);switch(k[0]){case 1:n.push(_),i.push(p.yytext),a.push(p.yylloc),n.push(k[1]),_=null,x?(_=x,x=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,m=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[k[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},v&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(T=this.performAction.apply(M,[s,u,c,y.yy,k[1],i,a].concat(d))))return T;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[k[1]][0]),i.push(M.$),a.push(M._$),S=o[n[n.length-2]][n[n.length-1]],n.push(S);break;case 3:return!0}}return!0}},l={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){return this.next()||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),21;case 1:return this.begin("type_directive"),22;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),24;case 4:return 23;case 5:case 6:case 8:case 9:break;case 7:return 11;case 10:return 4;case 11:return 17;case 12:return 18;case 13:return 19;case 14:return 20;case 15:return 15;case 16:return 6;case 17:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:journey\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,13,14,15,16,17],inclusive:!0}}};function h(){this.yy={}}return u.lexer=l,h.prototype=u,u.Parser=h,new h}();e.parser=r,e.Parser=r.Parser,e.parse=function(){return r.parse.apply(r,arguments)},e.main=function(t){t[1]||(console.log("Usage: "+t[0]+" FILE"),process.exit(1));var r=n(9143).readFileSync(n(6470).normalize(t[1]),"utf8");return e.parser.parse(r)},n.c[n.s]===t&&e.main(process.argv.slice(1))},9609:t=>{"use strict";var e=/^(%20|\s)*(javascript|data)/im,n=/[^\x20-\x7E]/gim,r=/^([^:]+):/gm,i=[".","/"];t.exports={sanitizeUrl:function(t){if(!t)return"about:blank";var a,o,s=t.replace(n,"").trim();return function(t){return i.indexOf(t[0])>-1}(s)?s:(o=s.match(r))?(a=o[0],e.test(a)?"about:blank":s):"about:blank"}}},3841:t=>{t.exports=function(t,e){return t.intersect(e)}},7458:(t,e,n)=>{"use strict";n.d(e,{default:()=>hC});var r=n(1941),i=n.n(r),a={debug:1,info:2,warn:3,error:4,fatal:5},o={debug:function(){},info:function(){},warn:function(){},error:function(){},fatal:function(){}},s=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"fatal";isNaN(t)&&(t=t.toLowerCase(),void 0!==a[t]&&(t=a[t])),o.trace=function(){},o.debug=function(){},o.info=function(){},o.warn=function(){},o.error=function(){},o.fatal=function(){},t<=a.fatal&&(o.fatal=console.error?console.error.bind(console,c("FATAL"),"color: orange"):console.log.bind(console,"",c("FATAL"))),t<=a.error&&(o.error=console.error?console.error.bind(console,c("ERROR"),"color: orange"):console.log.bind(console,"",c("ERROR"))),t<=a.warn&&(o.warn=console.warn?console.warn.bind(console,c("WARN"),"color: orange"):console.log.bind(console,"",c("WARN"))),t<=a.info&&(o.info=console.info?console.info.bind(console,c("INFO"),"color: lightblue"):console.log.bind(console,"",c("INFO"))),t<=a.debug&&(o.debug=console.debug?console.debug.bind(console,c("DEBUG"),"color: lightgreen"):console.log.bind(console,"",c("DEBUG")))},c=function(t){var e=i()().format("ss.SSS");return"%c".concat(e," : ").concat(t," : ")};function u(t,e){let n;if(void 0===e)for(const e of t)null!=e&&(n<e||void 0===n&&e>=e)&&(n=e);else{let r=-1;for(let i of t)null!=(i=e(i,++r,t))&&(n<i||void 0===n&&i>=i)&&(n=i)}return n}function l(t,e){let n;if(void 0===e)for(const e of t)null!=e&&(n>e||void 0===n&&e>=e)&&(n=e);else{let r=-1;for(let i of t)null!=(i=e(i,++r,t))&&(n>i||void 0===n&&i>=i)&&(n=i)}return n}function h(t){return t}var f=1e-6;function d(t){return"translate("+t+",0)"}function p(t){return"translate(0,"+t+")"}function y(t){return e=>+t(e)}function g(t,e){return e=Math.max(0,t.bandwidth()-2*e)/2,t.round()&&(e=Math.round(e)),n=>+t(n)+e}function m(){return!this.__axis}function v(t,e){var n=[],r=null,i=null,a=6,o=6,s=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,u=1===t||4===t?-1:1,l=4===t||2===t?"x":"y",v=1===t||3===t?d:p;function b(d){var p=null==r?e.ticks?e.ticks.apply(e,n):e.domain():r,b=null==i?e.tickFormat?e.tickFormat.apply(e,n):h:i,_=Math.max(a,0)+s,x=e.range(),w=+x[0]+c,k=+x[x.length-1]+c,T=(e.bandwidth?g:y)(e.copy(),c),E=d.selection?d.selection():d,C=E.selectAll(".domain").data([null]),S=E.selectAll(".tick").data(p,e).order(),A=S.exit(),M=S.enter().append("g").attr("class","tick"),N=S.select("line"),D=S.select("text");C=C.merge(C.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),S=S.merge(M),N=N.merge(M.append("line").attr("stroke","currentColor").attr(l+"2",u*a)),D=D.merge(M.append("text").attr("fill","currentColor").attr(l,u*_).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),d!==E&&(C=C.transition(d),S=S.transition(d),N=N.transition(d),D=D.transition(d),A=A.transition(d).attr("opacity",f).attr("transform",(function(t){return isFinite(t=T(t))?v(t+c):this.getAttribute("transform")})),M.attr("opacity",f).attr("transform",(function(t){var e=this.parentNode.__axis;return v((e&&isFinite(e=e(t))?e:T(t))+c)}))),A.remove(),C.attr("d",4===t||2===t?o?"M"+u*o+","+w+"H"+c+"V"+k+"H"+u*o:"M"+c+","+w+"V"+k:o?"M"+w+","+u*o+"V"+c+"H"+k+"V"+u*o:"M"+w+","+c+"H"+k),S.attr("opacity",1).attr("transform",(function(t){return v(T(t)+c)})),N.attr(l+"2",u*a),D.attr(l,u*_).text(b),E.filter(m).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),E.each((function(){this.__axis=T}))}return b.scale=function(t){return arguments.length?(e=t,b):e},b.ticks=function(){return n=Array.from(arguments),b},b.tickArguments=function(t){return arguments.length?(n=null==t?[]:Array.from(t),b):n.slice()},b.tickValues=function(t){return arguments.length?(r=null==t?null:Array.from(t),b):r&&r.slice()},b.tickFormat=function(t){return arguments.length?(i=t,b):i},b.tickSize=function(t){return arguments.length?(a=o=+t,b):a},b.tickSizeInner=function(t){return arguments.length?(a=+t,b):a},b.tickSizeOuter=function(t){return arguments.length?(o=+t,b):o},b.tickPadding=function(t){return arguments.length?(s=+t,b):s},b.offset=function(t){return arguments.length?(c=+t,b):c},b}function b(){}function _(t){return null==t?b:function(){return this.querySelector(t)}}function x(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function w(){return[]}function k(t){return null==t?w:function(){return this.querySelectorAll(t)}}function T(t){return function(){return this.matches(t)}}function E(t){return function(e){return e.matches(t)}}var C=Array.prototype.find;function S(){return this.firstElementChild}var A=Array.prototype.filter;function M(){return Array.from(this.children)}function N(t){return new Array(t.length)}function D(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function O(t){return function(){return t}}function B(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new D(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function L(t,e,n,r,i,a,o){var s,c,u,l=new Map,h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u=o.call(c,c.__data__,s,e)+"",l.has(u)?i[s]=c:l.set(u,c));for(s=0;s<f;++s)u=o.call(t,a[s],s,a)+"",(c=l.get(u))?(r[s]=c,c.__data__=a[s],l.delete(u)):n[s]=new D(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l.get(d[s])===c&&(i[s]=c)}function I(t){return t.__data__}function R(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function F(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}D.prototype={constructor:D,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var P="http://www.w3.org/1999/xhtml";const j={svg:"http://www.w3.org/2000/svg",xhtml:P,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Y(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),j.hasOwnProperty(e)?{space:j[e],local:t}:t}function z(t){return function(){this.removeAttribute(t)}}function U(t){return function(){this.removeAttributeNS(t.space,t.local)}}function q(t,e){return function(){this.setAttribute(t,e)}}function H(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function $(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function W(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function V(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function G(t){return function(){this.style.removeProperty(t)}}function X(t,e,n){return function(){this.style.setProperty(t,e,n)}}function Z(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function Q(t,e){return t.style.getPropertyValue(e)||V(t).getComputedStyle(t,null).getPropertyValue(e)}function K(t){return function(){delete this[t]}}function J(t,e){return function(){this[t]=e}}function tt(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function et(t){return t.trim().split(/^|\s+/)}function nt(t){return t.classList||new rt(t)}function rt(t){this._node=t,this._names=et(t.getAttribute("class")||"")}function it(t,e){for(var n=nt(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function at(t,e){for(var n=nt(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function ot(t){return function(){it(this,t)}}function st(t){return function(){at(this,t)}}function ct(t,e){return function(){(e.apply(this,arguments)?it:at)(this,t)}}function ut(){this.textContent=""}function lt(t){return function(){this.textContent=t}}function ht(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function ft(){this.innerHTML=""}function dt(t){return function(){this.innerHTML=t}}function pt(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function yt(){this.nextSibling&&this.parentNode.appendChild(this)}function gt(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function mt(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===P&&e.documentElement.namespaceURI===P?e.createElement(t):e.createElementNS(n,t)}}function vt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function bt(t){var e=Y(t);return(e.local?vt:mt)(e)}function _t(){return null}function xt(){var t=this.parentNode;t&&t.removeChild(this)}function wt(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function kt(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Tt(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function Et(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.options);++i?e.length=i:delete this.__on}}}function Ct(t,e,n){return function(){var r,i=this.__on,a=function(t){return function(e){t.call(this,e,this.__data__)}}(e);if(i)for(var o=0,s=i.length;o<s;++o)if((r=i[o]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=a,r.options=n),void(r.value=e);this.addEventListener(t.type,a,n),r={type:t.type,name:t.name,value:e,listener:a,options:n},i?i.push(r):this.__on=[r]}}function St(t,e,n){var r=V(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function At(t,e){return function(){return St(this,t,e)}}function Mt(t,e){return function(){return St(this,t,e.apply(this,arguments))}}rt.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Nt=[null];function Dt(t,e){this._groups=t,this._parents=e}function Ot(){return new Dt([[document.documentElement]],Nt)}Dt.prototype=Ot.prototype={constructor:Dt,select:function(t){"function"!=typeof t&&(t=_(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new Dt(r,this._parents)},selectAll:function(t){t="function"==typeof t?function(t){return function(){return x(t.apply(this,arguments))}}(t):k(t);for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new Dt(r,i)},selectChild:function(t){return this.select(null==t?S:function(t){return function(){return C.call(this.children,t)}}("function"==typeof t?t:E(t)))},selectChildren:function(t){return this.selectAll(null==t?M:function(t){return function(){return A.call(this.children,t)}}("function"==typeof t?t:E(t)))},filter:function(t){"function"!=typeof t&&(t=T(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Dt(r,this._parents)},data:function(t,e){if(!arguments.length)return Array.from(this,I);var n=e?L:B,r=this._parents,i=this._groups;"function"!=typeof t&&(t=O(t));for(var a=i.length,o=new Array(a),s=new Array(a),c=new Array(a),u=0;u<a;++u){var l=r[u],h=i[u],f=h.length,d=R(t.call(l,l&&l.__data__,u,r)),p=d.length,y=s[u]=new Array(p),g=o[u]=new Array(p),m=c[u]=new Array(f);n(l,h,y,g,m,d,e);for(var v,b,_=0,x=0;_<p;++_)if(v=y[_]){for(_>=x&&(x=_+1);!(b=g[x])&&++x<p;);v._next=b||null}}return(o=new Dt(o,r))._enter=s,o._exit=c,o},enter:function(){return new Dt(this._enter||this._groups.map(N),this._parents)},exit:function(){return new Dt(this._exit||this._groups.map(N),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=e&&(i=e(i))&&(i=i.selection()),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=t.selection?t.selection():t,n=this._groups,r=e._groups,i=n.length,a=r.length,o=Math.min(i,a),s=new Array(i),c=0;c<o;++c)for(var u,l=n[c],h=r[c],f=l.length,d=s[c]=new Array(f),p=0;p<f;++p)(u=l[p]||h[p])&&(d[p]=u);for(;c<i;++c)s[c]=n[c];return new Dt(s,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=F);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new Dt(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){let t=0;for(const e of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=Y(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?U:z:"function"==typeof e?n.local?W:$:n.local?H:q)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?G:"function"==typeof e?Z:X)(t,e,null==n?"":n)):Q(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?K:"function"==typeof e?tt:J)(t,e)):this.node()[t]},classed:function(t,e){var n=et(t+"");if(arguments.length<2){for(var r=nt(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?ct:e?ot:st)(n,e))},text:function(t){return arguments.length?this.each(null==t?ut:("function"==typeof t?ht:lt)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?ft:("function"==typeof t?pt:dt)(t)):this.node().innerHTML},raise:function(){return this.each(yt)},lower:function(){return this.each(gt)},append:function(t){var e="function"==typeof t?t:bt(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:bt(t),r=null==e?_t:"function"==typeof e?e:_(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(xt)},clone:function(t){return this.select(t?kt:wt)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=Tt(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?Ct:Et,r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?Mt:At)(t,e))},[Symbol.iterator]:function*(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r,i=t[e],a=0,o=i.length;a<o;++a)(r=i[a])&&(yield r)}};const Bt=Ot;var Lt={value:()=>{}};function It(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new Rt(r)}function Rt(t){this._=t}function Ft(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function Pt(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function jt(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=Lt,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}Rt.prototype=It.prototype={constructor:Rt,on:function(t,e){var n,r=this._,i=Ft(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=jt(r[n],t.name,e);else if(null==e)for(n in r)r[n]=jt(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=Pt(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new Rt(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const Yt=It;var zt,Ut,qt=0,Ht=0,$t=0,Wt=0,Vt=0,Gt=0,Xt="object"==typeof performance&&performance.now?performance:Date,Zt="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function Qt(){return Vt||(Zt(Kt),Vt=Xt.now()+Gt)}function Kt(){Vt=0}function Jt(){this._call=this._time=this._next=null}function te(t,e,n){var r=new Jt;return r.restart(t,e,n),r}function ee(){Vt=(Wt=Xt.now())+Gt,qt=Ht=0;try{!function(){Qt(),++qt;for(var t,e=zt;e;)(t=Vt-e._time)>=0&&e._call.call(void 0,t),e=e._next;--qt}()}finally{qt=0,function(){for(var t,e,n=zt,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:zt=e);Ut=t,re(r)}(),Vt=0}}function ne(){var t=Xt.now(),e=t-Wt;e>1e3&&(Gt-=e,Wt=t)}function re(t){qt||(Ht&&(Ht=clearTimeout(Ht)),t-Vt>24?(t<1/0&&(Ht=setTimeout(ee,t-Xt.now()-Gt)),$t&&($t=clearInterval($t))):($t||(Wt=Xt.now(),$t=setInterval(ne,1e3)),qt=1,Zt(ee)))}function ie(t,e,n){var r=new Jt;return e=null==e?0:+e,r.restart((n=>{r.stop(),t(n+e)}),e,n),r}Jt.prototype=te.prototype={constructor:Jt,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?Qt():+n)+(null==e?0:+e),this._next||Ut===this||(Ut?Ut._next=this:zt=this,Ut=this),this._call=t,this._time=n,re()},stop:function(){this._call&&(this._call=null,this._time=1/0,re())}};var ae=Yt("start","end","cancel","interrupt"),oe=[];function se(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return ie(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(ie((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=te((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:ae,tween:oe,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function ce(t,e){var n=le(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function ue(t,e){var n=le(t,e);if(n.state>3)throw new Error("too late; already running");return n}function le(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function he(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}var fe,de=180/Math.PI,pe={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function ye(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*de,skewX:Math.atan(c)*de,scaleX:o,scaleY:s}}function ge(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:he(t,i)},{i:c-2,x:he(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:he(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:he(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:he(t,n)},{i:s-2,x:he(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var me=ge((function(t){const e=new("function"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+"");return e.isIdentity?pe:ye(e.a,e.b,e.c,e.d,e.e,e.f)}),"px, ","px)","deg)"),ve=ge((function(t){return null==t?pe:(fe||(fe=document.createElementNS("http://www.w3.org/2000/svg","g")),fe.setAttribute("transform",t),(t=fe.transform.baseVal.consolidate())?ye((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):pe)}),", ",")",")");function be(t,e){var n,r;return function(){var i=ue(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function _e(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=ue(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function xe(t,e,n){var r=t._id;return t.each((function(){var t=ue(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return le(t,r).value[e]}}function we(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function ke(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function Te(){}var Ee=.7,Ce=1/Ee,Se="\\s*([+-]?\\d+)\\s*",Ae="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Me="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Ne=/^#([0-9a-f]{3,8})$/,De=new RegExp("^rgb\\("+[Se,Se,Se]+"\\)$"),Oe=new RegExp("^rgb\\("+[Me,Me,Me]+"\\)$"),Be=new RegExp("^rgba\\("+[Se,Se,Se,Ae]+"\\)$"),Le=new RegExp("^rgba\\("+[Me,Me,Me,Ae]+"\\)$"),Ie=new RegExp("^hsl\\("+[Ae,Me,Me]+"\\)$"),Re=new RegExp("^hsla\\("+[Ae,Me,Me,Ae]+"\\)$"),Fe={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function Pe(){return this.rgb().formatHex()}function je(){return this.rgb().formatRgb()}function Ye(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Ne.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?ze(e):3===n?new $e(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?Ue(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?Ue(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=De.exec(t))?new $e(e[1],e[2],e[3],1):(e=Oe.exec(t))?new $e(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Be.exec(t))?Ue(e[1],e[2],e[3],e[4]):(e=Le.exec(t))?Ue(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=Ie.exec(t))?Xe(e[1],e[2]/100,e[3]/100,1):(e=Re.exec(t))?Xe(e[1],e[2]/100,e[3]/100,e[4]):Fe.hasOwnProperty(t)?ze(Fe[t]):"transparent"===t?new $e(NaN,NaN,NaN,0):null}function ze(t){return new $e(t>>16&255,t>>8&255,255&t,1)}function Ue(t,e,n,r){return r<=0&&(t=e=n=NaN),new $e(t,e,n,r)}function qe(t){return t instanceof Te||(t=Ye(t)),t?new $e((t=t.rgb()).r,t.g,t.b,t.opacity):new $e}function He(t,e,n,r){return 1===arguments.length?qe(t):new $e(t,e,n,null==r?1:r)}function $e(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function We(){return"#"+Ge(this.r)+Ge(this.g)+Ge(this.b)}function Ve(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function Ge(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function Xe(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new Qe(t,e,n,r)}function Ze(t){if(t instanceof Qe)return new Qe(t.h,t.s,t.l,t.opacity);if(t instanceof Te||(t=Ye(t)),!t)return new Qe;if(t instanceof Qe)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new Qe(o,s,c,t.opacity)}function Qe(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function Ke(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function Je(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}we(Te,Ye,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:Pe,formatHex:Pe,formatHsl:function(){return Ze(this).formatHsl()},formatRgb:je,toString:je}),we($e,He,ke(Te,{brighter:function(t){return t=null==t?Ce:Math.pow(Ce,t),new $e(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Ee:Math.pow(Ee,t),new $e(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:We,formatHex:We,formatRgb:Ve,toString:Ve})),we(Qe,(function(t,e,n,r){return 1===arguments.length?Ze(t):new Qe(t,e,n,null==r?1:r)}),ke(Te,{brighter:function(t){return t=null==t?Ce:Math.pow(Ce,t),new Qe(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Ee:Math.pow(Ee,t),new Qe(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new $e(Ke(t>=240?t-240:t+120,i,r),Ke(t,i,r),Ke(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const tn=t=>()=>t;function en(t,e){var n=e-t;return n?function(t,e){return function(n){return t+n*e}}(t,n):tn(isNaN(t)?e:t)}const nn=function t(e){var n=function(t){return 1==(t=+t)?en:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):tn(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=He(t)).r,(e=He(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=en(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function rn(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=He(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}rn((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return Je((n-r/e)*e,o,i,a,s)}})),rn((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return Je((n-r/e)*e,i,a,o,s)}}));var an=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,on=new RegExp(an.source,"g");function sn(t,e){var n,r,i,a=an.lastIndex=on.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=an.exec(t))&&(r=on.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:he(n,r)})),a=on.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function cn(t,e){var n;return("number"==typeof e?he:e instanceof Ye?nn:(n=Ye(e))?(e=n,nn):sn)(t,e)}function un(t){return function(){this.removeAttribute(t)}}function ln(t){return function(){this.removeAttributeNS(t.space,t.local)}}function hn(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function fn(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function dn(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function pn(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function yn(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function gn(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function mn(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&gn(t,i)),n}return i._value=e,i}function vn(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&yn(t,i)),n}return i._value=e,i}function bn(t,e){return function(){ce(this,t).delay=+e.apply(this,arguments)}}function _n(t,e){return e=+e,function(){ce(this,t).delay=e}}function xn(t,e){return function(){ue(this,t).duration=+e.apply(this,arguments)}}function wn(t,e){return e=+e,function(){ue(this,t).duration=e}}function kn(t,e){if("function"!=typeof e)throw new Error;return function(){ue(this,t).ease=e}}function Tn(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?ce:ue;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var En=Bt.prototype.constructor;function Cn(t){return function(){this.style.removeProperty(t)}}function Sn(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function An(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Sn(t,a,n)),r}return a._value=e,a}function Mn(t){return function(e){this.textContent=t.call(this,e)}}function Nn(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Mn(r)),e}return r._value=t,r}var Dn=0;function On(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function Bn(){return++Dn}var Ln=Bt.prototype;On.prototype=function(t){return Bt().transition(t)}.prototype={constructor:On,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=_(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,se(h[f],e,n,f,h,le(s,n)));return new On(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=k(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=le(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&se(f,e,n,y,d,p);a.push(d),o.push(c)}return new On(a,o,e,n)},selectChild:Ln.selectChild,selectChildren:Ln.selectChildren,filter:function(t){"function"!=typeof t&&(t=T(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new On(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new On(o,this._parents,this._name,this._id)},selection:function(){return new En(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=Bn(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=le(o,e);se(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new On(r,this._parents,t,n)},call:Ln.call,nodes:Ln.nodes,node:Ln.node,size:Ln.size,empty:Ln.empty,each:Ln.each,on:function(t,e){var n=this._id;return arguments.length<2?le(this.node(),n).on.on(t):this.each(Tn(n,t,e))},attr:function(t,e){var n=Y(t),r="transform"===n?ve:cn;return this.attrTween(t,"function"==typeof e?(n.local?pn:dn)(n,r,xe(this,"attr."+t,e)):null==e?(n.local?ln:un)(n):(n.local?fn:hn)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=Y(t);return this.tween(n,(r.local?mn:vn)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?me:cn;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=Q(this,t),o=(this.style.removeProperty(t),Q(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Cn(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=Q(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=Q(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,xe(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=ue(this,t),u=c.on,l=null==c.value[o]?a||(a=Cn(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=Q(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,An(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(xe(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,Nn(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=le(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?be:_e)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?bn:_n)(e,t)):le(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?xn:wn)(e,t)):le(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(kn(e,t)):le(this.node(),e).ease},easeVarying:function(t){if("function"!=typeof t)throw new Error;return this.each(function(t,e){return function(){var n=e.apply(this,arguments);if("function"!=typeof n)throw new Error;ue(this,t).ease=n}}(this._id,t))},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=ue(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e})),0===i&&a()}))},[Symbol.iterator]:Ln[Symbol.iterator]};var In={time:null,delay:0,duration:250,ease:function(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}};function Rn(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))throw new Error(`transition ${e} not found`);return n}Bt.prototype.interrupt=function(t){return this.each((function(){!function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}(this,t)}))},Bt.prototype.transition=function(t){var e,n;t instanceof On?(e=t._id,t=t._name):(e=Bn(),(n=In).time=Qt(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&se(o,t,e,u,s,n||Rn(o,e));return new On(r,this._parents,t,e)};const{abs:Fn,max:Pn,min:jn}=Math;function Yn(t){return{type:t}}function zn(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Un(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function qn(){}["w","e"].map(Yn),["n","s"].map(Yn),["n","w","e","s","nw","ne","sw","se"].map(Yn);var Hn=.7,$n=1/Hn,Wn="\\s*([+-]?\\d+)\\s*",Vn="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Gn="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Xn=/^#([0-9a-f]{3,8})$/,Zn=new RegExp("^rgb\\("+[Wn,Wn,Wn]+"\\)$"),Qn=new RegExp("^rgb\\("+[Gn,Gn,Gn]+"\\)$"),Kn=new RegExp("^rgba\\("+[Wn,Wn,Wn,Vn]+"\\)$"),Jn=new RegExp("^rgba\\("+[Gn,Gn,Gn,Vn]+"\\)$"),tr=new RegExp("^hsl\\("+[Vn,Gn,Gn]+"\\)$"),er=new RegExp("^hsla\\("+[Vn,Gn,Gn,Vn]+"\\)$"),nr={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function rr(){return this.rgb().formatHex()}function ir(){return this.rgb().formatRgb()}function ar(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Xn.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?or(e):3===n?new lr(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?sr(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?sr(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Zn.exec(t))?new lr(e[1],e[2],e[3],1):(e=Qn.exec(t))?new lr(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Kn.exec(t))?sr(e[1],e[2],e[3],e[4]):(e=Jn.exec(t))?sr(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=tr.exec(t))?pr(e[1],e[2]/100,e[3]/100,1):(e=er.exec(t))?pr(e[1],e[2]/100,e[3]/100,e[4]):nr.hasOwnProperty(t)?or(nr[t]):"transparent"===t?new lr(NaN,NaN,NaN,0):null}function or(t){return new lr(t>>16&255,t>>8&255,255&t,1)}function sr(t,e,n,r){return r<=0&&(t=e=n=NaN),new lr(t,e,n,r)}function cr(t){return t instanceof qn||(t=ar(t)),t?new lr((t=t.rgb()).r,t.g,t.b,t.opacity):new lr}function ur(t,e,n,r){return 1===arguments.length?cr(t):new lr(t,e,n,null==r?1:r)}function lr(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function hr(){return"#"+dr(this.r)+dr(this.g)+dr(this.b)}function fr(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function dr(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function pr(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new gr(t,e,n,r)}function yr(t){if(t instanceof gr)return new gr(t.h,t.s,t.l,t.opacity);if(t instanceof qn||(t=ar(t)),!t)return new gr;if(t instanceof gr)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new gr(o,s,c,t.opacity)}function gr(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function mr(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}zn(qn,ar,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:rr,formatHex:rr,formatHsl:function(){return yr(this).formatHsl()},formatRgb:ir,toString:ir}),zn(lr,ur,Un(qn,{brighter:function(t){return t=null==t?$n:Math.pow($n,t),new lr(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Hn:Math.pow(Hn,t),new lr(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:hr,formatHex:hr,formatRgb:fr,toString:fr})),zn(gr,(function(t,e,n,r){return 1===arguments.length?yr(t):new gr(t,e,n,null==r?1:r)}),Un(qn,{brighter:function(t){return t=null==t?$n:Math.pow($n,t),new gr(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Hn:Math.pow(Hn,t),new gr(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new lr(mr(t>=240?t-240:t+120,i,r),mr(t,i,r),mr(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const vr=Math.PI/180,br=180/Math.PI,_r=.96422,xr=.82521,wr=4/29,kr=6/29,Tr=3*kr*kr;function Er(t){if(t instanceof Cr)return new Cr(t.l,t.a,t.b,t.opacity);if(t instanceof Br)return Lr(t);t instanceof lr||(t=cr(t));var e,n,r=Nr(t.r),i=Nr(t.g),a=Nr(t.b),o=Sr((.2225045*r+.7168786*i+.0606169*a)/1);return r===i&&i===a?e=n=o:(e=Sr((.4360747*r+.3850649*i+.1430804*a)/_r),n=Sr((.0139322*r+.0971045*i+.7141733*a)/xr)),new Cr(116*o-16,500*(e-o),200*(o-n),t.opacity)}function Cr(t,e,n,r){this.l=+t,this.a=+e,this.b=+n,this.opacity=+r}function Sr(t){return t>.008856451679035631?Math.pow(t,1/3):t/Tr+wr}function Ar(t){return t>kr?t*t*t:Tr*(t-wr)}function Mr(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Nr(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Dr(t){if(t instanceof Br)return new Br(t.h,t.c,t.l,t.opacity);if(t instanceof Cr||(t=Er(t)),0===t.a&&0===t.b)return new Br(NaN,0<t.l&&t.l<100?0:NaN,t.l,t.opacity);var e=Math.atan2(t.b,t.a)*br;return new Br(e<0?e+360:e,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function Or(t,e,n,r){return 1===arguments.length?Dr(t):new Br(t,e,n,null==r?1:r)}function Br(t,e,n,r){this.h=+t,this.c=+e,this.l=+n,this.opacity=+r}function Lr(t){if(isNaN(t.h))return new Cr(t.l,0,0,t.opacity);var e=t.h*vr;return new Cr(t.l,Math.cos(e)*t.c,Math.sin(e)*t.c,t.opacity)}zn(Cr,(function(t,e,n,r){return 1===arguments.length?Er(t):new Cr(t,e,n,null==r?1:r)}),Un(qn,{brighter:function(t){return new Cr(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Cr(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this.b)?t:t-this.b/200;return new lr(Mr(3.1338561*(e=_r*Ar(e))-1.6168667*(t=1*Ar(t))-.4906146*(n=xr*Ar(n))),Mr(-.9787684*e+1.9161415*t+.033454*n),Mr(.0719453*e-.2289914*t+1.4052427*n),this.opacity)}})),zn(Br,Or,Un(qn,{brighter:function(t){return new Br(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new Br(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return Lr(this).rgb()}}));const Ir=t=>()=>t;function Rr(t,e){return function(n){return t+n*e}}function Fr(t,e){var n=e-t;return n?Rr(t,n):Ir(isNaN(t)?e:t)}function Pr(t){return function(e,n){var r=t((e=Or(e)).h,(n=Or(n)).h),i=Fr(e.c,n.c),a=Fr(e.l,n.l),o=Fr(e.opacity,n.opacity);return function(t){return e.h=r(t),e.c=i(t),e.l=a(t),e.opacity=o(t),e+""}}}const jr=Pr((function(t,e){var n=e-t;return n?Rr(t,n>180||n<-180?n-360*Math.round(n/360):n):Ir(isNaN(t)?e:t)}));Pr(Fr);var Yr=Math.sqrt(50),zr=Math.sqrt(10),Ur=Math.sqrt(2);function qr(t,e,n){var r=(e-t)/Math.max(0,n),i=Math.floor(Math.log(r)/Math.LN10),a=r/Math.pow(10,i);return i>=0?(a>=Yr?10:a>=zr?5:a>=Ur?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=Yr?10:a>=zr?5:a>=Ur?2:1)}function Hr(t,e,n){var r=Math.abs(e-t)/Math.max(0,n),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),a=r/i;return a>=Yr?i*=10:a>=zr?i*=5:a>=Ur&&(i*=2),e<t?-i:i}function $r(t,e){return null==t||null==e?NaN:t<e?-1:t>e?1:t>=e?0:NaN}function Wr(t){let e=t,n=t,r=t;function i(t,e,i=0,a=t.length){if(i<a){if(0!==n(e,e))return a;do{const n=i+a>>>1;r(t[n],e)<0?i=n+1:a=n}while(i<a)}return i}return 2!==t.length&&(e=(e,n)=>t(e)-n,n=$r,r=(e,n)=>$r(t(e),n)),{left:i,center:function(t,n,r=0,a=t.length){const o=i(t,n,r,a-1);return o>r&&e(t[o-1],n)>-e(t[o],n)?o-1:o},right:function(t,e,i=0,a=t.length){if(i<a){if(0!==n(e,e))return a;do{const n=i+a>>>1;r(t[n],e)<=0?i=n+1:a=n}while(i<a)}return i}}}const Vr=Wr($r),Gr=Vr.right,Xr=(Vr.left,Wr((function(t){return null===t?NaN:+t})).center,Gr);function Zr(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Qr(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function Kr(){}var Jr=.7,ti=1.4285714285714286,ei="\\s*([+-]?\\d+)\\s*",ni="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",ri="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",ii=/^#([0-9a-f]{3,8})$/,ai=new RegExp("^rgb\\("+[ei,ei,ei]+"\\)$"),oi=new RegExp("^rgb\\("+[ri,ri,ri]+"\\)$"),si=new RegExp("^rgba\\("+[ei,ei,ei,ni]+"\\)$"),ci=new RegExp("^rgba\\("+[ri,ri,ri,ni]+"\\)$"),ui=new RegExp("^hsl\\("+[ni,ri,ri]+"\\)$"),li=new RegExp("^hsla\\("+[ni,ri,ri,ni]+"\\)$"),hi={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function fi(){return this.rgb().formatHex()}function di(){return this.rgb().formatRgb()}function pi(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=ii.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?yi(e):3===n?new bi(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?gi(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?gi(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=ai.exec(t))?new bi(e[1],e[2],e[3],1):(e=oi.exec(t))?new bi(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=si.exec(t))?gi(e[1],e[2],e[3],e[4]):(e=ci.exec(t))?gi(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=ui.exec(t))?ki(e[1],e[2]/100,e[3]/100,1):(e=li.exec(t))?ki(e[1],e[2]/100,e[3]/100,e[4]):hi.hasOwnProperty(t)?yi(hi[t]):"transparent"===t?new bi(NaN,NaN,NaN,0):null}function yi(t){return new bi(t>>16&255,t>>8&255,255&t,1)}function gi(t,e,n,r){return r<=0&&(t=e=n=NaN),new bi(t,e,n,r)}function mi(t){return t instanceof Kr||(t=pi(t)),t?new bi((t=t.rgb()).r,t.g,t.b,t.opacity):new bi}function vi(t,e,n,r){return 1===arguments.length?mi(t):new bi(t,e,n,null==r?1:r)}function bi(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function _i(){return"#"+wi(this.r)+wi(this.g)+wi(this.b)}function xi(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function wi(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function ki(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new Ei(t,e,n,r)}function Ti(t){if(t instanceof Ei)return new Ei(t.h,t.s,t.l,t.opacity);if(t instanceof Kr||(t=pi(t)),!t)return new Ei;if(t instanceof Ei)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new Ei(o,s,c,t.opacity)}function Ei(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function Ci(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function Si(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}Zr(Kr,pi,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:fi,formatHex:fi,formatHsl:function(){return Ti(this).formatHsl()},formatRgb:di,toString:di}),Zr(bi,vi,Qr(Kr,{brighter:function(t){return t=null==t?ti:Math.pow(ti,t),new bi(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Jr:Math.pow(Jr,t),new bi(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:_i,formatHex:_i,formatRgb:xi,toString:xi})),Zr(Ei,(function(t,e,n,r){return 1===arguments.length?Ti(t):new Ei(t,e,n,null==r?1:r)}),Qr(Kr,{brighter:function(t){return t=null==t?ti:Math.pow(ti,t),new Ei(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Jr:Math.pow(Jr,t),new Ei(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new bi(Ci(t>=240?t-240:t+120,i,r),Ci(t,i,r),Ci(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const Ai=t=>()=>t;function Mi(t,e){var n=e-t;return n?function(t,e){return function(n){return t+n*e}}(t,n):Ai(isNaN(t)?e:t)}const Ni=function t(e){var n=function(t){return 1==(t=+t)?Mi:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):Ai(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=vi(t)).r,(e=vi(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=Mi(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function Di(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=vi(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}function Oi(t,e){var n,r=e?e.length:0,i=t?Math.min(r,t.length):0,a=new Array(i),o=new Array(r);for(n=0;n<i;++n)a[n]=Yi(t[n],e[n]);for(;n<r;++n)o[n]=e[n];return function(t){for(n=0;n<i;++n)o[n]=a[n](t);return o}}function Bi(t,e){var n=new Date;return t=+t,e=+e,function(r){return n.setTime(t*(1-r)+e*r),n}}function Li(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}function Ii(t,e){var n,r={},i={};for(n in null!==t&&"object"==typeof t||(t={}),null!==e&&"object"==typeof e||(e={}),e)n in t?r[n]=Yi(t[n],e[n]):i[n]=e[n];return function(t){for(n in r)i[n]=r[n](t);return i}}Di((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return Si((n-r/e)*e,o,i,a,s)}})),Di((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return Si((n-r/e)*e,i,a,o,s)}}));var Ri=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Fi=new RegExp(Ri.source,"g");function Pi(t,e){var n,r,i,a=Ri.lastIndex=Fi.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=Ri.exec(t))&&(r=Fi.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:Li(n,r)})),a=Fi.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function ji(t,e){e||(e=[]);var n,r=t?Math.min(e.length,t.length):0,i=e.slice();return function(a){for(n=0;n<r;++n)i[n]=t[n]*(1-a)+e[n]*a;return i}}function Yi(t,e){var n,r,i=typeof e;return null==e||"boolean"===i?Ai(e):("number"===i?Li:"string"===i?(n=pi(e))?(e=n,Ni):Pi:e instanceof pi?Ni:e instanceof Date?Bi:(r=e,!ArrayBuffer.isView(r)||r instanceof DataView?Array.isArray(e)?Oi:"function"!=typeof e.valueOf&&"function"!=typeof e.toString||isNaN(e)?Ii:Li:ji))(t,e)}function zi(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}}function Ui(t){return+t}var qi=[0,1];function Hi(t){return t}function $i(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:(n=isNaN(e)?NaN:.5,function(){return n});var n}function Wi(t,e,n){var r=t[0],i=t[1],a=e[0],o=e[1];return i<r?(r=$i(i,r),a=n(o,a)):(r=$i(r,i),a=n(a,o)),function(t){return a(r(t))}}function Vi(t,e,n){var r=Math.min(t.length,e.length)-1,i=new Array(r),a=new Array(r),o=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),e=e.slice().reverse());++o<r;)i[o]=$i(t[o],t[o+1]),a[o]=n(e[o],e[o+1]);return function(e){var n=Xr(t,e,1,r)-1;return a[n](i[n](e))}}function Gi(t,e){return e.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function Xi(){return function(){var t,e,n,r,i,a,o=qi,s=qi,c=Yi,u=Hi;function l(){var t,e,n,c=Math.min(o.length,s.length);return u!==Hi&&(t=o[0],e=o[c-1],t>e&&(n=t,t=e,e=n),u=function(n){return Math.max(t,Math.min(e,n))}),r=c>2?Vi:Wi,i=a=null,h}function h(e){return null==e||isNaN(e=+e)?n:(i||(i=r(o.map(t),s,c)))(t(u(e)))}return h.invert=function(n){return u(e((a||(a=r(s,o.map(t),Li)))(n)))},h.domain=function(t){return arguments.length?(o=Array.from(t,Ui),l()):o.slice()},h.range=function(t){return arguments.length?(s=Array.from(t),l()):s.slice()},h.rangeRound=function(t){return s=Array.from(t),c=zi,l()},h.clamp=function(t){return arguments.length?(u=!!t||Hi,l()):u!==Hi},h.interpolate=function(t){return arguments.length?(c=t,l()):c},h.unknown=function(t){return arguments.length?(n=t,h):n},function(n,r){return t=n,e=r,l()}}()(Hi,Hi)}function Zi(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t)}return this}var Qi,Ki=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Ji(t){if(!(e=Ki.exec(t)))throw new Error("invalid format: "+t);var e;return new ta({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}function ta(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function ea(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,r=t.slice(0,n);return[r.length>1?r[0]+r.slice(2):r,+t.slice(n+1)]}function na(t){return(t=ea(Math.abs(t)))?t[1]:NaN}function ra(t,e){var n=ea(t,e);if(!n)return t+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}Ji.prototype=ta.prototype,ta.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};const ia={"%":(t,e)=>(100*t).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>ra(100*t,e),r:ra,s:function(t,e){var n=ea(t,e);if(!n)return t+"";var r=n[0],i=n[1],a=i-(Qi=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,o=r.length;return a===o?r:a>o?r+new Array(a-o+1).join("0"):a>0?r.slice(0,a)+"."+r.slice(a):"0."+new Array(1-a).join("0")+ea(t,Math.max(0,e+a-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function aa(t){return t}var oa,sa,ca,ua=Array.prototype.map,la=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function ha(t){var e=t.domain;return t.ticks=function(t){var n=e();return function(t,e,n){var r,i,a,o,s=-1;if(n=+n,(t=+t)==(e=+e)&&n>0)return[t];if((r=e<t)&&(i=t,t=e,e=i),0===(o=qr(t,e,n))||!isFinite(o))return[];if(o>0){let n=Math.round(t/o),r=Math.round(e/o);for(n*o<t&&++n,r*o>e&&--r,a=new Array(i=r-n+1);++s<i;)a[s]=(n+s)*o}else{o=-o;let n=Math.round(t*o),r=Math.round(e*o);for(n/o<t&&++n,r/o>e&&--r,a=new Array(i=r-n+1);++s<i;)a[s]=(n+s)/o}return r&&a.reverse(),a}(n[0],n[n.length-1],null==t?10:t)},t.tickFormat=function(t,n){var r=e();return function(t,e,n,r){var i,a=Hr(t,e,n);switch((r=Ji(null==r?",f":r)).type){case"s":var o=Math.max(Math.abs(t),Math.abs(e));return null!=r.precision||isNaN(i=function(t,e){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(na(e)/3)))-na(Math.abs(t)))}(a,o))||(r.precision=i),ca(r,o);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=function(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,na(e)-na(t))+1}(a,Math.max(Math.abs(t),Math.abs(e))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=function(t){return Math.max(0,-na(Math.abs(t)))}(a))||(r.precision=i-2*("%"===r.type))}return sa(r)}(r[0],r[r.length-1],null==t?10:t,n)},t.nice=function(n){null==n&&(n=10);var r,i,a=e(),o=0,s=a.length-1,c=a[o],u=a[s],l=10;for(u<c&&(i=c,c=u,u=i,i=o,o=s,s=i);l-- >0;){if((i=qr(c,u,n))===r)return a[o]=c,a[s]=u,e(a);if(i>0)c=Math.floor(c/i)*i,u=Math.ceil(u/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,u=Math.floor(u*i)/i}r=i}return t},t}function fa(){var t=Xi();return t.copy=function(){return Gi(t,fa())},Zi.apply(t,arguments),ha(t)}oa=function(t){var e,n,r=void 0===t.grouping||void 0===t.thousands?aa:(e=ua.call(t.grouping,Number),n=t.thousands+"",function(t,r){for(var i=t.length,a=[],o=0,s=e[0],c=0;i>0&&s>0&&(c+s+1>r&&(s=Math.max(1,r-c)),a.push(t.substring(i-=s,i+s)),!((c+=s+1)>r));)s=e[o=(o+1)%e.length];return a.reverse().join(n)}),i=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",o=void 0===t.decimal?".":t.decimal+"",s=void 0===t.numerals?aa:function(t){return function(e){return e.replace(/[0-9]/g,(function(e){return t[+e]}))}}(ua.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",u=void 0===t.minus?"−":t.minus+"",l=void 0===t.nan?"NaN":t.nan+"";function h(t){var e=(t=Ji(t)).fill,n=t.align,h=t.sign,f=t.symbol,d=t.zero,p=t.width,y=t.comma,g=t.precision,m=t.trim,v=t.type;"n"===v?(y=!0,v="g"):ia[v]||(void 0===g&&(g=12),m=!0,v="g"),(d||"0"===e&&"="===n)&&(d=!0,e="0",n="=");var b="$"===f?i:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?a:/[%p]/.test(v)?c:"",x=ia[v],w=/[defgprs%]/.test(v);function k(t){var i,a,c,f=b,k=_;if("c"===v)k=x(t)+k,t="";else{var T=(t=+t)<0||1/t<0;if(t=isNaN(t)?l:x(Math.abs(t),g),m&&(t=function(t){t:for(var e,n=t.length,r=1,i=-1;r<n;++r)switch(t[r]){case".":i=e=r;break;case"0":0===i&&(i=r),e=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(e+1):t}(t)),T&&0==+t&&"+"!==h&&(T=!1),f=(T?"("===h?h:u:"-"===h||"("===h?"":h)+f,k=("s"===v?la[8+Qi/3]:"")+k+(T&&"("===h?")":""),w)for(i=-1,a=t.length;++i<a;)if(48>(c=t.charCodeAt(i))||c>57){k=(46===c?o+t.slice(i+1):t.slice(i))+k,t=t.slice(0,i);break}}y&&!d&&(t=r(t,1/0));var E=f.length+t.length+k.length,C=E<p?new Array(p-E+1).join(e):"";switch(y&&d&&(t=r(C+t,C.length?p-k.length:1/0),C=""),n){case"<":t=f+t+k+C;break;case"=":t=f+C+t+k;break;case"^":t=C.slice(0,E=C.length>>1)+f+t+k+C.slice(E);break;default:t=C+f+t+k}return s(t)}return g=void 0===g?6:/[gprs]/.test(v)?Math.max(1,Math.min(21,g)):Math.max(0,Math.min(20,g)),k.toString=function(){return t+""},k}return{format:h,formatPrefix:function(t,e){var n=h(((t=Ji(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(na(e)/3))),i=Math.pow(10,-r),a=la[8+r/3];return function(t){return n(i*t)+a}}}}({thousands:",",grouping:[3],currency:["$",""]}),sa=oa.format,ca=oa.formatPrefix;class da extends Map{constructor(t,e=ya){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:e}}),null!=t)for(const[e,n]of t)this.set(e,n)}get(t){return super.get(pa(this,t))}has(t){return super.has(pa(this,t))}set(t,e){return super.set(function({_intern:t,_key:e},n){const r=e(n);return t.has(r)?t.get(r):(t.set(r,n),n)}(this,t),e)}delete(t){return super.delete(function({_intern:t,_key:e},n){const r=e(n);return t.has(r)&&(n=t.get(r),t.delete(r)),n}(this,t))}}function pa({_intern:t,_key:e},n){const r=e(n);return t.has(r)?t.get(r):n}function ya(t){return null!==t&&"object"==typeof t?t.valueOf():t}Set;const ga=Symbol("implicit");function ma(){var t=new da,e=[],n=[],r=ga;function i(i){let a=t.get(i);if(void 0===a){if(r!==ga)return r;t.set(i,a=e.push(i)-1)}return n[a%n.length]}return i.domain=function(n){if(!arguments.length)return e.slice();e=[],t=new da;for(const r of n)t.has(r)||t.set(r,e.push(r)-1);return i},i.range=function(t){return arguments.length?(n=Array.from(t),i):n.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return ma(e,n).unknown(r)},Zi.apply(i,arguments),i}const va=1e3,ba=6e4,_a=36e5,xa=864e5,wa=6048e5,ka=31536e6;var Ta=new Date,Ea=new Date;function Ca(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return Ca((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return Ta.setTime(+e),Ea.setTime(+r),t(Ta),t(Ea),Math.floor(n(Ta,Ea))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}var Sa=Ca((function(){}),(function(t,e){t.setTime(+t+e)}),(function(t,e){return e-t}));Sa.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Ca((function(e){e.setTime(Math.floor(e/t)*t)}),(function(e,n){e.setTime(+e+n*t)}),(function(e,n){return(n-e)/t})):Sa:null};const Aa=Sa;Sa.range;var Ma=Ca((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,e){t.setTime(+t+e*va)}),(function(t,e){return(e-t)/va}),(function(t){return t.getUTCSeconds()}));const Na=Ma;Ma.range;var Da=Ca((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*va)}),(function(t,e){t.setTime(+t+e*ba)}),(function(t,e){return(e-t)/ba}),(function(t){return t.getMinutes()}));const Oa=Da;Da.range;var Ba=Ca((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*va-t.getMinutes()*ba)}),(function(t,e){t.setTime(+t+e*_a)}),(function(t,e){return(e-t)/_a}),(function(t){return t.getHours()}));const La=Ba;Ba.range;var Ia=Ca((t=>t.setHours(0,0,0,0)),((t,e)=>t.setDate(t.getDate()+e)),((t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*ba)/xa),(t=>t.getDate()-1));const Ra=Ia;function Fa(t){return Ca((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*ba)/wa}))}Ia.range;var Pa=Fa(0),ja=Fa(1),Ya=Fa(2),za=Fa(3),Ua=Fa(4),qa=Fa(5),Ha=Fa(6),$a=(Pa.range,ja.range,Ya.range,za.range,Ua.range,qa.range,Ha.range,Ca((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,e){t.setMonth(t.getMonth()+e)}),(function(t,e){return e.getMonth()-t.getMonth()+12*(e.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})));const Wa=$a;$a.range;var Va=Ca((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));Va.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Ca((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};const Ga=Va;Va.range;var Xa=Ca((function(t){t.setUTCSeconds(0,0)}),(function(t,e){t.setTime(+t+e*ba)}),(function(t,e){return(e-t)/ba}),(function(t){return t.getUTCMinutes()}));const Za=Xa;Xa.range;var Qa=Ca((function(t){t.setUTCMinutes(0,0,0)}),(function(t,e){t.setTime(+t+e*_a)}),(function(t,e){return(e-t)/_a}),(function(t){return t.getUTCHours()}));const Ka=Qa;Qa.range;var Ja=Ca((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/xa}),(function(t){return t.getUTCDate()-1}));const to=Ja;function eo(t){return Ca((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/wa}))}Ja.range;var no=eo(0),ro=eo(1),io=eo(2),ao=eo(3),oo=eo(4),so=eo(5),co=eo(6),uo=(no.range,ro.range,io.range,ao.range,oo.range,so.range,co.range,Ca((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCMonth(t.getUTCMonth()+e)}),(function(t,e){return e.getUTCMonth()-t.getUTCMonth()+12*(e.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})));const lo=uo;uo.range;var ho=Ca((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));ho.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Ca((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};const fo=ho;function po(t,e,n,r,i,a){const o=[[Na,1,va],[Na,5,5e3],[Na,15,15e3],[Na,30,3e4],[a,1,ba],[a,5,3e5],[a,15,9e5],[a,30,18e5],[i,1,_a],[i,3,108e5],[i,6,216e5],[i,12,432e5],[r,1,xa],[r,2,1728e5],[n,1,wa],[e,1,2592e6],[e,3,7776e6],[t,1,ka]];function s(e,n,r){const i=Math.abs(n-e)/r,a=Wr((([,,t])=>t)).right(o,i);if(a===o.length)return t.every(Hr(e/ka,n/ka,r));if(0===a)return Aa.every(Math.max(Hr(e,n,r),1));const[s,c]=o[i/o[a-1][2]<o[a][2]/i?a-1:a];return s.every(c)}return[function(t,e,n){const r=e<t;r&&([t,e]=[e,t]);const i=n&&"function"==typeof n.range?n:s(t,e,n),a=i?i.range(t,+e+1):[];return r?a.reverse():a},s]}ho.range;const[yo,go]=po(fo,lo,no,to,Ka,Za),[mo,vo]=po(Ga,Wa,Pa,Ra,La,Oa);function bo(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function _o(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function xo(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}var wo,ko,To={"-":"",_:" ",0:"0"},Eo=/^\s*\d+/,Co=/^%/,So=/[\\^$*+?|[\]().{}]/g;function Ao(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function Mo(t){return t.replace(So,"\\$&")}function No(t){return new RegExp("^(?:"+t.map(Mo).join("|")+")","i")}function Do(t){return new Map(t.map(((t,e)=>[t.toLowerCase(),e])))}function Oo(t,e,n){var r=Eo.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function Bo(t,e,n){var r=Eo.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function Lo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function Io(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function Ro(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function Fo(t,e,n){var r=Eo.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function Po(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function jo(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function Yo(t,e,n){var r=Eo.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function zo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function Uo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function qo(t,e,n){var r=Eo.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function Ho(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function $o(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function Wo(t,e,n){var r=Eo.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function Vo(t,e,n){var r=Eo.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function Go(t,e,n){var r=Eo.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function Xo(t,e,n){var r=Co.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function Zo(t,e,n){var r=Eo.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function Qo(t,e,n){var r=Eo.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function Ko(t,e){return Ao(t.getDate(),e,2)}function Jo(t,e){return Ao(t.getHours(),e,2)}function ts(t,e){return Ao(t.getHours()%12||12,e,2)}function es(t,e){return Ao(1+Ra.count(Ga(t),t),e,3)}function ns(t,e){return Ao(t.getMilliseconds(),e,3)}function rs(t,e){return ns(t,e)+"000"}function is(t,e){return Ao(t.getMonth()+1,e,2)}function as(t,e){return Ao(t.getMinutes(),e,2)}function os(t,e){return Ao(t.getSeconds(),e,2)}function ss(t){var e=t.getDay();return 0===e?7:e}function cs(t,e){return Ao(Pa.count(Ga(t)-1,t),e,2)}function us(t){var e=t.getDay();return e>=4||0===e?Ua(t):Ua.ceil(t)}function ls(t,e){return t=us(t),Ao(Ua.count(Ga(t),t)+(4===Ga(t).getDay()),e,2)}function hs(t){return t.getDay()}function fs(t,e){return Ao(ja.count(Ga(t)-1,t),e,2)}function ds(t,e){return Ao(t.getFullYear()%100,e,2)}function ps(t,e){return Ao((t=us(t)).getFullYear()%100,e,2)}function ys(t,e){return Ao(t.getFullYear()%1e4,e,4)}function gs(t,e){var n=t.getDay();return Ao((t=n>=4||0===n?Ua(t):Ua.ceil(t)).getFullYear()%1e4,e,4)}function ms(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Ao(e/60|0,"0",2)+Ao(e%60,"0",2)}function vs(t,e){return Ao(t.getUTCDate(),e,2)}function bs(t,e){return Ao(t.getUTCHours(),e,2)}function _s(t,e){return Ao(t.getUTCHours()%12||12,e,2)}function xs(t,e){return Ao(1+to.count(fo(t),t),e,3)}function ws(t,e){return Ao(t.getUTCMilliseconds(),e,3)}function ks(t,e){return ws(t,e)+"000"}function Ts(t,e){return Ao(t.getUTCMonth()+1,e,2)}function Es(t,e){return Ao(t.getUTCMinutes(),e,2)}function Cs(t,e){return Ao(t.getUTCSeconds(),e,2)}function Ss(t){var e=t.getUTCDay();return 0===e?7:e}function As(t,e){return Ao(no.count(fo(t)-1,t),e,2)}function Ms(t){var e=t.getUTCDay();return e>=4||0===e?oo(t):oo.ceil(t)}function Ns(t,e){return t=Ms(t),Ao(oo.count(fo(t),t)+(4===fo(t).getUTCDay()),e,2)}function Ds(t){return t.getUTCDay()}function Os(t,e){return Ao(ro.count(fo(t)-1,t),e,2)}function Bs(t,e){return Ao(t.getUTCFullYear()%100,e,2)}function Ls(t,e){return Ao((t=Ms(t)).getUTCFullYear()%100,e,2)}function Is(t,e){return Ao(t.getUTCFullYear()%1e4,e,4)}function Rs(t,e){var n=t.getUTCDay();return Ao((t=n>=4||0===n?oo(t):oo.ceil(t)).getUTCFullYear()%1e4,e,4)}function Fs(){return"+0000"}function Ps(){return"%"}function js(t){return+t}function Ys(t){return Math.floor(+t/1e3)}function zs(t){return new Date(t)}function Us(t){return t instanceof Date?+t:+new Date(+t)}function qs(t,e,n,r,i,a,o,s,c,u){var l=Xi(),h=l.invert,f=l.domain,d=u(".%L"),p=u(":%S"),y=u("%I:%M"),g=u("%I %p"),m=u("%a %d"),v=u("%b %d"),b=u("%B"),_=u("%Y");function x(t){return(c(t)<t?d:s(t)<t?p:o(t)<t?y:a(t)<t?g:r(t)<t?i(t)<t?m:v:n(t)<t?b:_)(t)}return l.invert=function(t){return new Date(h(t))},l.domain=function(t){return arguments.length?f(Array.from(t,Us)):f().map(zs)},l.ticks=function(e){var n=f();return t(n[0],n[n.length-1],null==e?10:e)},l.tickFormat=function(t,e){return null==e?x:u(e)},l.nice=function(t){var n=f();return t&&"function"==typeof t.range||(t=e(n[0],n[n.length-1],null==t?10:t)),t?f(function(t,e){var n,r=0,i=(t=t.slice()).length-1,a=t[r],o=t[i];return o<a&&(n=r,r=i,i=n,n=a,a=o,o=n),t[r]=e.floor(a),t[i]=e.ceil(o),t}(n,t)):l},l.copy=function(){return Gi(l,qs(t,e,n,r,i,a,o,s,c,u))},l}function Hs(){}function $s(t){return null==t?Hs:function(){return this.querySelector(t)}}function Ws(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Vs(){return[]}function Gs(t){return null==t?Vs:function(){return this.querySelectorAll(t)}}function Xs(t){return function(){return this.matches(t)}}function Zs(t){return function(e){return e.matches(t)}}wo=function(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=No(i),l=Do(i),h=No(a),f=Do(a),d=No(o),p=Do(o),y=No(s),g=Do(s),m=No(c),v=Do(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:Ko,e:Ko,f:rs,g:ps,G:gs,H:Jo,I:ts,j:es,L:ns,m:is,M:as,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:js,s:Ys,S:os,u:ss,U:cs,V:ls,w:hs,W:fs,x:null,X:null,y:ds,Y:ys,Z:ms,"%":Ps},_={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:vs,e:vs,f:ks,g:Ls,G:Rs,H:bs,I:_s,j:xs,L:ws,m:Ts,M:Es,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:js,s:Ys,S:Cs,u:Ss,U:As,V:Ns,w:Ds,W:Os,x:null,X:null,y:Bs,Y:Is,Z:Fs,"%":Ps},x={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p.get(r[0].toLowerCase()),n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f.get(r[0].toLowerCase()),n+r[0].length):-1},b:function(t,e,n){var r=m.exec(e.slice(n));return r?(t.m=v.get(r[0].toLowerCase()),n+r[0].length):-1},B:function(t,e,n){var r=y.exec(e.slice(n));return r?(t.m=g.get(r[0].toLowerCase()),n+r[0].length):-1},c:function(t,n,r){return T(t,e,n,r)},d:Uo,e:Uo,f:Go,g:Po,G:Fo,H:Ho,I:Ho,j:qo,L:Vo,m:zo,M:$o,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l.get(r[0].toLowerCase()),n+r[0].length):-1},q:Yo,Q:Zo,s:Qo,S:Wo,u:Bo,U:Lo,V:Io,w:Oo,W:Ro,x:function(t,e,r){return T(t,n,e,r)},X:function(t,e,n){return T(t,r,e,n)},y:Po,Y:Fo,Z:jo,"%":Xo};function w(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=To[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function k(t,e){return function(n){var r,i,a=xo(1900,void 0,1);if(T(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(e&&!("Z"in a)&&(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=_o(xo(a.y,0,1))).getUTCDay(),r=i>4||0===i?ro.ceil(r):ro(r),r=to.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=bo(xo(a.y,0,1))).getDay(),r=i>4||0===i?ja.ceil(r):ja(r),r=Ra.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?_o(xo(a.y,0,1)).getUTCDay():bo(xo(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,_o(a)):bo(a)}}function T(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=x[i in To?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return b.x=w(n,b),b.X=w(r,b),b.c=w(e,b),_.x=w(n,_),_.X=w(r,_),_.c=w(e,_),{format:function(t){var e=w(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=k(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=w(t+="",_);return e.toString=function(){return t},e},utcParse:function(t){var e=k(t+="",!0);return e.toString=function(){return t},e}}}({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}),ko=wo.format,wo.parse,wo.utcFormat,wo.utcParse;var Qs=Array.prototype.find;function Ks(){return this.firstElementChild}var Js=Array.prototype.filter;function tc(){return Array.from(this.children)}function ec(t){return new Array(t.length)}function nc(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function rc(t){return function(){return t}}function ic(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new nc(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function ac(t,e,n,r,i,a,o){var s,c,u,l=new Map,h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u=o.call(c,c.__data__,s,e)+"",l.has(u)?i[s]=c:l.set(u,c));for(s=0;s<f;++s)u=o.call(t,a[s],s,a)+"",(c=l.get(u))?(r[s]=c,c.__data__=a[s],l.delete(u)):n[s]=new nc(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l.get(d[s])===c&&(i[s]=c)}function oc(t){return t.__data__}function sc(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function cc(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}nc.prototype={constructor:nc,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var uc="http://www.w3.org/1999/xhtml";const lc={svg:"http://www.w3.org/2000/svg",xhtml:uc,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function hc(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),lc.hasOwnProperty(e)?{space:lc[e],local:t}:t}function fc(t){return function(){this.removeAttribute(t)}}function dc(t){return function(){this.removeAttributeNS(t.space,t.local)}}function pc(t,e){return function(){this.setAttribute(t,e)}}function yc(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function gc(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function mc(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function vc(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function bc(t){return function(){this.style.removeProperty(t)}}function _c(t,e,n){return function(){this.style.setProperty(t,e,n)}}function xc(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function wc(t,e){return t.style.getPropertyValue(e)||vc(t).getComputedStyle(t,null).getPropertyValue(e)}function kc(t){return function(){delete this[t]}}function Tc(t,e){return function(){this[t]=e}}function Ec(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function Cc(t){return t.trim().split(/^|\s+/)}function Sc(t){return t.classList||new Ac(t)}function Ac(t){this._node=t,this._names=Cc(t.getAttribute("class")||"")}function Mc(t,e){for(var n=Sc(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function Nc(t,e){for(var n=Sc(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function Dc(t){return function(){Mc(this,t)}}function Oc(t){return function(){Nc(this,t)}}function Bc(t,e){return function(){(e.apply(this,arguments)?Mc:Nc)(this,t)}}function Lc(){this.textContent=""}function Ic(t){return function(){this.textContent=t}}function Rc(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function Fc(){this.innerHTML=""}function Pc(t){return function(){this.innerHTML=t}}function jc(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function Yc(){this.nextSibling&&this.parentNode.appendChild(this)}function zc(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function Uc(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===uc&&e.documentElement.namespaceURI===uc?e.createElement(t):e.createElementNS(n,t)}}function qc(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Hc(t){var e=hc(t);return(e.local?qc:Uc)(e)}function $c(){return null}function Wc(){var t=this.parentNode;t&&t.removeChild(this)}function Vc(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Gc(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Xc(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function Zc(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.options);++i?e.length=i:delete this.__on}}}function Qc(t,e,n){return function(){var r,i=this.__on,a=function(t){return function(e){t.call(this,e,this.__data__)}}(e);if(i)for(var o=0,s=i.length;o<s;++o)if((r=i[o]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=a,r.options=n),void(r.value=e);this.addEventListener(t.type,a,n),r={type:t.type,name:t.name,value:e,listener:a,options:n},i?i.push(r):this.__on=[r]}}function Kc(t,e,n){var r=vc(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function Jc(t,e){return function(){return Kc(this,t,e)}}function tu(t,e){return function(){return Kc(this,t,e.apply(this,arguments))}}Ac.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var eu=[null];function nu(t,e){this._groups=t,this._parents=e}function ru(){return new nu([[document.documentElement]],eu)}nu.prototype=ru.prototype={constructor:nu,select:function(t){"function"!=typeof t&&(t=$s(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new nu(r,this._parents)},selectAll:function(t){t="function"==typeof t?function(t){return function(){return Ws(t.apply(this,arguments))}}(t):Gs(t);for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new nu(r,i)},selectChild:function(t){return this.select(null==t?Ks:function(t){return function(){return Qs.call(this.children,t)}}("function"==typeof t?t:Zs(t)))},selectChildren:function(t){return this.selectAll(null==t?tc:function(t){return function(){return Js.call(this.children,t)}}("function"==typeof t?t:Zs(t)))},filter:function(t){"function"!=typeof t&&(t=Xs(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new nu(r,this._parents)},data:function(t,e){if(!arguments.length)return Array.from(this,oc);var n=e?ac:ic,r=this._parents,i=this._groups;"function"!=typeof t&&(t=rc(t));for(var a=i.length,o=new Array(a),s=new Array(a),c=new Array(a),u=0;u<a;++u){var l=r[u],h=i[u],f=h.length,d=sc(t.call(l,l&&l.__data__,u,r)),p=d.length,y=s[u]=new Array(p),g=o[u]=new Array(p),m=c[u]=new Array(f);n(l,h,y,g,m,d,e);for(var v,b,_=0,x=0;_<p;++_)if(v=y[_]){for(_>=x&&(x=_+1);!(b=g[x])&&++x<p;);v._next=b||null}}return(o=new nu(o,r))._enter=s,o._exit=c,o},enter:function(){return new nu(this._enter||this._groups.map(ec),this._parents)},exit:function(){return new nu(this._exit||this._groups.map(ec),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=e&&(i=e(i))&&(i=i.selection()),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=t.selection?t.selection():t,n=this._groups,r=e._groups,i=n.length,a=r.length,o=Math.min(i,a),s=new Array(i),c=0;c<o;++c)for(var u,l=n[c],h=r[c],f=l.length,d=s[c]=new Array(f),p=0;p<f;++p)(u=l[p]||h[p])&&(d[p]=u);for(;c<i;++c)s[c]=n[c];return new nu(s,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=cc);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new nu(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){let t=0;for(const e of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=hc(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?dc:fc:"function"==typeof e?n.local?mc:gc:n.local?yc:pc)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?bc:"function"==typeof e?xc:_c)(t,e,null==n?"":n)):wc(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?kc:"function"==typeof e?Ec:Tc)(t,e)):this.node()[t]},classed:function(t,e){var n=Cc(t+"");if(arguments.length<2){for(var r=Sc(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?Bc:e?Dc:Oc)(n,e))},text:function(t){return arguments.length?this.each(null==t?Lc:("function"==typeof t?Rc:Ic)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?Fc:("function"==typeof t?jc:Pc)(t)):this.node().innerHTML},raise:function(){return this.each(Yc)},lower:function(){return this.each(zc)},append:function(t){var e="function"==typeof t?t:Hc(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:Hc(t),r=null==e?$c:"function"==typeof e?e:$s(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(Wc)},clone:function(t){return this.select(t?Gc:Vc)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=Xc(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?Qc:Zc,r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?tu:Jc)(t,e))},[Symbol.iterator]:function*(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r,i=t[e],a=0,o=i.length;a<o;++a)(r=i[a])&&(yield r)}};const iu=ru;function au(t){return"string"==typeof t?new nu([[document.querySelector(t)]],[document.documentElement]):new nu([[t]],eu)}function ou(t){return"string"==typeof t?new nu([document.querySelectorAll(t)],[document.documentElement]):new nu([Ws(t)],eu)}const su=Math.PI,cu=2*su,uu=1e-6,lu=cu-uu;function hu(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function fu(){return new hu}hu.prototype=fu.prototype={constructor:hu,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,r){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+r)},bezierCurveTo:function(t,e,n,r,i,a){this._+="C"+ +t+","+ +e+","+ +n+","+ +r+","+(this._x1=+i)+","+(this._y1=+a)},arcTo:function(t,e,n,r,i){t=+t,e=+e,n=+n,r=+r,i=+i;var a=this._x1,o=this._y1,s=n-t,c=r-e,u=a-t,l=o-e,h=u*u+l*l;if(i<0)throw new Error("negative radius: "+i);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>uu)if(Math.abs(l*s-c*u)>uu&&i){var f=n-a,d=r-o,p=s*s+c*c,y=f*f+d*d,g=Math.sqrt(p),m=Math.sqrt(h),v=i*Math.tan((su-Math.acos((p+h-y)/(2*g*m)))/2),b=v/m,_=v/g;Math.abs(b-1)>uu&&(this._+="L"+(t+b*u)+","+(e+b*l)),this._+="A"+i+","+i+",0,0,"+ +(l*f>u*d)+","+(this._x1=t+_*s)+","+(this._y1=e+_*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e)},arc:function(t,e,n,r,i,a){t=+t,e=+e,a=!!a;var o=(n=+n)*Math.cos(r),s=n*Math.sin(r),c=t+o,u=e+s,l=1^a,h=a?r-i:i-r;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+u:(Math.abs(this._x1-c)>uu||Math.abs(this._y1-u)>uu)&&(this._+="L"+c+","+u),n&&(h<0&&(h=h%cu+cu),h>lu?this._+="A"+n+","+n+",0,1,"+l+","+(t-o)+","+(e-s)+"A"+n+","+n+",0,1,"+l+","+(this._x1=c)+","+(this._y1=u):h>uu&&(this._+="A"+n+","+n+",0,"+ +(h>=su)+","+l+","+(this._x1=t+n*Math.cos(i))+","+(this._y1=e+n*Math.sin(i))))},rect:function(t,e,n,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};const du=fu;function pu(t){return function(){return t}}var yu=Math.abs,gu=Math.atan2,mu=Math.cos,vu=Math.max,bu=Math.min,_u=Math.sin,xu=Math.sqrt,wu=1e-12,ku=Math.PI,Tu=ku/2,Eu=2*ku;function Cu(t){return t>1?0:t<-1?ku:Math.acos(t)}function Su(t){return t>=1?Tu:t<=-1?-Tu:Math.asin(t)}function Au(t){return t.innerRadius}function Mu(t){return t.outerRadius}function Nu(t){return t.startAngle}function Du(t){return t.endAngle}function Ou(t){return t&&t.padAngle}function Bu(t,e,n,r,i,a,o,s){var c=n-t,u=r-e,l=o-i,h=s-a,f=h*c-l*u;if(!(f*f<wu))return[t+(f=(l*(e-a)-h*(t-i))/f)*c,e+f*u]}function Lu(t,e,n,r,i,a,o){var s=t-n,c=e-r,u=(o?a:-a)/xu(s*s+c*c),l=u*c,h=-u*s,f=t+l,d=e+h,p=n+l,y=r+h,g=(f+p)/2,m=(d+y)/2,v=p-f,b=y-d,_=v*v+b*b,x=i-a,w=f*y-p*d,k=(b<0?-1:1)*xu(vu(0,x*x*_-w*w)),T=(w*b-v*k)/_,E=(-w*v-b*k)/_,C=(w*b+v*k)/_,S=(-w*v+b*k)/_,A=T-g,M=E-m,N=C-g,D=S-m;return A*A+M*M>N*N+D*D&&(T=C,E=S),{cx:T,cy:E,x01:-l,y01:-h,x11:T*(i/x-1),y11:E*(i/x-1)}}function Iu(){var t=Au,e=Mu,n=pu(0),r=null,i=Nu,a=Du,o=Ou,s=null;function c(){var c,u,l=+t.apply(this,arguments),h=+e.apply(this,arguments),f=i.apply(this,arguments)-Tu,d=a.apply(this,arguments)-Tu,p=yu(d-f),y=d>f;if(s||(s=c=du()),h<l&&(u=h,h=l,l=u),h>wu)if(p>Eu-wu)s.moveTo(h*mu(f),h*_u(f)),s.arc(0,0,h,f,d,!y),l>wu&&(s.moveTo(l*mu(d),l*_u(d)),s.arc(0,0,l,d,f,y));else{var g,m,v=f,b=d,_=f,x=d,w=p,k=p,T=o.apply(this,arguments)/2,E=T>wu&&(r?+r.apply(this,arguments):xu(l*l+h*h)),C=bu(yu(h-l)/2,+n.apply(this,arguments)),S=C,A=C;if(E>wu){var M=Su(E/l*_u(T)),N=Su(E/h*_u(T));(w-=2*M)>wu?(_+=M*=y?1:-1,x-=M):(w=0,_=x=(f+d)/2),(k-=2*N)>wu?(v+=N*=y?1:-1,b-=N):(k=0,v=b=(f+d)/2)}var D=h*mu(v),O=h*_u(v),B=l*mu(x),L=l*_u(x);if(C>wu){var I,R=h*mu(b),F=h*_u(b),P=l*mu(_),j=l*_u(_);if(p<ku&&(I=Bu(D,O,P,j,R,F,B,L))){var Y=D-I[0],z=O-I[1],U=R-I[0],q=F-I[1],H=1/_u(Cu((Y*U+z*q)/(xu(Y*Y+z*z)*xu(U*U+q*q)))/2),$=xu(I[0]*I[0]+I[1]*I[1]);S=bu(C,(l-$)/(H-1)),A=bu(C,(h-$)/(H+1))}}k>wu?A>wu?(g=Lu(P,j,D,O,h,A,y),m=Lu(R,F,B,L,h,A,y),s.moveTo(g.cx+g.x01,g.cy+g.y01),A<C?s.arc(g.cx,g.cy,A,gu(g.y01,g.x01),gu(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,A,gu(g.y01,g.x01),gu(g.y11,g.x11),!y),s.arc(0,0,h,gu(g.cy+g.y11,g.cx+g.x11),gu(m.cy+m.y11,m.cx+m.x11),!y),s.arc(m.cx,m.cy,A,gu(m.y11,m.x11),gu(m.y01,m.x01),!y))):(s.moveTo(D,O),s.arc(0,0,h,v,b,!y)):s.moveTo(D,O),l>wu&&w>wu?S>wu?(g=Lu(B,L,R,F,l,-S,y),m=Lu(D,O,P,j,l,-S,y),s.lineTo(g.cx+g.x01,g.cy+g.y01),S<C?s.arc(g.cx,g.cy,S,gu(g.y01,g.x01),gu(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,S,gu(g.y01,g.x01),gu(g.y11,g.x11),!y),s.arc(0,0,l,gu(g.cy+g.y11,g.cx+g.x11),gu(m.cy+m.y11,m.cx+m.x11),y),s.arc(m.cx,m.cy,S,gu(m.y11,m.x11),gu(m.y01,m.x01),!y))):s.arc(0,0,l,x,_,y):s.lineTo(B,L)}else s.moveTo(0,0);if(s.closePath(),c)return s=null,c+""||null}return c.centroid=function(){var n=(+t.apply(this,arguments)+ +e.apply(this,arguments))/2,r=(+i.apply(this,arguments)+ +a.apply(this,arguments))/2-ku/2;return[mu(r)*n,_u(r)*n]},c.innerRadius=function(e){return arguments.length?(t="function"==typeof e?e:pu(+e),c):t},c.outerRadius=function(t){return arguments.length?(e="function"==typeof t?t:pu(+t),c):e},c.cornerRadius=function(t){return arguments.length?(n="function"==typeof t?t:pu(+t),c):n},c.padRadius=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:pu(+t),c):r},c.startAngle=function(t){return arguments.length?(i="function"==typeof t?t:pu(+t),c):i},c.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:pu(+t),c):a},c.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:pu(+t),c):o},c.context=function(t){return arguments.length?(s=null==t?null:t,c):s},c}function Ru(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Fu(t){this._context=t}function Pu(t){return new Fu(t)}function ju(t){return t[0]}function Yu(t){return t[1]}function zu(t,e){var n=pu(!0),r=null,i=Pu,a=null;function o(o){var s,c,u,l=(o=Ru(o)).length,h=!1;for(null==r&&(a=i(u=du())),s=0;s<=l;++s)!(s<l&&n(c=o[s],s,o))===h&&((h=!h)?a.lineStart():a.lineEnd()),h&&a.point(+t(c,s,o),+e(c,s,o));if(u)return a=null,u+""||null}return t="function"==typeof t?t:void 0===t?ju:pu(t),e="function"==typeof e?e:void 0===e?Yu:pu(e),o.x=function(e){return arguments.length?(t="function"==typeof e?e:pu(+e),o):t},o.y=function(t){return arguments.length?(e="function"==typeof t?t:pu(+t),o):e},o.defined=function(t){return arguments.length?(n="function"==typeof t?t:pu(!!t),o):n},o.curve=function(t){return arguments.length?(i=t,null!=r&&(a=i(r)),o):i},o.context=function(t){return arguments.length?(null==t?r=a=null:a=i(r=t),o):r},o}function Uu(t,e){return e<t?-1:e>t?1:e>=t?0:NaN}function qu(t){return t}function Hu(){}function $u(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function Wu(t){this._context=t}function Vu(t){return new Wu(t)}function Gu(t){this._context=t}function Xu(t){this._context=t}function Zu(t){this._context=t}function Qu(t){return t<0?-1:1}function Ku(t,e,n){var r=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(r||i<0&&-0),o=(n-t._y1)/(i||r<0&&-0),s=(a*i+o*r)/(r+i);return(Qu(a)+Qu(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(s))||0}function Ju(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function tl(t,e,n){var r=t._x0,i=t._y0,a=t._x1,o=t._y1,s=(a-r)/3;t._context.bezierCurveTo(r+s,i+s*e,a-s,o-s*n,a,o)}function el(t){this._context=t}function nl(t){this._context=new rl(t)}function rl(t){this._context=t}function il(t){this._context=t}function al(t){var e,n,r=t.length-1,i=new Array(r),a=new Array(r),o=new Array(r);for(i[0]=0,a[0]=2,o[0]=t[0]+2*t[1],e=1;e<r-1;++e)i[e]=1,a[e]=4,o[e]=4*t[e]+2*t[e+1];for(i[r-1]=2,a[r-1]=7,o[r-1]=8*t[r-1]+t[r],e=1;e<r;++e)n=i[e]/a[e-1],a[e]-=n,o[e]-=n*o[e-1];for(i[r-1]=o[r-1]/a[r-1],e=r-2;e>=0;--e)i[e]=(o[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e<r-1;++e)a[e]=2*t[e+1]-i[e+1];return[i,a]}function ol(t,e){this._context=t,this._t=e}Array.prototype.slice,Fu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:this._context.lineTo(t,e)}}},Wu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:$u(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:$u(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},Gu.prototype={areaStart:Hu,areaEnd:Hu,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:$u(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},Xu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:$u(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},Zu.prototype={areaStart:Hu,areaEnd:Hu,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}},el.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:tl(this,this._t0,Ju(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(e=+e,(t=+t)!==this._x1||e!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,tl(this,Ju(this,n=Ku(this,t,e)),n);break;default:tl(this,this._t0,n=Ku(this,t,e))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}},(nl.prototype=Object.create(el.prototype)).point=function(t,e){el.prototype.point.call(this,e,t)},rl.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,r,i,a){this._context.bezierCurveTo(e,t,r,n,a,i)}},il.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),2===n)this._context.lineTo(t[1],e[1]);else for(var r=al(t),i=al(e),a=0,o=1;o<n;++a,++o)this._context.bezierCurveTo(r[0][a],i[0][a],r[1][a],i[1][a],t[o],e[o]);(this._line||0!==this._line&&1===n)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(t,e){this._x.push(+t),this._y.push(+e)}},ol.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&2===this._point&&this._context.lineTo(this._x,this._y),(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var sl=new Date,cl=new Date;function ul(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return ul((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return sl.setTime(+e),cl.setTime(+r),t(sl),t(cl),Math.floor(n(sl,cl))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}const ll=864e5,hl=6048e5;function fl(t){return ul((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/hl}))}var dl=fl(0),pl=fl(1),yl=fl(2),gl=fl(3),ml=fl(4),vl=fl(5),bl=fl(6),_l=(dl.range,pl.range,yl.range,gl.range,ml.range,vl.range,bl.range,ul((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/ll}),(function(t){return t.getUTCDate()-1})));const xl=_l;function wl(t){return ul((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/hl}))}_l.range;var kl=wl(0),Tl=wl(1),El=wl(2),Cl=wl(3),Sl=wl(4),Al=wl(5),Ml=wl(6),Nl=(kl.range,Tl.range,El.range,Cl.range,Sl.range,Al.range,Ml.range,ul((t=>t.setHours(0,0,0,0)),((t,e)=>t.setDate(t.getDate()+e)),((t,e)=>(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/ll),(t=>t.getDate()-1)));const Dl=Nl;Nl.range;var Ol=ul((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));Ol.every=function(t){return isFinite(t=Math.floor(t))&&t>0?ul((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};const Bl=Ol;Ol.range;var Ll=ul((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));Ll.every=function(t){return isFinite(t=Math.floor(t))&&t>0?ul((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};const Il=Ll;function Rl(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function Fl(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Pl(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}Ll.range;var jl,Yl,zl={"-":"",_:" ",0:"0"},Ul=/^\s*\d+/,ql=/^%/,Hl=/[\\^$*+?|[\]().{}]/g;function $l(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function Wl(t){return t.replace(Hl,"\\$&")}function Vl(t){return new RegExp("^(?:"+t.map(Wl).join("|")+")","i")}function Gl(t){return new Map(t.map(((t,e)=>[t.toLowerCase(),e])))}function Xl(t,e,n){var r=Ul.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function Zl(t,e,n){var r=Ul.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function Ql(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function Kl(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function Jl(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function th(t,e,n){var r=Ul.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function eh(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function nh(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function rh(t,e,n){var r=Ul.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function ih(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function ah(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function oh(t,e,n){var r=Ul.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function sh(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function ch(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function uh(t,e,n){var r=Ul.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function lh(t,e,n){var r=Ul.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function hh(t,e,n){var r=Ul.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function fh(t,e,n){var r=ql.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function dh(t,e,n){var r=Ul.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function ph(t,e,n){var r=Ul.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function yh(t,e){return $l(t.getDate(),e,2)}function gh(t,e){return $l(t.getHours(),e,2)}function mh(t,e){return $l(t.getHours()%12||12,e,2)}function vh(t,e){return $l(1+Dl.count(Bl(t),t),e,3)}function bh(t,e){return $l(t.getMilliseconds(),e,3)}function _h(t,e){return bh(t,e)+"000"}function xh(t,e){return $l(t.getMonth()+1,e,2)}function wh(t,e){return $l(t.getMinutes(),e,2)}function kh(t,e){return $l(t.getSeconds(),e,2)}function Th(t){var e=t.getDay();return 0===e?7:e}function Eh(t,e){return $l(kl.count(Bl(t)-1,t),e,2)}function Ch(t){var e=t.getDay();return e>=4||0===e?Sl(t):Sl.ceil(t)}function Sh(t,e){return t=Ch(t),$l(Sl.count(Bl(t),t)+(4===Bl(t).getDay()),e,2)}function Ah(t){return t.getDay()}function Mh(t,e){return $l(Tl.count(Bl(t)-1,t),e,2)}function Nh(t,e){return $l(t.getFullYear()%100,e,2)}function Dh(t,e){return $l((t=Ch(t)).getFullYear()%100,e,2)}function Oh(t,e){return $l(t.getFullYear()%1e4,e,4)}function Bh(t,e){var n=t.getDay();return $l((t=n>=4||0===n?Sl(t):Sl.ceil(t)).getFullYear()%1e4,e,4)}function Lh(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+$l(e/60|0,"0",2)+$l(e%60,"0",2)}function Ih(t,e){return $l(t.getUTCDate(),e,2)}function Rh(t,e){return $l(t.getUTCHours(),e,2)}function Fh(t,e){return $l(t.getUTCHours()%12||12,e,2)}function Ph(t,e){return $l(1+xl.count(Il(t),t),e,3)}function jh(t,e){return $l(t.getUTCMilliseconds(),e,3)}function Yh(t,e){return jh(t,e)+"000"}function zh(t,e){return $l(t.getUTCMonth()+1,e,2)}function Uh(t,e){return $l(t.getUTCMinutes(),e,2)}function qh(t,e){return $l(t.getUTCSeconds(),e,2)}function Hh(t){var e=t.getUTCDay();return 0===e?7:e}function $h(t,e){return $l(dl.count(Il(t)-1,t),e,2)}function Wh(t){var e=t.getUTCDay();return e>=4||0===e?ml(t):ml.ceil(t)}function Vh(t,e){return t=Wh(t),$l(ml.count(Il(t),t)+(4===Il(t).getUTCDay()),e,2)}function Gh(t){return t.getUTCDay()}function Xh(t,e){return $l(pl.count(Il(t)-1,t),e,2)}function Zh(t,e){return $l(t.getUTCFullYear()%100,e,2)}function Qh(t,e){return $l((t=Wh(t)).getUTCFullYear()%100,e,2)}function Kh(t,e){return $l(t.getUTCFullYear()%1e4,e,4)}function Jh(t,e){var n=t.getUTCDay();return $l((t=n>=4||0===n?ml(t):ml.ceil(t)).getUTCFullYear()%1e4,e,4)}function tf(){return"+0000"}function ef(){return"%"}function nf(t){return+t}function rf(t){return Math.floor(+t/1e3)}jl=function(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=Vl(i),l=Gl(i),h=Vl(a),f=Gl(a),d=Vl(o),p=Gl(o),y=Vl(s),g=Gl(s),m=Vl(c),v=Gl(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:yh,e:yh,f:_h,g:Dh,G:Bh,H:gh,I:mh,j:vh,L:bh,m:xh,M:wh,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:nf,s:rf,S:kh,u:Th,U:Eh,V:Sh,w:Ah,W:Mh,x:null,X:null,y:Nh,Y:Oh,Z:Lh,"%":ef},_={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:Ih,e:Ih,f:Yh,g:Qh,G:Jh,H:Rh,I:Fh,j:Ph,L:jh,m:zh,M:Uh,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:nf,s:rf,S:qh,u:Hh,U:$h,V:Vh,w:Gh,W:Xh,x:null,X:null,y:Zh,Y:Kh,Z:tf,"%":ef},x={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p.get(r[0].toLowerCase()),n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f.get(r[0].toLowerCase()),n+r[0].length):-1},b:function(t,e,n){var r=m.exec(e.slice(n));return r?(t.m=v.get(r[0].toLowerCase()),n+r[0].length):-1},B:function(t,e,n){var r=y.exec(e.slice(n));return r?(t.m=g.get(r[0].toLowerCase()),n+r[0].length):-1},c:function(t,n,r){return T(t,e,n,r)},d:ah,e:ah,f:hh,g:eh,G:th,H:sh,I:sh,j:oh,L:lh,m:ih,M:ch,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l.get(r[0].toLowerCase()),n+r[0].length):-1},q:rh,Q:dh,s:ph,S:uh,u:Zl,U:Ql,V:Kl,w:Xl,W:Jl,x:function(t,e,r){return T(t,n,e,r)},X:function(t,e,n){return T(t,r,e,n)},y:eh,Y:th,Z:nh,"%":fh};function w(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=zl[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function k(t,e){return function(n){var r,i,a=Pl(1900,void 0,1);if(T(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(e&&!("Z"in a)&&(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=Fl(Pl(a.y,0,1))).getUTCDay(),r=i>4||0===i?pl.ceil(r):pl(r),r=xl.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=Rl(Pl(a.y,0,1))).getDay(),r=i>4||0===i?Tl.ceil(r):Tl(r),r=Dl.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?Fl(Pl(a.y,0,1)).getUTCDay():Rl(Pl(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,Fl(a)):Rl(a)}}function T(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=x[i in zl?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return b.x=w(n,b),b.X=w(r,b),b.c=w(e,b),_.x=w(n,_),_.X=w(r,_),_.c=w(e,_),{format:function(t){var e=w(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=k(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=w(t+="",_);return e.toString=function(){return t},e},utcParse:function(t){var e=k(t+="",!0);return e.toString=function(){return t},e}}}({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}),Yl=jl.format,jl.parse,jl.utcFormat,jl.utcParse;var af={value:()=>{}};function of(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new sf(r)}function sf(t){this._=t}function cf(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function uf(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function lf(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=af,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}sf.prototype=of.prototype={constructor:sf,on:function(t,e){var n,r=this._,i=cf(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=lf(r[n],t.name,e);else if(null==e)for(n in r)r[n]=lf(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=uf(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new sf(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const hf=of;var ff,df,pf=0,yf=0,gf=0,mf=0,vf=0,bf=0,_f="object"==typeof performance&&performance.now?performance:Date,xf="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function wf(){return vf||(xf(kf),vf=_f.now()+bf)}function kf(){vf=0}function Tf(){this._call=this._time=this._next=null}function Ef(t,e,n){var r=new Tf;return r.restart(t,e,n),r}function Cf(){vf=(mf=_f.now())+bf,pf=yf=0;try{!function(){wf(),++pf;for(var t,e=ff;e;)(t=vf-e._time)>=0&&e._call.call(void 0,t),e=e._next;--pf}()}finally{pf=0,function(){for(var t,e,n=ff,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:ff=e);df=t,Af(r)}(),vf=0}}function Sf(){var t=_f.now(),e=t-mf;e>1e3&&(bf-=e,mf=t)}function Af(t){pf||(yf&&(yf=clearTimeout(yf)),t-vf>24?(t<1/0&&(yf=setTimeout(Cf,t-_f.now()-bf)),gf&&(gf=clearInterval(gf))):(gf||(mf=_f.now(),gf=setInterval(Sf,1e3)),pf=1,xf(Cf)))}function Mf(t,e,n){var r=new Tf;return e=null==e?0:+e,r.restart((n=>{r.stop(),t(n+e)}),e,n),r}Tf.prototype=Ef.prototype={constructor:Tf,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?wf():+n)+(null==e?0:+e),this._next||df===this||(df?df._next=this:ff=this,df=this),this._call=t,this._time=n,Af()},stop:function(){this._call&&(this._call=null,this._time=1/0,Af())}};var Nf=hf("start","end","cancel","interrupt"),Df=[];function Of(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return Mf(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(Mf((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=Ef((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:Nf,tween:Df,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function Bf(t,e){var n=If(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function Lf(t,e){var n=If(t,e);if(n.state>3)throw new Error("too late; already running");return n}function If(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function Rf(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}var Ff,Pf=180/Math.PI,jf={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Yf(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*Pf,skewX:Math.atan(c)*Pf,scaleX:o,scaleY:s}}function zf(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:Rf(t,i)},{i:c-2,x:Rf(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:Rf(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:Rf(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:Rf(t,n)},{i:s-2,x:Rf(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var Uf=zf((function(t){const e=new("function"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+"");return e.isIdentity?jf:Yf(e.a,e.b,e.c,e.d,e.e,e.f)}),"px, ","px)","deg)"),qf=zf((function(t){return null==t?jf:(Ff||(Ff=document.createElementNS("http://www.w3.org/2000/svg","g")),Ff.setAttribute("transform",t),(t=Ff.transform.baseVal.consolidate())?Yf((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):jf)}),", ",")",")");function Hf(t,e){var n,r;return function(){var i=Lf(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function $f(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=Lf(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function Wf(t,e,n){var r=t._id;return t.each((function(){var t=Lf(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return If(t,r).value[e]}}function Vf(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}const Gf=function t(e){var n=function(t){return 1==(t=+t)?Fr:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):Ir(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=ur(t)).r,(e=ur(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=Fr(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function Xf(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=ur(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}Xf((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return Vf((n-r/e)*e,o,i,a,s)}})),Xf((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return Vf((n-r/e)*e,i,a,o,s)}}));var Zf=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Qf=new RegExp(Zf.source,"g");function Kf(t,e){var n,r,i,a=Zf.lastIndex=Qf.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=Zf.exec(t))&&(r=Qf.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:Rf(n,r)})),a=Qf.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function Jf(t,e){var n;return("number"==typeof e?Rf:e instanceof ar?Gf:(n=ar(e))?(e=n,Gf):Kf)(t,e)}function td(t){return function(){this.removeAttribute(t)}}function ed(t){return function(){this.removeAttributeNS(t.space,t.local)}}function nd(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function rd(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function id(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function ad(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function od(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function sd(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function cd(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&sd(t,i)),n}return i._value=e,i}function ud(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&od(t,i)),n}return i._value=e,i}function ld(t,e){return function(){Bf(this,t).delay=+e.apply(this,arguments)}}function hd(t,e){return e=+e,function(){Bf(this,t).delay=e}}function fd(t,e){return function(){Lf(this,t).duration=+e.apply(this,arguments)}}function dd(t,e){return e=+e,function(){Lf(this,t).duration=e}}function pd(t,e){if("function"!=typeof e)throw new Error;return function(){Lf(this,t).ease=e}}function yd(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?Bf:Lf;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var gd=iu.prototype.constructor;function md(t){return function(){this.style.removeProperty(t)}}function vd(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function bd(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&vd(t,a,n)),r}return a._value=e,a}function _d(t){return function(e){this.textContent=t.call(this,e)}}function xd(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&_d(r)),e}return r._value=t,r}var wd=0;function kd(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function Td(){return++wd}var Ed=iu.prototype;kd.prototype=function(t){return iu().transition(t)}.prototype={constructor:kd,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=$s(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,Of(h[f],e,n,f,h,If(s,n)));return new kd(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=Gs(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=If(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&Of(f,e,n,y,d,p);a.push(d),o.push(c)}return new kd(a,o,e,n)},selectChild:Ed.selectChild,selectChildren:Ed.selectChildren,filter:function(t){"function"!=typeof t&&(t=Xs(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new kd(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new kd(o,this._parents,this._name,this._id)},selection:function(){return new gd(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=Td(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=If(o,e);Of(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new kd(r,this._parents,t,n)},call:Ed.call,nodes:Ed.nodes,node:Ed.node,size:Ed.size,empty:Ed.empty,each:Ed.each,on:function(t,e){var n=this._id;return arguments.length<2?If(this.node(),n).on.on(t):this.each(yd(n,t,e))},attr:function(t,e){var n=hc(t),r="transform"===n?qf:Jf;return this.attrTween(t,"function"==typeof e?(n.local?ad:id)(n,r,Wf(this,"attr."+t,e)):null==e?(n.local?ed:td)(n):(n.local?rd:nd)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=hc(t);return this.tween(n,(r.local?cd:ud)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?Uf:Jf;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=wc(this,t),o=(this.style.removeProperty(t),wc(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,md(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=wc(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=wc(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,Wf(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=Lf(this,t),u=c.on,l=null==c.value[o]?a||(a=md(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=wc(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,bd(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(Wf(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,xd(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=If(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?Hf:$f)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?ld:hd)(e,t)):If(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?fd:dd)(e,t)):If(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(pd(e,t)):If(this.node(),e).ease},easeVarying:function(t){if("function"!=typeof t)throw new Error;return this.each(function(t,e){return function(){var n=e.apply(this,arguments);if("function"!=typeof n)throw new Error;Lf(this,t).ease=n}}(this._id,t))},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=Lf(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e})),0===i&&a()}))},[Symbol.iterator]:Ed[Symbol.iterator]};var Cd={time:null,delay:0,duration:250,ease:function(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}};function Sd(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))throw new Error(`transition ${e} not found`);return n}function Ad(){}function Md(t){return null==t?Ad:function(){return this.querySelector(t)}}function Nd(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Dd(){return[]}function Od(t){return null==t?Dd:function(){return this.querySelectorAll(t)}}function Bd(t){return function(){return this.matches(t)}}function Ld(t){return function(e){return e.matches(t)}}iu.prototype.interrupt=function(t){return this.each((function(){!function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}(this,t)}))},iu.prototype.transition=function(t){var e,n;t instanceof kd?(e=t._id,t=t._name):(e=Td(),(n=Cd).time=wf(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&Of(o,t,e,u,s,n||Sd(o,e));return new kd(r,this._parents,t,e)};var Id=Array.prototype.find;function Rd(){return this.firstElementChild}var Fd=Array.prototype.filter;function Pd(){return Array.from(this.children)}function jd(t){return new Array(t.length)}function Yd(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function zd(t){return function(){return t}}function Ud(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new Yd(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function qd(t,e,n,r,i,a,o){var s,c,u,l=new Map,h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u=o.call(c,c.__data__,s,e)+"",l.has(u)?i[s]=c:l.set(u,c));for(s=0;s<f;++s)u=o.call(t,a[s],s,a)+"",(c=l.get(u))?(r[s]=c,c.__data__=a[s],l.delete(u)):n[s]=new Yd(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l.get(d[s])===c&&(i[s]=c)}function Hd(t){return t.__data__}function $d(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Wd(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}Yd.prototype={constructor:Yd,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var Vd="http://www.w3.org/1999/xhtml";const Gd={svg:"http://www.w3.org/2000/svg",xhtml:Vd,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Xd(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),Gd.hasOwnProperty(e)?{space:Gd[e],local:t}:t}function Zd(t){return function(){this.removeAttribute(t)}}function Qd(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Kd(t,e){return function(){this.setAttribute(t,e)}}function Jd(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function tp(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function ep(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function np(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function rp(t){return function(){this.style.removeProperty(t)}}function ip(t,e,n){return function(){this.style.setProperty(t,e,n)}}function ap(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function op(t,e){return t.style.getPropertyValue(e)||np(t).getComputedStyle(t,null).getPropertyValue(e)}function sp(t){return function(){delete this[t]}}function cp(t,e){return function(){this[t]=e}}function up(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function lp(t){return t.trim().split(/^|\s+/)}function hp(t){return t.classList||new fp(t)}function fp(t){this._node=t,this._names=lp(t.getAttribute("class")||"")}function dp(t,e){for(var n=hp(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function pp(t,e){for(var n=hp(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function yp(t){return function(){dp(this,t)}}function gp(t){return function(){pp(this,t)}}function mp(t,e){return function(){(e.apply(this,arguments)?dp:pp)(this,t)}}function vp(){this.textContent=""}function bp(t){return function(){this.textContent=t}}function _p(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function xp(){this.innerHTML=""}function wp(t){return function(){this.innerHTML=t}}function kp(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function Tp(){this.nextSibling&&this.parentNode.appendChild(this)}function Ep(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function Cp(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===Vd&&e.documentElement.namespaceURI===Vd?e.createElement(t):e.createElementNS(n,t)}}function Sp(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Ap(t){var e=Xd(t);return(e.local?Sp:Cp)(e)}function Mp(){return null}function Np(){var t=this.parentNode;t&&t.removeChild(this)}function Dp(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Op(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function Bp(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function Lp(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.options);++i?e.length=i:delete this.__on}}}function Ip(t,e,n){return function(){var r,i=this.__on,a=function(t){return function(e){t.call(this,e,this.__data__)}}(e);if(i)for(var o=0,s=i.length;o<s;++o)if((r=i[o]).type===t.type&&r.name===t.name)return this.removeEventListener(r.type,r.listener,r.options),this.addEventListener(r.type,r.listener=a,r.options=n),void(r.value=e);this.addEventListener(t.type,a,n),r={type:t.type,name:t.name,value:e,listener:a,options:n},i?i.push(r):this.__on=[r]}}function Rp(t,e,n){var r=np(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function Fp(t,e){return function(){return Rp(this,t,e)}}function Pp(t,e){return function(){return Rp(this,t,e.apply(this,arguments))}}fp.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var jp=[null];function Yp(t,e){this._groups=t,this._parents=e}function zp(){return new Yp([[document.documentElement]],jp)}Yp.prototype=zp.prototype={constructor:Yp,select:function(t){"function"!=typeof t&&(t=Md(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new Yp(r,this._parents)},selectAll:function(t){t="function"==typeof t?function(t){return function(){return Nd(t.apply(this,arguments))}}(t):Od(t);for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new Yp(r,i)},selectChild:function(t){return this.select(null==t?Rd:function(t){return function(){return Id.call(this.children,t)}}("function"==typeof t?t:Ld(t)))},selectChildren:function(t){return this.selectAll(null==t?Pd:function(t){return function(){return Fd.call(this.children,t)}}("function"==typeof t?t:Ld(t)))},filter:function(t){"function"!=typeof t&&(t=Bd(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Yp(r,this._parents)},data:function(t,e){if(!arguments.length)return Array.from(this,Hd);var n=e?qd:Ud,r=this._parents,i=this._groups;"function"!=typeof t&&(t=zd(t));for(var a=i.length,o=new Array(a),s=new Array(a),c=new Array(a),u=0;u<a;++u){var l=r[u],h=i[u],f=h.length,d=$d(t.call(l,l&&l.__data__,u,r)),p=d.length,y=s[u]=new Array(p),g=o[u]=new Array(p),m=c[u]=new Array(f);n(l,h,y,g,m,d,e);for(var v,b,_=0,x=0;_<p;++_)if(v=y[_]){for(_>=x&&(x=_+1);!(b=g[x])&&++x<p;);v._next=b||null}}return(o=new Yp(o,r))._enter=s,o._exit=c,o},enter:function(){return new Yp(this._enter||this._groups.map(jd),this._parents)},exit:function(){return new Yp(this._exit||this._groups.map(jd),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=e&&(i=e(i))&&(i=i.selection()),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=t.selection?t.selection():t,n=this._groups,r=e._groups,i=n.length,a=r.length,o=Math.min(i,a),s=new Array(i),c=0;c<o;++c)for(var u,l=n[c],h=r[c],f=l.length,d=s[c]=new Array(f),p=0;p<f;++p)(u=l[p]||h[p])&&(d[p]=u);for(;c<i;++c)s[c]=n[c];return new Yp(s,this._parents)},selection:function(){return this},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=Wd);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new Yp(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){return Array.from(this)},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){let t=0;for(const e of this)++t;return t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=Xd(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?Qd:Zd:"function"==typeof e?n.local?ep:tp:n.local?Jd:Kd)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?rp:"function"==typeof e?ap:ip)(t,e,null==n?"":n)):op(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?sp:"function"==typeof e?up:cp)(t,e)):this.node()[t]},classed:function(t,e){var n=lp(t+"");if(arguments.length<2){for(var r=hp(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?mp:e?yp:gp)(n,e))},text:function(t){return arguments.length?this.each(null==t?vp:("function"==typeof t?_p:bp)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?xp:("function"==typeof t?kp:wp)(t)):this.node().innerHTML},raise:function(){return this.each(Tp)},lower:function(){return this.each(Ep)},append:function(t){var e="function"==typeof t?t:Ap(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:Ap(t),r=null==e?Mp:"function"==typeof e?e:Md(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(Np)},clone:function(t){return this.select(t?Op:Dp)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=Bp(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?Ip:Lp,r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?Pp:Fp)(t,e))},[Symbol.iterator]:function*(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r,i=t[e],a=0,o=i.length;a<o;++a)(r=i[a])&&(yield r)}};const Up=zp;var qp={value:()=>{}};function Hp(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new $p(r)}function $p(t){this._=t}function Wp(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function Vp(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function Gp(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=qp,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}$p.prototype=Hp.prototype={constructor:$p,on:function(t,e){var n,r=this._,i=Wp(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=Gp(r[n],t.name,e);else if(null==e)for(n in r)r[n]=Gp(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=Vp(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new $p(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const Xp=Hp;var Zp,Qp,Kp=0,Jp=0,ty=0,ey=0,ny=0,ry=0,iy="object"==typeof performance&&performance.now?performance:Date,ay="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function oy(){return ny||(ay(sy),ny=iy.now()+ry)}function sy(){ny=0}function cy(){this._call=this._time=this._next=null}function uy(t,e,n){var r=new cy;return r.restart(t,e,n),r}function ly(){ny=(ey=iy.now())+ry,Kp=Jp=0;try{!function(){oy(),++Kp;for(var t,e=Zp;e;)(t=ny-e._time)>=0&&e._call.call(void 0,t),e=e._next;--Kp}()}finally{Kp=0,function(){for(var t,e,n=Zp,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:Zp=e);Qp=t,fy(r)}(),ny=0}}function hy(){var t=iy.now(),e=t-ey;e>1e3&&(ry-=e,ey=t)}function fy(t){Kp||(Jp&&(Jp=clearTimeout(Jp)),t-ny>24?(t<1/0&&(Jp=setTimeout(ly,t-iy.now()-ry)),ty&&(ty=clearInterval(ty))):(ty||(ey=iy.now(),ty=setInterval(hy,1e3)),Kp=1,ay(ly)))}function dy(t,e,n){var r=new cy;return e=null==e?0:+e,r.restart((n=>{r.stop(),t(n+e)}),e,n),r}cy.prototype=uy.prototype={constructor:cy,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?oy():+n)+(null==e?0:+e),this._next||Qp===this||(Qp?Qp._next=this:Zp=this,Qp=this),this._call=t,this._time=n,fy()},stop:function(){this._call&&(this._call=null,this._time=1/0,fy())}};var py=Xp("start","end","cancel","interrupt"),yy=[];function gy(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return dy(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(dy((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=uy((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:py,tween:yy,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function my(t,e){var n=by(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function vy(t,e){var n=by(t,e);if(n.state>3)throw new Error("too late; already running");return n}function by(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function _y(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}var xy,wy=180/Math.PI,ky={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Ty(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*wy,skewX:Math.atan(c)*wy,scaleX:o,scaleY:s}}function Ey(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:_y(t,i)},{i:c-2,x:_y(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:_y(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:_y(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:_y(t,n)},{i:s-2,x:_y(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var Cy=Ey((function(t){const e=new("function"==typeof DOMMatrix?DOMMatrix:WebKitCSSMatrix)(t+"");return e.isIdentity?ky:Ty(e.a,e.b,e.c,e.d,e.e,e.f)}),"px, ","px)","deg)"),Sy=Ey((function(t){return null==t?ky:(xy||(xy=document.createElementNS("http://www.w3.org/2000/svg","g")),xy.setAttribute("transform",t),(t=xy.transform.baseVal.consolidate())?Ty((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):ky)}),", ",")",")");function Ay(t,e){var n,r;return function(){var i=vy(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function My(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=vy(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function Ny(t,e,n){var r=t._id;return t.each((function(){var t=vy(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return by(t,r).value[e]}}function Dy(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Oy(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function By(){}var Ly=.7,Iy=1.4285714285714286,Ry="\\s*([+-]?\\d+)\\s*",Fy="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Py="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",jy=/^#([0-9a-f]{3,8})$/,Yy=new RegExp("^rgb\\("+[Ry,Ry,Ry]+"\\)$"),zy=new RegExp("^rgb\\("+[Py,Py,Py]+"\\)$"),Uy=new RegExp("^rgba\\("+[Ry,Ry,Ry,Fy]+"\\)$"),qy=new RegExp("^rgba\\("+[Py,Py,Py,Fy]+"\\)$"),Hy=new RegExp("^hsl\\("+[Fy,Py,Py]+"\\)$"),$y=new RegExp("^hsla\\("+[Fy,Py,Py,Fy]+"\\)$"),Wy={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function Vy(){return this.rgb().formatHex()}function Gy(){return this.rgb().formatRgb()}function Xy(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=jy.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?Zy(e):3===n?new tg(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?Qy(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?Qy(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Yy.exec(t))?new tg(e[1],e[2],e[3],1):(e=zy.exec(t))?new tg(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Uy.exec(t))?Qy(e[1],e[2],e[3],e[4]):(e=qy.exec(t))?Qy(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=Hy.exec(t))?ig(e[1],e[2]/100,e[3]/100,1):(e=$y.exec(t))?ig(e[1],e[2]/100,e[3]/100,e[4]):Wy.hasOwnProperty(t)?Zy(Wy[t]):"transparent"===t?new tg(NaN,NaN,NaN,0):null}function Zy(t){return new tg(t>>16&255,t>>8&255,255&t,1)}function Qy(t,e,n,r){return r<=0&&(t=e=n=NaN),new tg(t,e,n,r)}function Ky(t){return t instanceof By||(t=Xy(t)),t?new tg((t=t.rgb()).r,t.g,t.b,t.opacity):new tg}function Jy(t,e,n,r){return 1===arguments.length?Ky(t):new tg(t,e,n,null==r?1:r)}function tg(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function eg(){return"#"+rg(this.r)+rg(this.g)+rg(this.b)}function ng(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function rg(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function ig(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new og(t,e,n,r)}function ag(t){if(t instanceof og)return new og(t.h,t.s,t.l,t.opacity);if(t instanceof By||(t=Xy(t)),!t)return new og;if(t instanceof og)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new og(o,s,c,t.opacity)}function og(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function sg(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function cg(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}Dy(By,Xy,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:Vy,formatHex:Vy,formatHsl:function(){return ag(this).formatHsl()},formatRgb:Gy,toString:Gy}),Dy(tg,Jy,Oy(By,{brighter:function(t){return t=null==t?Iy:Math.pow(Iy,t),new tg(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Ly:Math.pow(Ly,t),new tg(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:eg,formatHex:eg,formatRgb:ng,toString:ng})),Dy(og,(function(t,e,n,r){return 1===arguments.length?ag(t):new og(t,e,n,null==r?1:r)}),Oy(By,{brighter:function(t){return t=null==t?Iy:Math.pow(Iy,t),new og(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Ly:Math.pow(Ly,t),new og(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new tg(sg(t>=240?t-240:t+120,i,r),sg(t,i,r),sg(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const ug=t=>()=>t;function lg(t,e){var n=e-t;return n?function(t,e){return function(n){return t+n*e}}(t,n):ug(isNaN(t)?e:t)}const hg=function t(e){var n=function(t){return 1==(t=+t)?lg:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):ug(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=Jy(t)).r,(e=Jy(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=lg(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function fg(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=Jy(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}fg((function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return cg((n-r/e)*e,o,i,a,s)}})),fg((function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return cg((n-r/e)*e,i,a,o,s)}}));var dg=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pg=new RegExp(dg.source,"g");function yg(t,e){var n,r,i,a=dg.lastIndex=pg.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=dg.exec(t))&&(r=pg.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:_y(n,r)})),a=pg.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function gg(t,e){var n;return("number"==typeof e?_y:e instanceof Xy?hg:(n=Xy(e))?(e=n,hg):yg)(t,e)}function mg(t){return function(){this.removeAttribute(t)}}function vg(t){return function(){this.removeAttributeNS(t.space,t.local)}}function bg(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function _g(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function xg(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function wg(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function kg(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function Tg(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function Eg(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Tg(t,i)),n}return i._value=e,i}function Cg(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&kg(t,i)),n}return i._value=e,i}function Sg(t,e){return function(){my(this,t).delay=+e.apply(this,arguments)}}function Ag(t,e){return e=+e,function(){my(this,t).delay=e}}function Mg(t,e){return function(){vy(this,t).duration=+e.apply(this,arguments)}}function Ng(t,e){return e=+e,function(){vy(this,t).duration=e}}function Dg(t,e){if("function"!=typeof e)throw new Error;return function(){vy(this,t).ease=e}}function Og(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?my:vy;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var Bg=Up.prototype.constructor;function Lg(t){return function(){this.style.removeProperty(t)}}function Ig(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function Rg(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Ig(t,a,n)),r}return a._value=e,a}function Fg(t){return function(e){this.textContent=t.call(this,e)}}function Pg(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Fg(r)),e}return r._value=t,r}var jg=0;function Yg(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function zg(){return++jg}var Ug=Up.prototype;Yg.prototype=function(t){return Up().transition(t)}.prototype={constructor:Yg,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=Md(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,gy(h[f],e,n,f,h,by(s,n)));return new Yg(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=Od(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=by(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&gy(f,e,n,y,d,p);a.push(d),o.push(c)}return new Yg(a,o,e,n)},selectChild:Ug.selectChild,selectChildren:Ug.selectChildren,filter:function(t){"function"!=typeof t&&(t=Bd(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Yg(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new Yg(o,this._parents,this._name,this._id)},selection:function(){return new Bg(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=zg(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=by(o,e);gy(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new Yg(r,this._parents,t,n)},call:Ug.call,nodes:Ug.nodes,node:Ug.node,size:Ug.size,empty:Ug.empty,each:Ug.each,on:function(t,e){var n=this._id;return arguments.length<2?by(this.node(),n).on.on(t):this.each(Og(n,t,e))},attr:function(t,e){var n=Xd(t),r="transform"===n?Sy:gg;return this.attrTween(t,"function"==typeof e?(n.local?wg:xg)(n,r,Ny(this,"attr."+t,e)):null==e?(n.local?vg:mg)(n):(n.local?_g:bg)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=Xd(t);return this.tween(n,(r.local?Eg:Cg)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?Cy:gg;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=op(this,t),o=(this.style.removeProperty(t),op(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Lg(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=op(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=op(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,Ny(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=vy(this,t),u=c.on,l=null==c.value[o]?a||(a=Lg(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=op(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,Rg(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(Ny(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,Pg(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=by(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?Ay:My)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Sg:Ag)(e,t)):by(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Mg:Ng)(e,t)):by(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(Dg(e,t)):by(this.node(),e).ease},easeVarying:function(t){if("function"!=typeof t)throw new Error;return this.each(function(t,e){return function(){var n=e.apply(this,arguments);if("function"!=typeof n)throw new Error;vy(this,t).ease=n}}(this._id,t))},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=vy(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e})),0===i&&a()}))},[Symbol.iterator]:Ug[Symbol.iterator]};var qg={time:null,delay:0,duration:250,ease:function(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}};function Hg(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))throw new Error(`transition ${e} not found`);return n}function $g(t,e,n){this.k=t,this.x=e,this.y=n}Up.prototype.interrupt=function(t){return this.each((function(){!function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}(this,t)}))},Up.prototype.transition=function(t){var e,n;t instanceof Yg?(e=t._id,t=t._name):(e=zg(),(n=qg).time=oy(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&gy(o,t,e,u,s,n||Hg(o,e));return new Yg(r,this._parents,t,e)},$g.prototype={constructor:$g,scale:function(t){return 1===t?this:new $g(this.k*t,this.x,this.y)},translate:function(t,e){return 0===t&0===e?this:new $g(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}},new $g(1,0,0),$g.prototype;var Wg="comm",Vg="rule",Gg="decl",Xg=Math.abs,Zg=String.fromCharCode;function Qg(t){return t.trim()}function Kg(t,e,n){return t.replace(e,n)}function Jg(t,e){return t.indexOf(e)}function tm(t,e){return 0|t.charCodeAt(e)}function em(t,e,n){return t.slice(e,n)}function nm(t){return t.length}function rm(t){return t.length}function im(t,e){return e.push(t),t}function am(t,e){for(var n="",r=rm(t),i=0;i<r;i++)n+=e(t[i],i,t,e)||"";return n}function om(t,e,n,r){switch(t.type){case"@import":case Gg:return t.return=t.return||t.value;case Wg:return"";case"@keyframes":return t.return=t.value+"{"+am(t.children,r)+"}";case Vg:t.value=t.props.join(",")}return nm(n=am(t.children,r))?t.return=t.value+"{"+n+"}":""}Object.assign;var sm=1,cm=1,um=0,lm=0,hm=0,fm="";function dm(t,e,n,r,i,a,o){return{value:t,root:e,parent:n,type:r,props:i,children:a,line:sm,column:cm,length:o,return:""}}function pm(){return hm=lm>0?tm(fm,--lm):0,cm--,10===hm&&(cm=1,sm--),hm}function ym(){return hm=lm<um?tm(fm,lm++):0,cm++,10===hm&&(cm=1,sm++),hm}function gm(){return tm(fm,lm)}function mm(){return lm}function vm(t,e){return em(fm,t,e)}function bm(t){switch(t){case 0:case 9:case 10:case 13:case 32:return 5;case 33:case 43:case 44:case 47:case 62:case 64:case 126:case 59:case 123:case 125:return 4;case 58:return 3;case 34:case 39:case 40:case 91:return 2;case 41:case 93:return 1}return 0}function _m(t){return Qg(vm(lm-1,km(91===t?t+2:40===t?t+1:t)))}function xm(t){for(;(hm=gm())&&hm<33;)ym();return bm(t)>2||bm(hm)>3?"":" "}function wm(t,e){for(;--e&&ym()&&!(hm<48||hm>102||hm>57&&hm<65||hm>70&&hm<97););return vm(t,mm()+(e<6&&32==gm()&&32==ym()))}function km(t){for(;ym();)switch(hm){case t:return lm;case 34:case 39:34!==t&&39!==t&&km(hm);break;case 40:41===t&&km(t);break;case 92:ym()}return lm}function Tm(t,e){for(;ym()&&t+hm!==57&&(t+hm!==84||47!==gm()););return"/*"+vm(e,lm-1)+"*"+Zg(47===t?t:ym())}function Em(t){for(;!bm(gm());)ym();return vm(t,lm)}function Cm(t){return function(t){return fm="",t}(Sm("",null,null,null,[""],t=function(t){return sm=cm=1,um=nm(fm=t),lm=0,[]}(t),0,[0],t))}function Sm(t,e,n,r,i,a,o,s,c){for(var u=0,l=0,h=o,f=0,d=0,p=0,y=1,g=1,m=1,v=0,b="",_=i,x=a,w=r,k=b;g;)switch(p=v,v=ym()){case 40:if(108!=p&&58==k.charCodeAt(h-1)){-1!=Jg(k+=Kg(_m(v),"&","&\f"),"&\f")&&(m=-1);break}case 34:case 39:case 91:k+=_m(v);break;case 9:case 10:case 13:case 32:k+=xm(p);break;case 92:k+=wm(mm()-1,7);continue;case 47:switch(gm()){case 42:case 47:im(Mm(Tm(ym(),mm()),e,n),c);break;default:k+="/"}break;case 123*y:s[u++]=nm(k)*m;case 125*y:case 59:case 0:switch(v){case 0:case 125:g=0;case 59+l:d>0&&nm(k)-h&&im(d>32?Nm(k+";",r,n,h-1):Nm(Kg(k," ","")+";",r,n,h-2),c);break;case 59:k+=";";default:if(im(w=Am(k,e,n,u,l,i,s,b,_=[],x=[],h),a),123===v)if(0===l)Sm(k,e,w,w,_,a,h,s,x);else switch(f){case 100:case 109:case 115:Sm(t,w,w,r&&im(Am(t,w,w,0,0,i,s,b,i,_=[],h),x),i,x,h,s,r?_:x);break;default:Sm(k,w,w,w,[""],x,0,s,x)}}u=l=d=0,y=m=1,b=k="",h=o;break;case 58:h=1+nm(k),d=p;default:if(y<1)if(123==v)--y;else if(125==v&&0==y++&&125==pm())continue;switch(k+=Zg(v),v*y){case 38:m=l>0?1:(k+="\f",-1);break;case 44:s[u++]=(nm(k)-1)*m,m=1;break;case 64:45===gm()&&(k+=_m(ym())),f=gm(),l=h=nm(b=k+=Em(mm())),v++;break;case 45:45===p&&2==nm(k)&&(y=0)}}return a}function Am(t,e,n,r,i,a,o,s,c,u,l){for(var h=i-1,f=0===i?a:[""],d=rm(f),p=0,y=0,g=0;p<r;++p)for(var m=0,v=em(t,h+1,h=Xg(y=o[p])),b=t;m<d;++m)(b=Qg(y>0?f[m]+" "+v:Kg(v,/&\f/g,f[m])))&&(c[g++]=b);return dm(t,e,n,0===i?Vg:s,c,u,l)}function Mm(t,e,n){return dm(t,e,n,Wg,Zg(hm),em(t,2,-2),0)}function Nm(t,e,n,r){return dm(t,e,n,Gg,em(t,0,r),em(t,r+1,-1),r)}const Dm="8.13.10";var Om=n(9609),Bm=n(7856),Lm=n.n(Bm),Im=function(t){var e=t.replace(/\\u[\dA-F]{4}/gi,(function(t){return String.fromCharCode(parseInt(t.replace(/\\u/g,""),16))}));return e=(e=(e=e.replace(/\\x([0-9a-f]{2})/gi,(function(t,e){return String.fromCharCode(parseInt(e,16))}))).replace(/\\[\d\d\d]{3}/gi,(function(t){return String.fromCharCode(parseInt(t.replace(/\\/g,""),8))}))).replace(/\\[\d\d\d]{2}/gi,(function(t){return String.fromCharCode(parseInt(t.replace(/\\/g,""),8))}))},Rm=function(t){for(var e="",n=0;n>=0;){if(!((n=t.indexOf("<script"))>=0)){e+=t,n=-1;break}e+=t.substr(0,n),(n=(t=t.substr(n+1)).indexOf("<\/script>"))>=0&&(n+=9,t=t.substr(n))}var r=Im(e);return(r=(r=(r=r.replace(/script>/gi,"#")).replace(/javascript:/gi,"#")).replace(/onerror=/gi,"onerror:")).replace(/<iframe/gi,"")},Fm=function(t,e){if(!t)return t;var n=Lm().sanitize(function(t,e){var n=t,r=!0;if(!e.flowchart||!1!==e.flowchart.htmlLabels&&"false"!==e.flowchart.htmlLabels||(r=!1),r){var i=e.securityLevel;"antiscript"===i||"strict"===i?n=Rm(n):"loose"!==i&&(n=(n=(n=Ym(n)).replace(/</g,"&lt;").replace(/>/g,"&gt;")).replace(/=/g,"&equals;"),n=jm(n))}return n}(t,e));return n},Pm=/<br\s*\/?>/gi,jm=function(t){return t.replace(/#br#/g,"<br/>")},Ym=function(t){return t.replace(Pm,"#br#")},zm=function(t){return"false"!==t&&!1!==t};const Um={getRows:function(t){if(!t)return 1;var e=Ym(t);return(e=e.replace(/\\n/g,"#br#")).split("#br#")},sanitizeText:Fm,sanitizeTextOrArray:function(t,e){return"string"==typeof t?Fm(t,e):t.flat().map((function(t){return Fm(t,e)}))},hasBreaks:function(t){return Pm.test(t)},splitBreaks:function(t){return t.split(Pm)},lineBreakRegex:Pm,removeScript:Rm,getUrl:function(t){var e="";return t&&(e=(e=(e=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),e},evaluate:zm,removeEscapes:Im};var qm=n(8613),Hm=function(t,e){return e?(0,qm.adjust)(t,{s:-40,l:10}):(0,qm.adjust)(t,{s:-40,l:-10})};function $m(t){return $m="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},$m(t)}function Wm(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var Vm=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.darkMode=!1,this.primaryColor="#fff4dd",this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#ddd":"#333"),this.secondaryColor=this.secondaryColor||(0,qm.adjust)(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||(0,qm.adjust)(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||Hm(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||Hm(this.noteBkgColor,this.darkMode),this.noteBkgColor=this.noteBkgColor||"#fff5ad",this.noteTextColor=this.noteTextColor||"#333",this.secondaryTextColor=this.secondaryTextColor||(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||(0,qm.invert)(this.tertiaryColor),this.lineColor=this.lineColor||(0,qm.invert)(this.background),this.textColor=this.textColor||this.primaryTextColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?(0,qm.darken)(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||"grey",this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||(0,qm.darken)(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||(0,qm.invert)(this.lineColor),this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.excludeBkgColor=this.excludeBkgColor||"#eeeeee",this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||(0,qm.lighten)(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||this.tertiaryColor,this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.specialStateColor=this.lineColor,this.classText=this.classText||this.textColor,this.fillType0=this.fillType0||this.primaryColor,this.fillType1=this.fillType1||this.secondaryColor,this.fillType2=this.fillType2||(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=this.fillType3||(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=this.fillType4||(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=this.fillType5||(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=this.fillType6||(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=this.fillType7||(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||this.primaryColor,this.pie2=this.pie2||this.secondaryColor,this.pie3=this.pie3||this.tertiaryColor,this.pie4=this.pie4||(0,qm.adjust)(this.primaryColor,{l:-10}),this.pie5=this.pie5||(0,qm.adjust)(this.secondaryColor,{l:-10}),this.pie6=this.pie6||(0,qm.adjust)(this.tertiaryColor,{l:-10}),this.pie7=this.pie7||(0,qm.adjust)(this.primaryColor,{h:60,l:-10}),this.pie8=this.pie8||(0,qm.adjust)(this.primaryColor,{h:-60,l:-10}),this.pie9=this.pie9||(0,qm.adjust)(this.primaryColor,{h:120,l:0}),this.pie10=this.pie10||(0,qm.adjust)(this.primaryColor,{h:60,l:-20}),this.pie11=this.pie11||(0,qm.adjust)(this.primaryColor,{h:-60,l:-20}),this.pie12=this.pie12||(0,qm.adjust)(this.primaryColor,{h:120,l:-10}),this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||(this.darkMode?(0,qm.darken)(this.secondaryColor,30):this.secondaryColor),this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===$m(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&Wm(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function Gm(t){return Gm="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Gm(t)}function Xm(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var Zm=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=(0,qm.lighten)(this.primaryColor,16),this.tertiaryColor=(0,qm.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.tertiaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.mainBkg="#1f2020",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=(0,qm.lighten)((0,qm.invert)("#323D47"),10),this.lineColor="calculated",this.border1="#81B1DB",this.border2=(0,qm.rgba)(255,255,255,.25),this.arrowheadColor="calculated",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#181818",this.textColor="#ccc",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#F9FFFE",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="calculated",this.activationBkgColor="calculated",this.sequenceNumberColor="black",this.sectionBkgColor=(0,qm.darken)("#EAE8D9",30),this.altSectionBkgColor="calculated",this.sectionBkgColor2="#EAE8D9",this.taskBorderColor=(0,qm.rgba)(255,255,255,70),this.taskBkgColor="calculated",this.taskTextColor="calculated",this.taskTextLightColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor=(0,qm.rgba)(255,255,255,50),this.activeTaskBkgColor="#81B1DB",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="grey",this.critBorderColor="#E83737",this.critBkgColor="#E83737",this.taskTextDarkColor="calculated",this.todayLineColor="#DB5757",this.labelColor="calculated",this.errorBkgColor="#a44141",this.errorTextColor="#ddd"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.secondBkg=(0,qm.lighten)(this.mainBkg,16),this.lineColor=this.mainContrastColor,this.arrowheadColor=this.mainContrastColor,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.edgeLabelBackground=(0,qm.lighten)(this.labelBackground,25),this.actorBorder=this.border1,this.actorBkg=this.mainBkg,this.actorTextColor=this.mainContrastColor,this.actorLineColor=this.mainContrastColor,this.signalColor=this.mainContrastColor,this.signalTextColor=this.mainContrastColor,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.mainContrastColor,this.loopTextColor=this.mainContrastColor,this.noteBorderColor=this.secondaryBorderColor,this.noteBkgColor=this.secondBkg,this.noteTextColor=this.secondaryTextColor,this.activationBorderColor=this.border1,this.activationBkgColor=this.secondBkg,this.altSectionBkgColor=this.background,this.taskBkgColor=(0,qm.lighten)(this.mainBkg,23),this.taskTextColor=this.darkTextColor,this.taskTextLightColor=this.mainContrastColor,this.taskTextOutsideColor=this.taskTextLightColor,this.gridColor=this.mainContrastColor,this.doneTaskBkgColor=this.mainContrastColor,this.taskTextDarkColor=this.darkTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#555",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.primaryBorderColor,this.specialStateColor="#f4f4f4",this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||"#0b0000",this.pie2=this.pie2||"#4d1037",this.pie3=this.pie3||"#3f5258",this.pie4=this.pie4||"#4f2f1b",this.pie5=this.pie5||"#6e0a0a",this.pie6=this.pie6||"#3b0048",this.pie7=this.pie7||"#995a01",this.pie8=this.pie8||"#154706",this.pie9=this.pie9||"#161722",this.pie10=this.pie10||"#00296f",this.pie11=this.pie11||"#01629c",this.pie12=this.pie12||"#010029",this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.classText=this.primaryTextColor,this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||(this.darkMode?(0,qm.darken)(this.secondaryColor,30):this.secondaryColor),this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===Gm(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&Xm(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function Qm(t){return Qm="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Qm(t)}function Km(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var Jm=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#ECECFF",this.secondaryColor=(0,qm.adjust)(this.primaryColor,{h:120}),this.secondaryColor="#ffffde",this.tertiaryColor=(0,qm.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.tertiaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.background="white",this.mainBkg="#ECECFF",this.secondBkg="#ffffde",this.lineColor="#333333",this.border1="#9370DB",this.border2="#aaaa33",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#e8e8e8",this.textColor="#333",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="calculated",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="calculated",this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor="calculated",this.taskTextOutsideColor=this.taskTextDarkColor,this.taskTextClickableColor="calculated",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBorderColor="calculated",this.critBkgColor="calculated",this.todayLineColor="calculated",this.sectionBkgColor=(0,qm.rgba)(102,102,255,.49),this.altSectionBkgColor="white",this.sectionBkgColor2="#fff400",this.taskBorderColor="#534fbc",this.taskBkgColor="#8a90dd",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="#534fbc",this.activeTaskBkgColor="#bfc7ff",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.updateColors()}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.textColor,this.edgeLabelBackground=this.labelBackground,this.actorBorder=(0,qm.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.signalColor=this.textColor,this.signalTextColor=this.textColor,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.nodeBorder,this.specialStateColor=this.lineColor,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||this.primaryColor,this.pie2=this.pie2||this.secondaryColor,this.pie3=this.pie3||(0,qm.adjust)(this.tertiaryColor,{l:-40}),this.pie4=this.pie4||(0,qm.adjust)(this.primaryColor,{l:-10}),this.pie5=this.pie5||(0,qm.adjust)(this.secondaryColor,{l:-30}),this.pie6=this.pie6||(0,qm.adjust)(this.tertiaryColor,{l:-20}),this.pie7=this.pie7||(0,qm.adjust)(this.primaryColor,{h:60,l:-20}),this.pie8=this.pie8||(0,qm.adjust)(this.primaryColor,{h:-60,l:-40}),this.pie9=this.pie9||(0,qm.adjust)(this.primaryColor,{h:120,l:-40}),this.pie10=this.pie10||(0,qm.adjust)(this.primaryColor,{h:60,l:-40}),this.pie11=this.pie11||(0,qm.adjust)(this.primaryColor,{h:-90,l:-40}),this.pie12=this.pie12||(0,qm.adjust)(this.primaryColor,{h:120,l:-30}),this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||this.labelBackground,this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===Qm(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&Km(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function tv(t){return tv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},tv(t)}function ev(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var nv=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#cde498",this.secondaryColor="#cdffb2",this.background="white",this.mainBkg="#cde498",this.secondBkg="#cdffb2",this.lineColor="green",this.border1="#13540c",this.border2="#6eaa49",this.arrowheadColor="green",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.tertiaryColor=(0,qm.lighten)("#cde498",10),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.primaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#333",this.edgeLabelBackground="#e8e8e8",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="#333",this.signalTextColor="#333",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="#326932",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="#6eaa49",this.altSectionBkgColor="white",this.sectionBkgColor2="#6eaa49",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="#487e3a",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.actorBorder=(0,qm.darken)(this.mainBkg,20),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskBorderColor=this.border1,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.transitionColor=this.transitionColor||this.lineColor,this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f0f0f0",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.compositeBorder=this.compositeBorder||this.nodeBorder,this.innerEndBackground=this.primaryBorderColor,this.specialStateColor=this.lineColor,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.transitionColor=this.transitionColor||this.lineColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||this.primaryColor,this.pie2=this.pie2||this.secondaryColor,this.pie3=this.pie3||this.tertiaryColor,this.pie4=this.pie4||(0,qm.adjust)(this.primaryColor,{l:-30}),this.pie5=this.pie5||(0,qm.adjust)(this.secondaryColor,{l:-30}),this.pie6=this.pie6||(0,qm.adjust)(this.tertiaryColor,{h:40,l:-40}),this.pie7=this.pie7||(0,qm.adjust)(this.primaryColor,{h:60,l:-10}),this.pie8=this.pie8||(0,qm.adjust)(this.primaryColor,{h:-60,l:-10}),this.pie9=this.pie9||(0,qm.adjust)(this.primaryColor,{h:120,l:0}),this.pie10=this.pie10||(0,qm.adjust)(this.primaryColor,{h:60,l:-50}),this.pie11=this.pie11||(0,qm.adjust)(this.primaryColor,{h:-60,l:-50}),this.pie12=this.pie12||(0,qm.adjust)(this.primaryColor,{h:120,l:-50}),this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||this.edgeLabelBackground,this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===tv(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&ev(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();function rv(t){return rv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},rv(t)}function iv(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var av=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.primaryColor="#eee",this.contrast="#707070",this.secondaryColor=(0,qm.lighten)(this.contrast,55),this.background="#ffffff",this.tertiaryColor=(0,qm.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=Hm(this.primaryColor,this.darkMode),this.secondaryBorderColor=Hm(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=Hm(this.tertiaryColor,this.darkMode),this.primaryTextColor=(0,qm.invert)(this.primaryColor),this.secondaryTextColor=(0,qm.invert)(this.secondaryColor),this.tertiaryTextColor=(0,qm.invert)(this.tertiaryColor),this.lineColor=(0,qm.invert)(this.background),this.textColor=(0,qm.invert)(this.background),this.mainBkg="#eee",this.secondBkg="calculated",this.lineColor="#666",this.border1="#999",this.border2="calculated",this.note="#ffa",this.text="#333",this.critical="#d42",this.done="#bbb",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="white",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="calculated",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="white",this.sectionBkgColor2="calculated",this.excludeBkgColor="#eeeeee",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBkgColor="calculated",this.critBorderColor="calculated",this.todayLineColor="calculated",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n;return e=t,n=[{key:"updateColors",value:function(){this.secondBkg=(0,qm.lighten)(this.contrast,55),this.border2=this.contrast,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.text,this.actorBorder=(0,qm.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.actorTextColor=this.text,this.actorLineColor=this.lineColor,this.signalColor=this.text,this.signalTextColor=this.text,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.text,this.loopTextColor=this.text,this.noteBorderColor="#999",this.noteBkgColor="#666",this.noteTextColor="#fff",this.sectionBkgColor=(0,qm.lighten)(this.contrast,30),this.sectionBkgColor2=(0,qm.lighten)(this.contrast,30),this.taskBorderColor=(0,qm.darken)(this.contrast,10),this.taskBkgColor=this.contrast,this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor=this.text,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.gridColor=(0,qm.lighten)(this.border1,30),this.doneTaskBkgColor=this.done,this.doneTaskBorderColor=this.lineColor,this.critBkgColor=this.critical,this.critBorderColor=(0,qm.darken)(this.critBkgColor,10),this.todayLineColor=this.critBkgColor,this.transitionColor=this.transitionColor||"#000",this.transitionLabelColor=this.transitionLabelColor||this.textColor,this.stateLabelColor=this.stateLabelColor||this.stateBkg||this.primaryTextColor,this.stateBkg=this.stateBkg||this.mainBkg,this.labelBackgroundColor=this.labelBackgroundColor||this.stateBkg,this.compositeBackground=this.compositeBackground||this.background||this.tertiaryColor,this.altBackground=this.altBackground||"#f4f4f4",this.compositeTitleBackground=this.compositeTitleBackground||this.mainBkg,this.stateBorder=this.stateBorder||"#000",this.innerEndBackground=this.primaryBorderColor,this.specialStateColor="#222",this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=(0,qm.adjust)(this.primaryColor,{h:64}),this.fillType3=(0,qm.adjust)(this.secondaryColor,{h:64}),this.fillType4=(0,qm.adjust)(this.primaryColor,{h:-64}),this.fillType5=(0,qm.adjust)(this.secondaryColor,{h:-64}),this.fillType6=(0,qm.adjust)(this.primaryColor,{h:128}),this.fillType7=(0,qm.adjust)(this.secondaryColor,{h:128}),this.pie1=this.pie1||"#F4F4F4",this.pie2=this.pie2||"#555",this.pie3=this.pie3||"#BBB",this.pie4=this.pie4||"#777",this.pie5=this.pie5||"#999",this.pie6=this.pie6||"#DDD",this.pie7=this.pie7||"#FFF",this.pie8=this.pie8||"#DDD",this.pie9=this.pie9||"#BBB",this.pie10=this.pie10||"#999",this.pie11=this.pie11||"#777",this.pie12=this.pie12||"#555",this.pieTitleTextSize=this.pieTitleTextSize||"25px",this.pieTitleTextColor=this.pieTitleTextColor||this.taskTextDarkColor,this.pieSectionTextSize=this.pieSectionTextSize||"17px",this.pieSectionTextColor=this.pieSectionTextColor||this.textColor,this.pieLegendTextSize=this.pieLegendTextSize||"17px",this.pieLegendTextColor=this.pieLegendTextColor||this.taskTextDarkColor,this.pieStrokeColor=this.pieStrokeColor||"black",this.pieStrokeWidth=this.pieStrokeWidth||"2px",this.pieOpacity=this.pieOpacity||"0.7",this.requirementBackground=this.requirementBackground||this.primaryColor,this.requirementBorderColor=this.requirementBorderColor||this.primaryBorderColor,this.requirementBorderSize=this.requirementBorderSize||this.primaryBorderColor,this.requirementTextColor=this.requirementTextColor||this.primaryTextColor,this.relationColor=this.relationColor||this.lineColor,this.relationLabelBackground=this.relationLabelBackground||this.edgeLabelBackground,this.relationLabelColor=this.relationLabelColor||this.actorTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===rv(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}],n&&iv(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}();const ov={base:{getThemeVariables:function(t){var e=new Vm;return e.calculate(t),e}},dark:{getThemeVariables:function(t){var e=new Zm;return e.calculate(t),e}},default:{getThemeVariables:function(t){var e=new Jm;return e.calculate(t),e}},forest:{getThemeVariables:function(t){var e=new nv;return e.calculate(t),e}},neutral:{getThemeVariables:function(t){var e=new av;return e.calculate(t),e}}};function sv(t){return function(t){if(Array.isArray(t))return cv(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return cv(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?cv(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function cv(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}function uv(t){return uv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},uv(t)}var lv={theme:"default",themeVariables:ov.default.getThemeVariables(),themeCSS:void 0,maxTextSize:5e4,darkMode:!1,fontFamily:'"trebuchet ms", verdana, arial, sans-serif;',logLevel:5,securityLevel:"strict",startOnLoad:!0,arrowMarkerAbsolute:!1,secure:["secure","securityLevel","startOnLoad","maxTextSize"],deterministicIds:!1,deterministicIDSeed:void 0,flowchart:{diagramPadding:8,htmlLabels:!0,nodeSpacing:50,rankSpacing:50,curve:"basis",padding:15,useMaxWidth:!0,defaultRenderer:"dagre-d3"},sequence:{activationWidth:10,diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",mirrorActors:!0,forceMenus:!1,bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1,showSequenceNumbers:!1,actorFontSize:14,actorFontFamily:'"Open-Sans", "sans-serif"',actorFontWeight:400,noteFontSize:14,noteFontFamily:'"trebuchet ms", verdana, arial, sans-serif',noteFontWeight:400,noteAlign:"center",messageFontSize:16,messageFontFamily:'"trebuchet ms", verdana, arial, sans-serif',messageFontWeight:400,wrap:!1,wrapPadding:10,labelBoxWidth:50,labelBoxHeight:20,messageFont:function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},noteFont:function(){return{fontFamily:this.noteFontFamily,fontSize:this.noteFontSize,fontWeight:this.noteFontWeight}},actorFont:function(){return{fontFamily:this.actorFontFamily,fontSize:this.actorFontSize,fontWeight:this.actorFontWeight}}},gantt:{titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,rightPadding:75,leftPadding:75,gridLineStartPadding:35,fontSize:11,sectionFontSize:11,numberSectionStyles:4,axisFormat:"%Y-%m-%d",useMaxWidth:!0,topAxis:!1,useWidth:void 0},journey:{diagramMarginX:50,diagramMarginY:10,leftMargin:150,width:150,height:50,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1,taskFontSize:14,taskFontFamily:'"Open-Sans", "sans-serif"',taskMargin:50,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"]},class:{arrowMarkerAbsolute:!1,useMaxWidth:!0,defaultRenderer:"dagre-wrapper"},git:{arrowMarkerAbsolute:!1,useWidth:void 0,useMaxWidth:!0},state:{dividerMargin:10,sizeUnit:5,padding:8,textHeight:10,titleShift:-15,noteMargin:10,forkWidth:70,forkHeight:7,miniPadding:2,fontSizeFactor:5.02,fontSize:24,labelHeight:16,edgeLengthFactor:"20",compositTitleSize:35,radius:5,useMaxWidth:!0,defaultRenderer:"dagre-wrapper"},er:{diagramPadding:20,layoutDirection:"TB",minEntityWidth:100,minEntityHeight:75,entityPadding:15,stroke:"gray",fill:"honeydew",fontSize:12,useMaxWidth:!0},pie:{useWidth:void 0,useMaxWidth:!0},requirement:{useWidth:void 0,useMaxWidth:!0,rect_fill:"#f9f9f9",text_color:"#333",rect_border_size:"0.5px",rect_border_color:"#bbb",rect_min_width:200,rect_min_height:200,fontSize:14,rect_padding:10,line_height:20}};lv.class.arrowMarkerAbsolute=lv.arrowMarkerAbsolute,lv.git.arrowMarkerAbsolute=lv.arrowMarkerAbsolute;var hv=function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return Object.keys(e).reduce((function(r,i){return Array.isArray(e[i])?r:"object"===uv(e[i])&&null!==e[i]?[].concat(sv(r),[n+i],sv(t(e[i],""))):[].concat(sv(r),[n+i])}),[])}(lv,"");const fv=lv;var dv=void 0;function pv(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function yv(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!n){if(Array.isArray(t)||(n=vv(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var r=0,i=function(){};return{s:i,n:function(){return r>=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,s=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return o=t.done,t},e:function(t){s=!0,a=t},f:function(){try{o||null==n.return||n.return()}finally{if(s)throw a}}}}function gv(t){return gv="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},gv(t)}function mv(t){return function(t){if(Array.isArray(t))return bv(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||vv(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function vv(t,e){if(t){if("string"==typeof t)return bv(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?bv(t,e):void 0}}function bv(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var _v,xv={curveBasis:Vu,curveBasisClosed:function(t){return new Gu(t)},curveBasisOpen:function(t){return new Xu(t)},curveLinear:Pu,curveLinearClosed:function(t){return new Zu(t)},curveMonotoneX:function(t){return new el(t)},curveMonotoneY:function(t){return new nl(t)},curveNatural:function(t){return new il(t)},curveStep:function(t){return new ol(t,.5)},curveStepAfter:function(t){return new ol(t,1)},curveStepBefore:function(t){return new ol(t,0)}},wv=/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,kv=/\s*(?:(?:(\w+)(?=:):|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,Tv=/\s*%%.*\n/gm,Ev=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;try{var n=new RegExp("[%]{2}(?![{]".concat(kv.source,")(?=[}][%]{2}).*\n"),"ig");t=t.trim().replace(n,"").replace(/'/gm,'"'),o.debug("Detecting diagram directive".concat(null!==e?" type:"+e:""," based on the text:").concat(t));for(var r,i=[];null!==(r=wv.exec(t));)if(r.index===wv.lastIndex&&wv.lastIndex++,r&&!e||e&&r[1]&&r[1].match(e)||e&&r[2]&&r[2].match(e)){var a=r[1]?r[1]:r[2],s=r[3]?r[3].trim():r[4]?JSON.parse(r[4].trim()):null;i.push({type:a,args:s})}return 0===i.length&&i.push({type:t,args:null}),1===i.length?i[0]:i}catch(n){return o.error("ERROR: ".concat(n.message," - Unable to parse directive\n      ").concat(null!==e?" type:"+e:""," based on the text:").concat(t)),{type:null,args:null}}},Cv=function(t,e){return(t=t.replace(wv,"").replace(Tv,"\n")).match(/^\s*sequenceDiagram/)?"sequence":t.match(/^\s*gantt/)?"gantt":t.match(/^\s*classDiagram-v2/)?"classDiagram":t.match(/^\s*classDiagram/)?e&&e.class&&"dagre-wrapper"===e.class.defaultRenderer?"classDiagram":"class":t.match(/^\s*stateDiagram-v2/)?"stateDiagram":t.match(/^\s*stateDiagram/)?e&&e.class&&"dagre-wrapper"===e.state.defaultRenderer?"stateDiagram":"state":t.match(/^\s*gitGraph/)?"git":t.match(/^\s*flowchart/)?"flowchart-v2":t.match(/^\s*info/)?"info":t.match(/^\s*pie/)?"pie":t.match(/^\s*erDiagram/)?"er":t.match(/^\s*journey/)?"journey":t.match(/^\s*requirement/)||t.match(/^\s*requirementDiagram/)?"requirement":e&&e.flowchart&&"dagre-wrapper"===e.flowchart.defaultRenderer?"flowchart-v2":"flowchart"},Sv=function(t,e){var n={};return function(){for(var r=arguments.length,i=new Array(r),a=0;a<r;a++)i[a]=arguments[a];var o=e?e.apply(dv,i):i[0];if(o in n)return n[o];var s=t.apply(void 0,i);return n[o]=s,s}},Av=function(t,e){if(!t)return e;var n="curve".concat(t.charAt(0).toUpperCase()+t.slice(1));return xv[n]||e},Mv=function(t,e){return t&&e?Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2)):0},Nv=function(t){for(var e="",n="",r=0;r<t.length;r++)void 0!==t[r]&&(t[r].startsWith("color:")||t[r].startsWith("text-align:")?n=n+t[r]+";":e=e+t[r]+";");return{style:e,labelStyle:n}},Dv=0,Ov=function(){return Dv++,"id-"+Math.random().toString(36).substr(2,12)+"-"+Dv},Bv=function(t){return function(t){for(var e="",n="0123456789abcdef",r=n.length,i=0;i<t;i++)e+=n.charAt(Math.floor(Math.random()*r));return e}(t.length)},Lv=function t(e,n,r){var i=Object.assign({depth:2,clobber:!1},r),a=i.depth,o=i.clobber;return Array.isArray(n)&&!Array.isArray(e)?(n.forEach((function(n){return t(e,n,r)})),e):Array.isArray(n)&&Array.isArray(e)?(n.forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),e):void 0===e||a<=0?null!=e&&"object"===gv(e)&&"object"===gv(n)?Object.assign(e,n):n:(void 0!==n&&"object"===gv(e)&&"object"===gv(n)&&Object.keys(n).forEach((function(r){"object"!==gv(n[r])||void 0!==e[r]&&"object"!==gv(e[r])?(o||"object"!==gv(e[r])&&"object"!==gv(n[r]))&&(e[r]=n[r]):(void 0===e[r]&&(e[r]=Array.isArray(n[r])?[]:{}),e[r]=t(e[r],n[r],{depth:a-1,clobber:o}))})),e)},Iv=function(t,e){var n=e.text.replace(Um.lineBreakRegex," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.style("text-anchor",e.anchor),r.style("font-family",e.fontFamily),r.style("font-size",e.fontSize),r.style("font-weight",e.fontWeight),r.attr("fill",e.fill),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.attr("fill",e.fill),i.text(n),r},Rv=Sv((function(t,e,n){if(!t)return t;if(n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",joinWith:"<br/>"},n),Um.lineBreakRegex.test(t))return t;var r=t.split(" "),i=[],a="";return r.forEach((function(t,o){var s=Pv("".concat(t," "),n),c=Pv(a,n);if(s>e){var u=Fv(t,e,"-",n),l=u.hyphenatedStrings,h=u.remainingWord;i.push.apply(i,[a].concat(mv(l))),a=h}else c+s>=e?(i.push(a),a=t):a=[a,t].filter(Boolean).join(" ");o+1===r.length&&i.push(a)})),i.filter((function(t){return""!==t})).join(n.joinWith)}),(function(t,e,n){return"".concat(t,"-").concat(e,"-").concat(n.fontSize,"-").concat(n.fontWeight,"-").concat(n.fontFamily,"-").concat(n.joinWith)})),Fv=Sv((function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;r=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},r);var i=t.split(""),a=[],o="";return i.forEach((function(t,s){var c="".concat(o).concat(t);if(Pv(c,r)>=e){var u=s+1,l=i.length===u,h="".concat(c).concat(n);a.push(l?c:h),o=""}else o=c})),{hyphenatedStrings:a,remainingWord:o}}),(function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;return"".concat(t,"-").concat(e,"-").concat(n,"-").concat(r.fontSize,"-").concat(r.fontWeight,"-").concat(r.fontFamily)})),Pv=function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),jv(t,e).width},jv=Sv((function(t,e){var n=e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),r=n.fontSize,i=n.fontFamily,a=n.fontWeight;if(!t)return{width:0,height:0};var o=["sans-serif",i],s=t.split(Um.lineBreakRegex),c=[],u=au("body");if(!u.remove)return{width:0,height:0,lineHeight:0};for(var l=u.append("svg"),h=0,f=o;h<f.length;h++){var d,p=f[h],y=0,g={width:0,height:0,lineHeight:0},m=yv(s);try{for(m.s();!(d=m.n()).done;){var v=d.value,b={x:0,y:0,fill:void 0,anchor:"start",style:"#666",width:100,height:100,textMargin:0,rx:0,ry:0,valign:void 0};b.text=v;var _=Iv(l,b).style("font-size",r).style("font-weight",a).style("font-family",p),x=(_._groups||_)[0][0].getBBox();g.width=Math.round(Math.max(g.width,x.width)),y=Math.round(x.height),g.height+=y,g.lineHeight=Math.round(Math.max(g.lineHeight,y))}}catch(t){m.e(t)}finally{m.f()}c.push(g)}return l.remove(),c[isNaN(c[1].height)||isNaN(c[1].width)||isNaN(c[1].lineHeight)||c[0].height>c[1].height&&c[0].width>c[1].width&&c[0].lineHeight>c[1].lineHeight?0:1]}),(function(t,e){return"".concat(t,"-").concat(e.fontSize,"-").concat(e.fontWeight,"-").concat(e.fontFamily)})),Yv=function(t,e,n){var r=new Map;return r.set("height",t),n?(r.set("width","100%"),r.set("style","max-width: ".concat(e,"px;"))):r.set("width",e),r},zv=function(t,e,n,r){!function(t,e){var n,r=yv(e);try{for(r.s();!(n=r.n()).done;){var i=n.value;t.attr(i[0],i[1])}}catch(t){r.e(t)}finally{r.f()}}(t,Yv(e,n,r))},Uv=function t(e){o.debug("directiveSanitizer called with",e),"object"===gv(e)&&(e.length?e.forEach((function(e){return t(e)})):Object.keys(e).forEach((function(n){o.debug("Checking key",n),0===n.indexOf("__")&&(o.debug("sanitize deleting __ option",n),delete e[n]),n.indexOf("proto")>=0&&(o.debug("sanitize deleting proto option",n),delete e[n]),n.indexOf("constr")>=0&&(o.debug("sanitize deleting constr option",n),delete e[n]),n.indexOf("themeCSS")>=0&&(o.debug("sanitizing themeCss option"),e[n]=qv(e[n])),hv.indexOf(n)<0?(o.debug("sanitize deleting option",n),delete e[n]):"object"===gv(e[n])&&(o.debug("sanitize deleting object",n),t(e[n]))})))},qv=function(t){return(t.match(/\{/g)||[]).length!==(t.match(/\}/g)||[]).length?"{ /* ERROR: Unbalanced CSS */ }":t};const Hv={assignWithDepth:Lv,wrapLabel:Rv,calculateTextHeight:function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:15},e),jv(t,e).height},calculateTextWidth:Pv,calculateTextDimensions:jv,calculateSvgSizeAttrs:Yv,configureSvgSize:zv,detectInit:function(t,e){var n=Ev(t,/(?:init\b)|(?:initialize\b)/),r={};if(Array.isArray(n)){var i=n.map((function(t){return t.args}));Uv(i),r=Lv(r,mv(i))}else r=n.args;if(r){var a=Cv(t,e);["config"].forEach((function(t){void 0!==r[t]&&("flowchart-v2"===a&&(a="flowchart"),r[a]=r[t],delete r[t])}))}return r},detectDirective:Ev,detectType:Cv,isSubstringInArray:function(t,e){for(var n=0;n<e.length;n++)if(e[n].match(t))return n;return-1},interpolateToCurve:Av,calcLabelPosition:function(t){return function(t){var e,n=0;t.forEach((function(t){n+=Mv(t,e),e=t}));var r=n/2,i=void 0;return e=void 0,t.forEach((function(t){if(e&&!i){var n=Mv(t,e);if(n<r)r-=n;else{var a=r/n;a<=0&&(i=e),a>=1&&(i={x:t.x,y:t.y}),a>0&&a<1&&(i={x:(1-a)*e.x+a*t.x,y:(1-a)*e.y+a*t.y})}}e=t})),i}(t)},calcCardinalityPosition:function(t,e,n){var r;o.info("our points",e),e[0]!==n&&(e=e.reverse()),e.forEach((function(t){Mv(t,r),r=t}));var i,a=25;r=void 0,e.forEach((function(t){if(r&&!i){var e=Mv(t,r);if(e<a)a-=e;else{var n=a/e;n<=0&&(i=r),n>=1&&(i={x:t.x,y:t.y}),n>0&&n<1&&(i={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var s=t?10:5,c=Math.atan2(e[0].y-i.y,e[0].x-i.x),u={x:0,y:0};return u.x=Math.sin(c)*s+(e[0].x+i.x)/2,u.y=-Math.cos(c)*s+(e[0].y+i.y)/2,u},calcTerminalLabelPosition:function(t,e,n){var r,i=JSON.parse(JSON.stringify(n));o.info("our points",i),"start_left"!==e&&"start_right"!==e&&(i=i.reverse()),i.forEach((function(t){Mv(t,r),r=t}));var a,s=25+t;r=void 0,i.forEach((function(t){if(r&&!a){var e=Mv(t,r);if(e<s)s-=e;else{var n=s/e;n<=0&&(a=r),n>=1&&(a={x:t.x,y:t.y}),n>0&&n<1&&(a={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var c=10+.5*t,u=Math.atan2(i[0].y-a.y,i[0].x-a.x),l={x:0,y:0};return l.x=Math.sin(u)*c+(i[0].x+a.x)/2,l.y=-Math.cos(u)*c+(i[0].y+a.y)/2,"start_left"===e&&(l.x=Math.sin(u+Math.PI)*c+(i[0].x+a.x)/2,l.y=-Math.cos(u+Math.PI)*c+(i[0].y+a.y)/2),"end_right"===e&&(l.x=Math.sin(u-Math.PI)*c+(i[0].x+a.x)/2-5,l.y=-Math.cos(u-Math.PI)*c+(i[0].y+a.y)/2-5),"end_left"===e&&(l.x=Math.sin(u)*c+(i[0].x+a.x)/2-5,l.y=-Math.cos(u)*c+(i[0].y+a.y)/2-5),l},formatUrl:function(t,e){var n=t.trim();if(n)return"loose"!==e.securityLevel?(0,Om.sanitizeUrl)(n):n},getStylesFromArray:Nv,generateId:Ov,random:Bv,memoize:Sv,runFunc:function(t){for(var e,n=t.split("."),r=n.length-1,i=n[r],a=window,o=0;o<r;o++)if(!(a=a[n[o]]))return;for(var s=arguments.length,c=new Array(s>1?s-1:0),u=1;u<s;u++)c[u-1]=arguments[u];(e=a)[i].apply(e,c)},entityDecode:function(t){return _v=_v||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),_v.innerHTML=t,unescape(_v.textContent)},initIdGeneratior:function(){function t(e,n){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.deterministic=e,this.seed=n,this.count=n?n.length:0}var e,n;return e=t,(n=[{key:"next",value:function(){return this.deterministic?this.count++:Date.now()}}])&&pv(e.prototype,n),Object.defineProperty(e,"prototype",{writable:!1}),t}(),directiveSanitizer:Uv,sanitizeCss:qv};function $v(t){return $v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},$v(t)}var Wv,Vv=Object.freeze(fv),Gv=Lv({},Vv),Xv=[],Zv=Lv({},Vv),Qv=function(t,e){for(var n=Lv({},t),r={},i=0;i<e.length;i++){var a=e[i];tb(a),r=Lv(r,a)}if(n=Lv(n,r),r.theme){var o=Lv({},Wv),s=Lv(o.themeVariables||{},r.themeVariables);n.themeVariables=ov[n.theme].getThemeVariables(s)}return Zv=n,n},Kv=function(){return Lv({},Gv)},Jv=function(){return Lv({},Zv)},tb=function t(e){Object.keys(Gv.secure).forEach((function(t){void 0!==e[Gv.secure[t]]&&(o.debug("Denied attempt to modify a secure key ".concat(Gv.secure[t]),e[Gv.secure[t]]),delete e[Gv.secure[t]])})),Object.keys(e).forEach((function(t){0===t.indexOf("__")&&delete e[t]})),Object.keys(e).forEach((function(n){"string"==typeof e[n]&&(e[n].indexOf("<")>-1||e[n].indexOf(">")>-1||e[n].indexOf("url(data:")>-1)&&delete e[n],"object"===$v(e[n])&&t(e[n])}))},eb=function(t){t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),Xv.push(t),Qv(Gv,Xv)},nb=function(){Qv(Gv,Xv=[])};function rb(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var ib="classid-",ab=[],ob={},sb=0,cb=[],ub=function(t){return Um.sanitizeText(t,Jv())},lb=function(t){var e="",n=t;if(t.indexOf("~")>0){var r=t.split("~");n=r[0],e=Um.sanitizeText(r[1],Jv())}return{className:n,type:e}},hb=function(t){var e=lb(t);void 0===ob[e.className]&&(ob[e.className]={id:e.className,type:e.type,cssClasses:[],methods:[],members:[],annotations:[],domId:ib+e.className+"-"+sb},sb++)},fb=function(t){for(var e=Object.keys(ob),n=0;n<e.length;n++)if(ob[e[n]].id===t)return ob[e[n]].domId},db=function(t,e){var n=lb(t).className,r=ob[n];if("string"==typeof e){var i=e.trim();i.startsWith("<<")&&i.endsWith(">>")?r.annotations.push(ub(i.substring(2,i.length-2))):i.indexOf(")")>0?r.methods.push(ub(i)):i&&r.members.push(ub(i))}},pb=function(t,e){t.split(",").forEach((function(t){var n=t;t[0].match(/\d/)&&(n=ib+n),void 0!==ob[n]&&ob[n].cssClasses.push(e)}))},yb=function(t,e,n){var r=Jv(),i=t,a=fb(i);if("loose"===r.securityLevel&&void 0!==e&&void 0!==ob[i]){var o=[];if("string"==typeof n){o=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var s=0;s<o.length;s++){var c=o[s].trim();'"'===c.charAt(0)&&'"'===c.charAt(c.length-1)&&(c=c.substr(1,c.length-2)),o[s]=c}}0===o.length&&o.push(a),cb.push((function(){var t=document.querySelector('[id="'.concat(a,'"]'));null!==t&&t.addEventListener("click",(function(){var t;Hv.runFunc.apply(Hv,[e].concat(function(t){if(Array.isArray(t))return rb(t)}(t=o)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return rb(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?rb(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()))}),!1)}))}},gb={AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},mb=function(t){var e=au(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=au("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),au(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=au(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),au(this).classed("hover",!1)}))};cb.push(mb);var vb="TB";const bb={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().class},addClass:hb,bindFunctions:function(t){cb.forEach((function(e){e(t)}))},clear:function(){ab=[],ob={},(cb=[]).push(mb)},getClass:function(t){return ob[t]},getClasses:function(){return ob},addAnnotation:function(t,e){var n=lb(t).className;ob[n].annotations.push(e)},getRelations:function(){return ab},addRelation:function(t){o.debug("Adding relation: "+JSON.stringify(t)),hb(t.id1),hb(t.id2),t.id1=lb(t.id1).className,t.id2=lb(t.id2).className,t.relationTitle1=Um.sanitizeText(t.relationTitle1.trim(),Jv()),t.relationTitle2=Um.sanitizeText(t.relationTitle2.trim(),Jv()),ab.push(t)},getDirection:function(){return vb},setDirection:function(t){vb=t},addMember:db,addMembers:function(t,e){Array.isArray(e)&&(e.reverse(),e.forEach((function(e){return db(t,e)})))},cleanupLabel:function(t){return":"===t.substring(0,1)?Um.sanitizeText(t.substr(1).trim(),Jv()):ub(t.trim())},lineType:{LINE:0,DOTTED_LINE:1},relationType:gb,setClickEvent:function(t,e,n){t.split(",").forEach((function(t){yb(t,e,n),ob[t].haveCallback=!0})),pb(t,"clickable")},setCssClass:pb,setLink:function(t,e,n){var r=Jv();t.split(",").forEach((function(t){var i=t;t[0].match(/\d/)&&(i=ib+i),void 0!==ob[i]&&(ob[i].link=Hv.formatUrl(e,r),ob[i].linkTarget="string"==typeof n?n:"_blank")})),pb(t,"clickable")},setTooltip:function(t,e){var n=Jv();t.split(",").forEach((function(t){void 0!==e&&(ob[t].tooltip=Um.sanitizeText(e,n))}))},lookUpDomId:fb};var _b=n(681),xb=n.n(_b),wb=n(8282),kb=n.n(wb),Tb=n(1362),Eb=n.n(Tb),Cb=0,Sb=function(t){var e=t.match(/^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+) *(\*|\$)?$/),n=t.match(/^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/);return e&&!n?Ab(e):n?Mb(n):Nb(t)},Ab=function(t){var e="",n="";try{var r=t[1]?t[1].trim():"",i=t[2]?t[2].trim():"",a=t[3]?Ob(t[3].trim()):"",o=t[4]?t[4].trim():"",s=t[5]?t[5].trim():"";n=r+i+a+" "+o,e=Bb(s)}catch(e){n=t}return{displayText:n,cssStyle:e}},Mb=function(t){var e="",n="";try{var r=t[1]?t[1].trim():"",i=t[2]?t[2].trim():"",a=t[3]?Ob(t[3].trim()):"",o=t[4]?t[4].trim():"";n=r+i+"("+a+")"+(t[5]?" : "+Ob(t[5]).trim():""),e=Bb(o)}catch(e){n=t}return{displayText:n,cssStyle:e}},Nb=function(t){var e="",n="",r="",i=t.indexOf("("),a=t.indexOf(")");if(i>1&&a>i&&a<=t.length){var o="",s="",c=t.substring(0,1);c.match(/\w/)?s=t.substring(0,i).trim():(c.match(/\+|-|~|#/)&&(o=c),s=t.substring(1,i).trim());var u=t.substring(i+1,a),l=t.substring(a+1,1);n=Bb(l),e=o+s+"("+Ob(u.trim())+")",a<"".length&&""!==(r=t.substring(a+2).trim())&&(r=" : "+Ob(r))}else e=Ob(t);return{displayText:e,cssStyle:n}},Db=function(t,e,n,r){var i=Sb(e),a=t.append("tspan").attr("x",r.padding).text(i.displayText);""!==i.cssStyle&&a.attr("style",i.cssStyle),n||a.attr("dy",r.textHeight)},Ob=function t(e){var n=e;return-1!=e.indexOf("~")?t(n=(n=n.replace("~","<")).replace("~",">")):n},Bb=function(t){switch(t){case"*":return"font-style:italic;";case"$":return"text-decoration:underline;";default:return""}};const Lb=function(t,e,n){o.info("Rendering class "+e);var r,i=e.id,a={id:i,label:e.id,width:0,height:0},s=t.append("g").attr("id",fb(i)).attr("class","classGroup");r=e.link?s.append("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget).append("text").attr("y",n.textHeight+n.padding).attr("x",0):s.append("text").attr("y",n.textHeight+n.padding).attr("x",0);var c=!0;e.annotations.forEach((function(t){var e=r.append("tspan").text("«"+t+"»");c||e.attr("dy",n.textHeight),c=!1}));var u=e.id;void 0!==e.type&&""!==e.type&&(u+="<"+e.type+">");var l=r.append("tspan").text(u).attr("class","title");c||l.attr("dy",n.textHeight);var h=r.node().getBBox().height,f=s.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin/2).attr("y2",n.padding+h+n.dividerMargin/2),d=s.append("text").attr("x",n.padding).attr("y",h+n.dividerMargin+n.textHeight).attr("fill","white").attr("class","classText");c=!0,e.members.forEach((function(t){Db(d,t,c,n),c=!1}));var p=d.node().getBBox(),y=s.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin+p.height).attr("y2",n.padding+h+n.dividerMargin+p.height),g=s.append("text").attr("x",n.padding).attr("y",h+2*n.dividerMargin+p.height+n.textHeight).attr("fill","white").attr("class","classText");c=!0,e.methods.forEach((function(t){Db(g,t,c,n),c=!1}));var m=s.node().getBBox(),v=" ";e.cssClasses.length>0&&(v+=e.cssClasses.join(" "));var b=s.insert("rect",":first-child").attr("x",0).attr("y",0).attr("width",m.width+2*n.padding).attr("height",m.height+n.padding+.5*n.dividerMargin).attr("class",v).node().getBBox().width;return r.node().childNodes.forEach((function(t){t.setAttribute("x",(b-t.getBBox().width)/2)})),e.tooltip&&r.insert("title").text(e.tooltip),f.attr("x2",b),y.attr("x2",b),a.width=b,a.height=m.height+n.padding+.5*n.dividerMargin,a};Tb.parser.yy=bb;var Ib={},Rb={dividerMargin:10,padding:5,textHeight:10},Fb=function(t){var e=Object.entries(Ib).find((function(e){return e[1].label===t}));if(e)return e[0]};const Pb=function(t){Object.keys(t).forEach((function(e){Rb[e]=t[e]}))},jb=function(t,e){Ib={},Tb.parser.yy.clear(),Tb.parser.parse(t),o.info("Rendering diagram "+t);var n,r=au("[id='".concat(e,"']"));r.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),(n=r).append("defs").append("marker").attr("id","extensionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),n.append("defs").append("marker").attr("id","extensionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z"),n.append("defs").append("marker").attr("id","compositionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","compositionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z");var i=new(kb().Graph)({multigraph:!0});i.setGraph({isMultiGraph:!0}),i.setDefaultEdgeLabel((function(){return{}}));for(var a=bb.getClasses(),s=Object.keys(a),c=0;c<s.length;c++){var u=a[s[c]],l=Lb(r,u,Rb);Ib[l.id]=l,i.setNode(l.id,l),o.info("Org height: "+l.height)}bb.getRelations().forEach((function(t){o.info("tjoho"+Fb(t.id1)+Fb(t.id2)+JSON.stringify(t)),i.setEdge(Fb(t.id1),Fb(t.id2),{relation:t},t.title||"DEFAULT")})),xb().layout(i),i.nodes().forEach((function(t){void 0!==t&&void 0!==i.node(t)&&(o.debug("Node "+t+": "+JSON.stringify(i.node(t))),au("#"+fb(t)).attr("transform","translate("+(i.node(t).x-i.node(t).width/2)+","+(i.node(t).y-i.node(t).height/2)+" )"))})),i.edges().forEach((function(t){void 0!==t&&void 0!==i.edge(t)&&(o.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(i.edge(t))),function(t,e,n,r){var i=function(t){switch(t){case gb.AGGREGATION:return"aggregation";case gb.EXTENSION:return"extension";case gb.COMPOSITION:return"composition";case gb.DEPENDENCY:return"dependency"}};e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var a,s,c=e.points,u=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(Vu),l=t.append("path").attr("d",u(c)).attr("id","edge"+Cb).attr("class","relation"),h="";r.arrowMarkerAbsolute&&(h=(h=(h=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),1==n.relation.lineType&&l.attr("class","relation dashed-line"),"none"!==n.relation.type1&&l.attr("marker-start","url("+h+"#"+i(n.relation.type1)+"Start)"),"none"!==n.relation.type2&&l.attr("marker-end","url("+h+"#"+i(n.relation.type2)+"End)");var f,d,p,y,g=e.points.length,m=Hv.calcLabelPosition(e.points);if(a=m.x,s=m.y,g%2!=0&&g>1){var v=Hv.calcCardinalityPosition("none"!==n.relation.type1,e.points,e.points[0]),b=Hv.calcCardinalityPosition("none"!==n.relation.type2,e.points,e.points[g-1]);o.debug("cardinality_1_point "+JSON.stringify(v)),o.debug("cardinality_2_point "+JSON.stringify(b)),f=v.x,d=v.y,p=b.x,y=b.y}if(void 0!==n.title){var _=t.append("g").attr("class","classLabel"),x=_.append("text").attr("class","label").attr("x",a).attr("y",s).attr("fill","red").attr("text-anchor","middle").text(n.title);window.label=x;var w=x.node().getBBox();_.insert("rect",":first-child").attr("class","box").attr("x",w.x-r.padding/2).attr("y",w.y-r.padding/2).attr("width",w.width+r.padding).attr("height",w.height+r.padding)}o.info("Rendering relation "+JSON.stringify(n)),void 0!==n.relationTitle1&&"none"!==n.relationTitle1&&t.append("g").attr("class","cardinality").append("text").attr("class","type1").attr("x",f).attr("y",d).attr("fill","black").attr("font-size","6").text(n.relationTitle1),void 0!==n.relationTitle2&&"none"!==n.relationTitle2&&t.append("g").attr("class","cardinality").append("text").attr("class","type2").attr("x",p).attr("y",y).attr("fill","black").attr("font-size","6").text(n.relationTitle2),Cb++}(r,i.edge(t),i.edge(t).relation,Rb))}));var h=r.node().getBBox(),f=h.width+40,d=h.height+40;zv(r,d,f,Rb.useMaxWidth);var p="".concat(h.x-20," ").concat(h.y-20," ").concat(f," ").concat(d);o.debug("viewBox ".concat(p)),r.attr("viewBox",p)};var Yb={extension:function(t,e,n){o.trace("Making markers for ",n),t.append("defs").append("marker").attr("id",e+"-extensionStart").attr("class","marker extension "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),t.append("defs").append("marker").attr("id",e+"-extensionEnd").attr("class","marker extension "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z")},composition:function(t,e){t.append("defs").append("marker").attr("id",e+"-compositionStart").attr("class","marker composition "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-compositionEnd").attr("class","marker composition "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},aggregation:function(t,e){t.append("defs").append("marker").attr("id",e+"-aggregationStart").attr("class","marker aggregation "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-aggregationEnd").attr("class","marker aggregation "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},dependency:function(t,e){t.append("defs").append("marker").attr("id",e+"-dependencyStart").attr("class","marker dependency "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-dependencyEnd").attr("class","marker dependency "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},point:function(t,e){t.append("marker").attr("id",e+"-pointEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-pointStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",0).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},circle:function(t,e){t.append("marker").attr("id",e+"-circleEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-circleStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},cross:function(t,e){t.append("marker").attr("id",e+"-crossEnd").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-crossStart").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0")},barb:function(t,e){t.append("defs").append("marker").attr("id",e+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","strokeWidth").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")}};const zb=function(t,e,n,r){e.forEach((function(e){Yb[e](t,n,r)}))};function Ub(t){return Ub="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Ub(t)}const qb=function(t,e,n,r){var i,a,s,c,u,l,h=t||"";if("object"===Ub(h)&&(h=h[0]),zm(Jv().flowchart.htmlLabels))return h=h.replace(/\\n|\n/g,"<br />"),o.info("vertexText"+h),i={isNode:r,label:h.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),labelStyle:e.replace("fill:","color:")},s=au(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")),c=s.append("xhtml:div"),u=i.label,l=i.isNode?"nodeLabel":"edgeLabel",c.html('<span class="'+l+'" '+(i.labelStyle?'style="'+i.labelStyle+'"':"")+">"+u+"</span>"),(a=i.labelStyle)&&c.attr("style",a),c.style("display","inline-block"),c.style("white-space","nowrap"),c.attr("xmlns","http://www.w3.org/1999/xhtml"),s.node();var f=document.createElementNS("http://www.w3.org/2000/svg","text");f.setAttribute("style",e.replace("color:","fill:"));var d=[];d="string"==typeof h?h.split(/\\n|\n|<br\s*\/?>/gi):Array.isArray(h)?h:[];for(var p=0;p<d.length;p++){var y=document.createElementNS("http://www.w3.org/2000/svg","tspan");y.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),y.setAttribute("dy","1em"),y.setAttribute("x","0"),n?y.setAttribute("class","title-row"):y.setAttribute("class","row"),y.textContent=d[p].trim(),f.appendChild(y)}return f};var Hb=function(t,e,n,r){var i;i=n||"node default";var a=t.insert("g").attr("class",i).attr("id",e.domId||e.id),o=a.insert("g").attr("class","label").attr("style",e.labelStyle),s="string"==typeof e.labelText?e.labelText:e.labelText[0],c=o.node().appendChild(qb(Fm(iC(s),Jv()),e.labelStyle,!1,r)),u=c.getBBox();if(zm(Jv().flowchart.htmlLabels)){var l=c.children[0],h=au(c);u=l.getBoundingClientRect(),h.attr("width",u.width),h.attr("height",u.height)}var f=e.padding/2;return o.attr("transform","translate("+-u.width/2+", "+-u.height/2+")"),{shapeSvg:a,bbox:u,halfPadding:f,label:o}},$b=function(t,e){var n=e.node().getBBox();t.width=n.width,t.height=n.height};function Wb(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("class","label-container").attr("transform","translate("+-e/2+","+n/2+")")}var Vb={},Gb={},Xb={},Zb=function(t,e){return o.trace("In isDecendant",e," ",t," = ",Gb[e].indexOf(t)>=0),Gb[e].indexOf(t)>=0},Qb=function t(e,n,r,i){o.warn("Copying children of ",e,"root",i,"data",n.node(e),i);var a=n.children(e)||[];e!==i&&a.push(e),o.warn("Copying (nodes) clusterId",e,"nodes",a),a.forEach((function(a){if(n.children(a).length>0)t(a,n,r,i);else{var s=n.node(a);o.info("cp ",a," to ",i," with parent ",e),r.setNode(a,s),i!==n.parent(a)&&(o.warn("Setting parent",a,n.parent(a)),r.setParent(a,n.parent(a))),e!==i&&a!==e?(o.debug("Setting parent",a,e),r.setParent(a,e)):(o.info("In copy ",e,"root",i,"data",n.node(e),i),o.debug("Not Setting parent for node=",a,"cluster!==rootId",e!==i,"node!==clusterId",a!==e));var c=n.edges(a);o.debug("Copying Edges",c),c.forEach((function(t){o.info("Edge",t);var a=n.edge(t.v,t.w,t.name);o.info("Edge data",a,i);try{!function(t,e){return o.info("Decendants of ",e," is ",Gb[e]),o.info("Edge is ",t),t.v!==e&&t.w!==e&&(Gb[e]?(o.info("Here "),Gb[e].indexOf(t.v)>=0||!!Zb(t.v,e)||!!Zb(t.w,e)||Gb[e].indexOf(t.w)>=0):(o.debug("Tilt, ",e,",not in decendants"),!1))}(t,i)?o.info("Skipping copy of edge ",t.v,"--\x3e",t.w," rootId: ",i," clusterId:",e):(o.info("Copying as ",t.v,t.w,a,t.name),r.setEdge(t.v,t.w,a,t.name),o.info("newGraph edges ",r.edges(),r.edge(r.edges()[0])))}catch(t){o.error(t)}}))}o.debug("Removing node",a),n.removeNode(a)}))},Kb=function t(e,n){for(var r=n.children(e),i=[].concat(r),a=0;a<r.length;a++)Xb[r[a]]=e,i=i.concat(t(r[a],n));return i},Jb=function t(e,n){o.trace("Searching",e);var r=n.children(e);if(o.trace("Searching children of id ",e,r),r.length<1)return o.trace("This is a valid node",e),e;for(var i=0;i<r.length;i++){var a=t(r[i],n);if(a)return o.trace("Found replacement for",e," => ",a),a}},t_=function(t){return Vb[t]&&Vb[t].externalConnections&&Vb[t]?Vb[t].id:t},e_=function(t,e){!t||e>10?o.debug("Opting out, no graph "):(o.debug("Opting in, graph "),t.nodes().forEach((function(e){t.children(e).length>0&&(o.warn("Cluster identified",e," Replacement id in edges: ",Jb(e,t)),Gb[e]=Kb(e,t),Vb[e]={id:Jb(e,t),clusterData:t.node(e)})})),t.nodes().forEach((function(e){var n=t.children(e),r=t.edges();n.length>0?(o.debug("Cluster identified",e,Gb),r.forEach((function(t){t.v!==e&&t.w!==e&&Zb(t.v,e)^Zb(t.w,e)&&(o.warn("Edge: ",t," leaves cluster ",e),o.warn("Decendants of XXX ",e,": ",Gb[e]),Vb[e].externalConnections=!0)}))):o.debug("Not a cluster ",e,Gb)})),t.edges().forEach((function(e){var n=t.edge(e);o.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(e)),o.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(t.edge(e)));var r=e.v,i=e.w;o.warn("Fix XXX",Vb,"ids:",e.v,e.w,"Translateing: ",Vb[e.v]," --- ",Vb[e.w]),(Vb[e.v]||Vb[e.w])&&(o.warn("Fixing and trixing - removing XXX",e.v,e.w,e.name),r=t_(e.v),i=t_(e.w),t.removeEdge(e.v,e.w,e.name),r!==e.v&&(n.fromCluster=e.v),i!==e.w&&(n.toCluster=e.w),o.warn("Fix Replacing with XXX",r,i,e.name),t.setEdge(r,i,n,e.name))})),o.warn("Adjusted Graph",kb().json.write(t)),n_(t,0),o.trace(Vb))},n_=function t(e,n){if(o.warn("extractor - ",n,kb().json.write(e),e.children("D")),n>10)o.error("Bailing out");else{for(var r=e.nodes(),i=!1,a=0;a<r.length;a++){var s=r[a],c=e.children(s);i=i||c.length>0}if(i){o.debug("Nodes = ",r,n);for(var u=0;u<r.length;u++){var l=r[u];if(o.debug("Extracting node",l,Vb,Vb[l]&&!Vb[l].externalConnections,!e.parent(l),e.node(l),e.children("D")," Depth ",n),Vb[l])if(!Vb[l].externalConnections&&e.children(l)&&e.children(l).length>0){o.warn("Cluster without external connections, without a parent and with children",l,n);var h="TB"===e.graph().rankdir?"LR":"TB";Vb[l]&&Vb[l].clusterData&&Vb[l].clusterData.dir&&(h=Vb[l].clusterData.dir,o.warn("Fixing dir",Vb[l].clusterData.dir,h));var f=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:h,nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));o.warn("Old graph before copy",kb().json.write(e)),Qb(l,e,f,l),e.setNode(l,{clusterNode:!0,id:l,clusterData:Vb[l].clusterData,labelText:Vb[l].labelText,graph:f}),o.warn("New graph after copy node: (",l,")",kb().json.write(f)),o.debug("Old graph after copy",kb().json.write(e))}else o.warn("Cluster ** ",l," **not meeting the criteria !externalConnections:",!Vb[l].externalConnections," no parent: ",!e.parent(l)," children ",e.children(l)&&e.children(l).length>0,e.children("D"),n),o.debug(Vb);else o.debug("Not a cluster",l,n)}r=e.nodes(),o.warn("New list of nodes",r);for(var d=0;d<r.length;d++){var p=r[d],y=e.node(p);o.warn(" Now next level",p,y),y.clusterNode&&t(y.graph,n+1)}}else o.debug("Done, no node has children",e.nodes())}},r_=function t(e,n){if(0===n.length)return[];var r=Object.assign(n);return n.forEach((function(n){var i=e.children(n),a=t(e,i);r=r.concat(a)})),r},i_=function(t){return r_(t,t.children())},a_=n(3841);const o_=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);return r.y<a&&(l=-l),{x:i+u,y:a+l}};function s_(t,e){return t*e>0}const c_=function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,y,g;if(i=e.y-t.y,o=t.x-e.x,c=e.x*t.y-t.x*e.y,f=i*n.x+o*n.y+c,d=i*r.x+o*r.y+c,!(0!==f&&0!==d&&s_(f,d)||(a=r.y-n.y,s=n.x-r.x,u=r.x*n.y-n.x*r.y,l=a*t.x+s*t.y+u,h=a*e.x+s*e.y+u,0!==l&&0!==h&&s_(l,h)||0==(p=i*s-a*o))))return y=Math.abs(p/2),{x:(g=o*u-s*c)<0?(g-y)/p:(g+y)/p,y:(g=a*c-i*u)<0?(g-y)/p:(g+y)/p}},u_=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o),{x:i+n,y:a+r}},l_=(n.n(a_)(),function(t,e,n){return o_(t,e,e,n)}),h_=function(t,e,n){var r=t.x,i=t.y,a=[],o=Number.POSITIVE_INFINITY,s=Number.POSITIVE_INFINITY;"function"==typeof e.forEach?e.forEach((function(t){o=Math.min(o,t.x),s=Math.min(s,t.y)})):(o=Math.min(o,e.x),s=Math.min(s,e.y));for(var c=r-t.width/2-o,u=i-t.height/2-s,l=0;l<e.length;l++){var h=e[l],f=e[l<e.length-1?l+1:0],d=c_(t,n,{x:c+h.x,y:u+h.y},{x:c+f.x,y:u+f.y});d&&a.push(d)}return a.length?(a.length>1&&a.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1})),a[0]):t},f_=u_;function d_(t){return d_="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},d_(t)}var p_=function(t,e,n){var r=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),i=70,a=10;"LR"===n&&(i=10,a=70);var o=r.append("rect").attr("x",-1*i/2).attr("y",-1*a/2).attr("width",i).attr("height",a).attr("class","fork-join");return $b(e,o),e.height=e.height+e.padding/2,e.width=e.width+e.padding/2,e.intersect=function(t){return f_(e,t)},r},y_={question:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding+(i.height+e.padding),s=[{x:a/2,y:0},{x:a,y:-a/2},{x:a/2,y:-a},{x:0,y:-a/2}];o.info("Question main (Circle)");var c=Wb(r,a,a,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return o.warn("Intersect called"),h_(e,s,t)},r},rect:function(t,e){var n=Hb(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;o.trace("Classes = ",e.classes);var s=r.insert("rect",":first-child"),c=i.width+e.padding,u=i.height+e.padding;if(s.attr("class","basic label-container").attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",c).attr("height",u),e.props){var l=new Set(Object.keys(e.props));e.props.borders&&(function(t,e,n,r){var i=[],a=function(t){i.push(t),i.push(0)},s=function(t){i.push(0),i.push(t)};e.includes("t")?(o.debug("add top border"),a(n)):s(n),e.includes("r")?(o.debug("add right border"),a(r)):s(r),e.includes("b")?(o.debug("add bottom border"),a(n)):s(n),e.includes("l")?(o.debug("add left border"),a(r)):s(r),t.attr("stroke-dasharray",i.join(" "))}(s,e.props.borders,c,u),l.delete("borders")),l.forEach((function(t){o.warn("Unknown node property ".concat(t))}))}return $b(e,s),e.intersect=function(t){return f_(e,t)},r},rectWithTitle:function(t,e){var n;n=e.classes?"node "+e.classes:"node default";var r,i=t.insert("g").attr("class",n).attr("id",e.domId||e.id),a=i.insert("rect",":first-child"),s=i.insert("line"),c=i.insert("g").attr("class","label"),u=e.labelText.flat?e.labelText.flat():e.labelText;r="object"===d_(u)?u[0]:u,o.info("Label text abc79",r,u,"object"===d_(u));var l=c.node().appendChild(qb(r,e.labelStyle,!0,!0)),h={width:0,height:0};if(zm(Jv().flowchart.htmlLabels)){var f=l.children[0],d=au(l);h=f.getBoundingClientRect(),d.attr("width",h.width),d.attr("height",h.height)}o.info("Text 2",u);var p=u.slice(1,u.length),y=l.getBBox(),g=c.node().appendChild(qb(p.join?p.join("<br/>"):p,e.labelStyle,!0,!0));if(zm(Jv().flowchart.htmlLabels)){var m=g.children[0],v=au(g);h=m.getBoundingClientRect(),v.attr("width",h.width),v.attr("height",h.height)}var b=e.padding/2;return au(g).attr("transform","translate( "+(h.width>y.width?0:(y.width-h.width)/2)+", "+(y.height+b+5)+")"),au(l).attr("transform","translate( "+(h.width<y.width?0:-(y.width-h.width)/2)+", 0)"),h=c.node().getBBox(),c.attr("transform","translate("+-h.width/2+", "+(-h.height/2-b+3)+")"),a.attr("class","outer title-state").attr("x",-h.width/2-b).attr("y",-h.height/2-b).attr("width",h.width+e.padding).attr("height",h.height+e.padding),s.attr("class","divider").attr("x1",-h.width/2-b).attr("x2",h.width/2+b).attr("y1",-h.height/2-b+y.height+b).attr("y2",-h.height/2-b+y.height+b),$b(e,a),e.intersect=function(t){return f_(e,t)},i},choice:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id);return n.insert("polygon",":first-child").attr("points",[{x:0,y:14},{x:14,y:0},{x:0,y:-14},{x:-14,y:0}].map((function(t){return t.x+","+t.y})).join(" ")).attr("class","state-start").attr("r",7).attr("width",28).attr("height",28),e.width=28,e.height=28,e.intersect=function(t){return l_(e,14,t)},n},circle:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding,s=r.insert("circle",":first-child");return s.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("r",i.width/2+a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),o.info("Circle main"),$b(e,s),e.intersect=function(t){return o.info("Circle intersect",e,i.width/2+a,t),l_(e,i.width/2+a,t)},r},stadium:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=i.width+a/4+e.padding,s=r.insert("rect",":first-child").attr("style",e.style).attr("rx",a/2).attr("ry",a/2).attr("x",-o/2).attr("y",-a/2).attr("width",o).attr("height",a);return $b(e,s),e.intersect=function(t){return f_(e,t)},r},hexagon:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=a/4,s=i.width+2*o+e.padding,c=[{x:o,y:0},{x:s-o,y:0},{x:s,y:-a/2},{x:s-o,y:-a},{x:o,y:-a},{x:0,y:-a/2}],u=Wb(r,s,a,c);return u.attr("style",e.style),$b(e,u),e.intersect=function(t){return h_(e,c,t)},r},rect_left_inv_arrow:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:-o/2,y:0},{x:a,y:0},{x:a,y:-o},{x:-o/2,y:-o},{x:0,y:-o/2}];return Wb(r,a,o,s).attr("style",e.style),e.width=a+o,e.height=o,e.intersect=function(t){return h_(e,s,t)},r},lean_right:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:-2*o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},lean_left:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:2*o/6,y:0},{x:a+o/6,y:0},{x:a-2*o/6,y:-o},{x:-o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},trapezoid:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:-2*o/6,y:0},{x:a+2*o/6,y:0},{x:a-o/6,y:-o},{x:o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},inv_trapezoid:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:-2*o/6,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},rect_right_inv_arrow:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:0,y:0},{x:a+o/2,y:0},{x:a,y:-o/2},{x:a+o/2,y:-o},{x:0,y:-o}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},cylinder:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=a/2,s=o/(2.5+a/50),c=i.height+s+e.padding,u="M 0,"+s+" a "+o+","+s+" 0,0,0 "+a+" 0 a "+o+","+s+" 0,0,0 "+-a+" 0 l 0,"+c+" a "+o+","+s+" 0,0,0 "+a+" 0 l 0,"+-c,l=r.attr("label-offset-y",s).insert("path",":first-child").attr("style",e.style).attr("d",u).attr("transform","translate("+-a/2+","+-(c/2+s)+")");return $b(e,l),e.intersect=function(t){var n=f_(e,t),r=n.x-e.x;if(0!=o&&(Math.abs(r)<e.width/2||Math.abs(r)==e.width/2&&Math.abs(n.y-e.y)>e.height/2-s)){var i=s*s*(1-r*r/(o*o));0!=i&&(i=Math.sqrt(i)),i=s-i,t.y-e.y>0&&(i=-i),n.y+=i}return n},r},start:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child");return r.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),$b(e,r),e.intersect=function(t){return l_(e,7,t)},n},end:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child"),i=n.insert("circle",":first-child");return i.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),r.attr("class","state-end").attr("r",5).attr("width",10).attr("height",10),$b(e,i),e.intersect=function(t){return l_(e,7,t)},n},note:function(t,e){var n=Hb(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;o.info("Classes = ",e.classes);var s=r.insert("rect",":first-child");return s.attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),$b(e,s),e.intersect=function(t){return f_(e,t)},r},subroutine:function(t,e){var n=Hb(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=[{x:0,y:0},{x:a,y:0},{x:a,y:-o},{x:0,y:-o},{x:0,y:0},{x:-8,y:0},{x:a+8,y:0},{x:a+8,y:-o},{x:-8,y:-o},{x:-8,y:0}],c=Wb(r,a,o,s);return c.attr("style",e.style),$b(e,c),e.intersect=function(t){return h_(e,s,t)},r},fork:p_,join:p_,class_box:function(t,e){var n,r=e.padding/2;n=e.classes?"node "+e.classes:"node default";var i=t.insert("g").attr("class",n).attr("id",e.domId||e.id),a=i.insert("rect",":first-child"),o=i.insert("line"),s=i.insert("line"),c=0,u=4,l=i.insert("g").attr("class","label"),h=0,f=e.classData.annotations&&e.classData.annotations[0],d=e.classData.annotations[0]?"«"+e.classData.annotations[0]+"»":"",p=l.node().appendChild(qb(d,e.labelStyle,!0,!0)),y=p.getBBox();if(zm(Jv().flowchart.htmlLabels)){var g=p.children[0],m=au(p);y=g.getBoundingClientRect(),m.attr("width",y.width),m.attr("height",y.height)}e.classData.annotations[0]&&(u+=y.height+4,c+=y.width);var v=e.classData.id;void 0!==e.classData.type&&""!==e.classData.type&&(Jv().flowchart.htmlLabels?v+="&lt;"+e.classData.type+"&gt;":v+="<"+e.classData.type+">");var b=l.node().appendChild(qb(v,e.labelStyle,!0,!0));au(b).attr("class","classTitle");var _=b.getBBox();if(zm(Jv().flowchart.htmlLabels)){var x=b.children[0],w=au(b);_=x.getBoundingClientRect(),w.attr("width",_.width),w.attr("height",_.height)}u+=_.height+4,_.width>c&&(c=_.width);var k=[];e.classData.members.forEach((function(t){var n=Sb(t),r=n.displayText;Jv().flowchart.htmlLabels&&(r=r.replace(/</g,"&lt;").replace(/>/g,"&gt;"));var i=l.node().appendChild(qb(r,n.cssStyle?n.cssStyle:e.labelStyle,!0,!0)),a=i.getBBox();if(zm(Jv().flowchart.htmlLabels)){var o=i.children[0],s=au(i);a=o.getBoundingClientRect(),s.attr("width",a.width),s.attr("height",a.height)}a.width>c&&(c=a.width),u+=a.height+4,k.push(i)})),u+=8;var T=[];if(e.classData.methods.forEach((function(t){var n=Sb(t),r=n.displayText;Jv().flowchart.htmlLabels&&(r=r.replace(/</g,"&lt;").replace(/>/g,"&gt;"));var i=l.node().appendChild(qb(r,n.cssStyle?n.cssStyle:e.labelStyle,!0,!0)),a=i.getBBox();if(zm(Jv().flowchart.htmlLabels)){var o=i.children[0],s=au(i);a=o.getBoundingClientRect(),s.attr("width",a.width),s.attr("height",a.height)}a.width>c&&(c=a.width),u+=a.height+4,T.push(i)})),u+=8,f){var E=(c-y.width)/2;au(p).attr("transform","translate( "+(-1*c/2+E)+", "+-1*u/2+")"),h=y.height+4}var C=(c-_.width)/2;return au(b).attr("transform","translate( "+(-1*c/2+C)+", "+(-1*u/2+h)+")"),h+=_.height+4,o.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,k.forEach((function(t){au(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h+4)+")"),h+=_.height+4})),h+=8,s.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,T.forEach((function(t){au(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h)+")"),h+=_.height+4})),a.attr("class","outer title-state").attr("x",-c/2-r).attr("y",-u/2-r).attr("width",c+e.padding).attr("height",u+e.padding),$b(e,a),e.intersect=function(t){return f_(e,t)},i}},g_={},m_=function(t){var e=g_[t.id];o.trace("Transforming node",t.diff,t,"translate("+(t.x-t.width/2-5)+", "+t.width/2+")");var n=t.diff||0;return t.clusterNode?e.attr("transform","translate("+(t.x+n-t.width/2)+", "+(t.y-t.height/2-8)+")"):e.attr("transform","translate("+t.x+", "+t.y+")"),n},v_={rect:function(t,e){o.trace("Creating subgraph rect for ",e.id,e);var n=t.insert("g").attr("class","cluster"+(e.class?" "+e.class:"")).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=i.node().appendChild(qb(e.labelText,e.labelStyle,void 0,!0)),s=a.getBBox();if(zm(Jv().flowchart.htmlLabels)){var c=a.children[0],u=au(a);s=c.getBoundingClientRect(),u.attr("width",s.width),u.attr("height",s.height)}var l=0*e.padding,h=l/2,f=e.width<=s.width+l?s.width+l:e.width;e.width<=s.width+l?e.diff=(s.width-e.width)/2:e.diff=-e.padding/2,o.trace("Data ",e,JSON.stringify(e)),r.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-f/2).attr("y",e.y-e.height/2-h).attr("width",f).attr("height",e.height+l),i.attr("transform","translate("+(e.x-s.width/2)+", "+(e.y-e.height/2+e.padding/3)+")");var d=r.node().getBBox();return e.width=d.width,e.height=d.height,e.intersect=function(t){return u_(e,t)},n},roundedWithTitle:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=n.append("rect"),o=i.node().appendChild(qb(e.labelText,e.labelStyle,void 0,!0)),s=o.getBBox();if(zm(Jv().flowchart.htmlLabels)){var c=o.children[0],u=au(o);s=c.getBoundingClientRect(),u.attr("width",s.width),u.attr("height",s.height)}s=o.getBBox();var l=0*e.padding,h=l/2,f=e.width<=s.width+e.padding?s.width+e.padding:e.width;e.width<=s.width+e.padding?e.diff=(s.width+0*e.padding-e.width)/2:e.diff=-e.padding/2,r.attr("class","outer").attr("x",e.x-f/2-h).attr("y",e.y-e.height/2-h).attr("width",f+l).attr("height",e.height+l),a.attr("class","inner").attr("x",e.x-f/2-h).attr("y",e.y-e.height/2-h+s.height-1).attr("width",f+l).attr("height",e.height+l-s.height-3),i.attr("transform","translate("+(e.x-s.width/2)+", "+(e.y-e.height/2-e.padding/3+(zm(Jv().flowchart.htmlLabels)?5:3))+")");var d=r.node().getBBox();return e.height=d.height,e.intersect=function(t){return u_(e,t)},n},noteGroup:function(t,e){var n=t.insert("g").attr("class","note-cluster").attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2-a).attr("width",e.width+i).attr("height",e.height+i).attr("fill","none");var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.intersect=function(t){return u_(e,t)},n},divider:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("class","divider").attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2).attr("width",e.width+i).attr("height",e.height+i);var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.diff=-e.padding/2,e.intersect=function(t){return u_(e,t)},n}},b_={},__={},x_={},w_=function(t,e){var n=qb(e.label,e.labelStyle),r=t.insert("g").attr("class","edgeLabel"),i=r.insert("g").attr("class","label");i.node().appendChild(n);var a,o=n.getBBox();if(zm(Jv().flowchart.htmlLabels)){var s=n.children[0],c=au(n);o=s.getBoundingClientRect(),c.attr("width",o.width),c.attr("height",o.height)}if(i.attr("transform","translate("+-o.width/2+", "+-o.height/2+")"),__[e.id]=r,e.width=o.width,e.height=o.height,e.startLabelLeft){var u=qb(e.startLabelLeft,e.labelStyle),l=t.insert("g").attr("class","edgeTerminals"),h=l.insert("g").attr("class","inner");a=h.node().appendChild(u);var f=u.getBBox();h.attr("transform","translate("+-f.width/2+", "+-f.height/2+")"),x_[e.id]||(x_[e.id]={}),x_[e.id].startLeft=l,k_(a,e.startLabelLeft)}if(e.startLabelRight){var d=qb(e.startLabelRight,e.labelStyle),p=t.insert("g").attr("class","edgeTerminals"),y=p.insert("g").attr("class","inner");a=p.node().appendChild(d),y.node().appendChild(d);var g=d.getBBox();y.attr("transform","translate("+-g.width/2+", "+-g.height/2+")"),x_[e.id]||(x_[e.id]={}),x_[e.id].startRight=p,k_(a,e.startLabelRight)}if(e.endLabelLeft){var m=qb(e.endLabelLeft,e.labelStyle),v=t.insert("g").attr("class","edgeTerminals"),b=v.insert("g").attr("class","inner");a=b.node().appendChild(m);var _=m.getBBox();b.attr("transform","translate("+-_.width/2+", "+-_.height/2+")"),v.node().appendChild(m),x_[e.id]||(x_[e.id]={}),x_[e.id].endLeft=v,k_(a,e.endLabelLeft)}if(e.endLabelRight){var x=qb(e.endLabelRight,e.labelStyle),w=t.insert("g").attr("class","edgeTerminals"),k=w.insert("g").attr("class","inner");a=k.node().appendChild(x);var T=x.getBBox();k.attr("transform","translate("+-T.width/2+", "+-T.height/2+")"),w.node().appendChild(x),x_[e.id]||(x_[e.id]={}),x_[e.id].endRight=w,k_(a,e.endLabelRight)}};function k_(t,e){Jv().flowchart.htmlLabels&&t&&(t.style.width=9*e.length+"px",t.style.height="12px")}var T_=function(t,e){o.info("Moving label abc78 ",t.id,t.label,__[t.id]);var n=e.updatedPath?e.updatedPath:e.originalPath;if(t.label){var r=__[t.id],i=t.x,a=t.y;if(n){var s=Hv.calcLabelPosition(n);o.info("Moving label from (",i,",",a,") to (",s.x,",",s.y,") abc78")}r.attr("transform","translate("+i+", "+a+")")}if(t.startLabelLeft){var c=x_[t.id].startLeft,u=t.x,l=t.y;if(n){var h=Hv.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_left",n);u=h.x,l=h.y}c.attr("transform","translate("+u+", "+l+")")}if(t.startLabelRight){var f=x_[t.id].startRight,d=t.x,p=t.y;if(n){var y=Hv.calcTerminalLabelPosition(t.arrowTypeStart?10:0,"start_right",n);d=y.x,p=y.y}f.attr("transform","translate("+d+", "+p+")")}if(t.endLabelLeft){var g=x_[t.id].endLeft,m=t.x,v=t.y;if(n){var b=Hv.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_left",n);m=b.x,v=b.y}g.attr("transform","translate("+m+", "+v+")")}if(t.endLabelRight){var _=x_[t.id].endRight,x=t.x,w=t.y;if(n){var k=Hv.calcTerminalLabelPosition(t.arrowTypeEnd?10:0,"end_right",n);x=k.x,w=k.y}_.attr("transform","translate("+x+", "+w+")")}},E_=function(t,e){o.warn("abc88 cutPathAtIntersect",t,e);var n=[],r=t[0],i=!1;return t.forEach((function(t){if(o.info("abc88 checking point",t,e),function(t,e){var n=t.x,r=t.y,i=Math.abs(e.x-n),a=Math.abs(e.y-r),o=t.width/2,s=t.height/2;return i>=o||a>=s}(e,t)||i)o.warn("abc88 outside",t,r),r=t,i||n.push(t);else{var a=function(t,e,n){o.warn("intersection calc abc89:\n  outsidePoint: ".concat(JSON.stringify(e),"\n  insidePoint : ").concat(JSON.stringify(n),"\n  node        : x:").concat(t.x," y:").concat(t.y," w:").concat(t.width," h:").concat(t.height));var r=t.x,i=t.y,a=Math.abs(r-n.x),s=t.width/2,c=n.x<e.x?s-a:s+a,u=t.height/2,l=Math.abs(e.y-n.y),h=Math.abs(e.x-n.x);if(Math.abs(i-e.y)*s>Math.abs(r-e.x)*u){var f=n.y<e.y?e.y-u-i:i-u-e.y;c=h*f/l;var d={x:n.x<e.x?n.x+c:n.x-h+c,y:n.y<e.y?n.y+l-f:n.y-l+f};return 0===c&&(d.x=e.x,d.y=e.y),0===h&&(d.x=e.x),0===l&&(d.y=e.y),o.warn("abc89 topp/bott calc, Q ".concat(l,", q ").concat(f,", R ").concat(h,", r ").concat(c),d),d}var p=l*(c=n.x<e.x?e.x-s-r:r-s-e.x)/h,y=n.x<e.x?n.x+h-c:n.x-h+c,g=n.y<e.y?n.y+p:n.y-p;return o.warn("sides calc abc89, Q ".concat(l,", q ").concat(p,", R ").concat(h,", r ").concat(c),{_x:y,_y:g}),0===c&&(y=e.x,g=e.y),0===h&&(y=e.x),0===l&&(g=e.y),{x:y,y:g}}(e,r,t);o.warn("abc88 inside",t,r,a),o.warn("abc88 intersection",a);var s=!1;n.forEach((function(t){s=s||t.x===a.x&&t.y===a.y})),n.find((function(t){return t.x===a.x&&t.y===a.y}))?o.warn("abc88 no intersect",a,n):n.push(a),i=!0}})),o.warn("abc88 returning points",n),n},C_=function t(e,n,r,i){o.info("Graph in recursive render: XXX",kb().json.write(n),i);var a=n.graph().rankdir;o.trace("Dir in recursive render - dir:",a);var s=e.insert("g").attr("class","root");n.nodes()?o.info("Recursive render XXX",n.nodes()):o.info("No nodes found for",n),n.edges().length>0&&o.trace("Recursive edges",n.edge(n.edges()[0]));var c=s.insert("g").attr("class","clusters"),u=s.insert("g").attr("class","edgePaths"),l=s.insert("g").attr("class","edgeLabels"),h=s.insert("g").attr("class","nodes");n.nodes().forEach((function(e){var s=n.node(e);if(void 0!==i){var c=JSON.parse(JSON.stringify(i.clusterData));o.info("Setting data for cluster XXX (",e,") ",c,i),n.setNode(i.id,c),n.parent(e)||(o.trace("Setting parent",e,i.id),n.setParent(e,i.id,c))}if(o.info("(Insert) Node XXX"+e+": "+JSON.stringify(n.node(e))),s&&s.clusterNode){o.info("Cluster identified",e,s.width,n.node(e));var u=t(h,s.graph,r,n.node(e)),l=u.elem;$b(s,l),s.diff=u.diff||0,o.info("Node bounds (abc123)",e,s,s.width,s.x,s.y),function(t,e){g_[e.id]=t}(l,s),o.warn("Recursive render complete ",l,s)}else n.children(e).length>0?(o.info("Cluster - the non recursive path XXX",e,s.id,s,n),o.info(Jb(s.id,n)),Vb[s.id]={id:Jb(s.id,n),node:s}):(o.info("Node - the non recursive path",e,s.id,s),function(t,e,n){var r,i;e.link?(r=t.insert("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget||"_blank"),i=y_[e.shape](r,e,n)):r=i=y_[e.shape](t,e,n),e.tooltip&&i.attr("title",e.tooltip),e.class&&i.attr("class","node default "+e.class),g_[e.id]=r,e.haveCallback&&g_[e.id].attr("class",g_[e.id].attr("class")+" clickable")}(h,n.node(e),a))})),n.edges().forEach((function(t){var e=n.edge(t.v,t.w,t.name);o.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t)),o.info("Edge "+t.v+" -> "+t.w+": ",t," ",JSON.stringify(n.edge(t))),o.info("Fix",Vb,"ids:",t.v,t.w,"Translateing: ",Vb[t.v],Vb[t.w]),w_(l,e)})),n.edges().forEach((function(t){o.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t))})),o.info("#############################################"),o.info("###                Layout                 ###"),o.info("#############################################"),o.info(n),xb().layout(n),o.info("Graph after layout:",kb().json.write(n));var f=0;return i_(n).forEach((function(t){var e=n.node(t);o.info("Position "+t+": "+JSON.stringify(n.node(t))),o.info("Position "+t+": ("+e.x,","+e.y,") width: ",e.width," height: ",e.height),e&&e.clusterNode?m_(e):n.children(t).length>0?(function(t,e){o.trace("Inserting cluster");var n=e.shape||"rect";b_[e.id]=v_[n](t,e)}(c,e),Vb[e.id].node=e):m_(e)})),n.edges().forEach((function(t){var e=n.edge(t);o.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(e),e);var i=function(t,e,n,r,i,a){var s=n.points,c=!1,u=a.node(e.v),l=a.node(e.w);o.info("abc88 InsertEdge: ",n),l.intersect&&u.intersect&&((s=s.slice(1,n.points.length-1)).unshift(u.intersect(s[0])),o.info("Last point",s[s.length-1],l,l.intersect(s[s.length-1])),s.push(l.intersect(s[s.length-1]))),n.toCluster&&(o.info("to cluster abc88",r[n.toCluster]),s=E_(n.points,r[n.toCluster].node),c=!0),n.fromCluster&&(o.info("from cluster abc88",r[n.fromCluster]),s=E_(s.reverse(),r[n.fromCluster].node).reverse(),c=!0);var h,f=s.filter((function(t){return!Number.isNaN(t.y)}));h=("graph"===i||"flowchart"===i)&&n.curve||Vu;var d,p=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(h);switch(n.thickness){case"normal":d="edge-thickness-normal";break;case"thick":d="edge-thickness-thick";break;default:d=""}switch(n.pattern){case"solid":d+=" edge-pattern-solid";break;case"dotted":d+=" edge-pattern-dotted";break;case"dashed":d+=" edge-pattern-dashed"}var y=t.append("path").attr("d",p(f)).attr("id",n.id).attr("class"," "+d+(n.classes?" "+n.classes:"")).attr("style",n.style),g="";switch(Jv().state.arrowMarkerAbsolute&&(g=(g=(g=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),o.info("arrowTypeStart",n.arrowTypeStart),o.info("arrowTypeEnd",n.arrowTypeEnd),n.arrowTypeStart){case"arrow_cross":y.attr("marker-start","url("+g+"#"+i+"-crossStart)");break;case"arrow_point":y.attr("marker-start","url("+g+"#"+i+"-pointStart)");break;case"arrow_barb":y.attr("marker-start","url("+g+"#"+i+"-barbStart)");break;case"arrow_circle":y.attr("marker-start","url("+g+"#"+i+"-circleStart)");break;case"aggregation":y.attr("marker-start","url("+g+"#"+i+"-aggregationStart)");break;case"extension":y.attr("marker-start","url("+g+"#"+i+"-extensionStart)");break;case"composition":y.attr("marker-start","url("+g+"#"+i+"-compositionStart)");break;case"dependency":y.attr("marker-start","url("+g+"#"+i+"-dependencyStart)")}switch(n.arrowTypeEnd){case"arrow_cross":y.attr("marker-end","url("+g+"#"+i+"-crossEnd)");break;case"arrow_point":y.attr("marker-end","url("+g+"#"+i+"-pointEnd)");break;case"arrow_barb":y.attr("marker-end","url("+g+"#"+i+"-barbEnd)");break;case"arrow_circle":y.attr("marker-end","url("+g+"#"+i+"-circleEnd)");break;case"aggregation":y.attr("marker-end","url("+g+"#"+i+"-aggregationEnd)");break;case"extension":y.attr("marker-end","url("+g+"#"+i+"-extensionEnd)");break;case"composition":y.attr("marker-end","url("+g+"#"+i+"-compositionEnd)");break;case"dependency":y.attr("marker-end","url("+g+"#"+i+"-dependencyEnd)")}var m={};return c&&(m.updatedPath=s),m.originalPath=n.points,m}(u,t,e,Vb,r,n);T_(e,i)})),n.nodes().forEach((function(t){var e=n.node(t);o.info(t,e.type,e.diff),"group"===e.type&&(f=e.diff)})),{elem:s,diff:f}},S_=function(t,e,n,r,i){zb(t,n,r,i),g_={},__={},x_={},b_={},Gb={},Xb={},Vb={},o.warn("Graph at first:",kb().json.write(e)),e_(e),o.warn("Graph after:",kb().json.write(e)),C_(t,e,r)};Tb.parser.yy=bb;var A_={dividerMargin:10,padding:5,textHeight:10};function M_(t){var e;switch(t){case 0:e="aggregation";break;case 1:e="extension";break;case 2:e="composition";break;case 3:e="dependency";break;default:e="none"}return e}var N_={},D_=[],O_="",B_=function(t){return void 0===N_[t]&&(N_[t]={attributes:[]},o.info("Added new entity :",t)),N_[t]};const L_={Cardinality:{ZERO_OR_ONE:"ZERO_OR_ONE",ZERO_OR_MORE:"ZERO_OR_MORE",ONE_OR_MORE:"ONE_OR_MORE",ONLY_ONE:"ONLY_ONE"},Identification:{NON_IDENTIFYING:"NON_IDENTIFYING",IDENTIFYING:"IDENTIFYING"},parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().er},addEntity:B_,addAttributes:function(t,e){var n,r=B_(t);for(n=e.length-1;n>=0;n--)r.attributes.push(e[n]),o.debug("Added attribute ",e[n].attributeName)},getEntities:function(){return N_},addRelationship:function(t,e,n,r){var i={entityA:t,roleA:e,entityB:n,relSpec:r};D_.push(i),o.debug("Added new relationship :",i)},getRelationships:function(){return D_},clear:function(){N_={},D_=[],O_=""},setTitle:function(t){O_=t},getTitle:function(){return O_}};var I_=n(5890),R_=n.n(I_),F_={ONLY_ONE_START:"ONLY_ONE_START",ONLY_ONE_END:"ONLY_ONE_END",ZERO_OR_ONE_START:"ZERO_OR_ONE_START",ZERO_OR_ONE_END:"ZERO_OR_ONE_END",ONE_OR_MORE_START:"ONE_OR_MORE_START",ONE_OR_MORE_END:"ONE_OR_MORE_END",ZERO_OR_MORE_START:"ZERO_OR_MORE_START",ZERO_OR_MORE_END:"ZERO_OR_MORE_END"};const P_=F_;var j_={},Y_=function(t){return(t.entityA+t.roleA+t.entityB).replace(/\s/g,"")},z_=0;const U_=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)j_[e[n]]=t[e[n]]},q_=function(t,e){o.info("Drawing ER diagram"),L_.clear();var n=R_().parser;n.yy=L_;try{n.parse(t)}catch(t){o.debug("Parsing failed")}var r,i=au("[id='".concat(e,"']"));(function(t,e){var n;t.append("defs").append("marker").attr("id",F_.ONLY_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18 M15,0 L15,18"),t.append("defs").append("marker").attr("id",F_.ONLY_ONE_END).attr("refX",18).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,0 L3,18 M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",21).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_ONE_END).attr("refX",30).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,0 L21,18"),t.append("defs").append("marker").attr("id",F_.ONE_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27"),t.append("defs").append("marker").attr("id",F_.ONE_OR_MORE_END).attr("refX",27).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",48).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q18,0 36,18 Q18,36 0,18"),(n=t.append("defs").append("marker").attr("id",F_.ZERO_OR_MORE_END).attr("refX",39).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,18 Q39,0 57,18 Q39,36 21,18")})(i,j_),r=new(kb().Graph)({multigraph:!0,directed:!0,compound:!1}).setGraph({rankdir:j_.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel((function(){return{}}));var a=function(t,e,n){var r;return Object.keys(e).forEach((function(i){var a=t.append("g").attr("id",i);r=void 0===r?i:r;var o="entity-"+i,s=a.append("text").attr("class","er entityLabel").attr("id",o).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+j_.fontSize+"px").text(i),c=function(t,e,n){var r=j_.entityPadding/3,i=j_.entityPadding/3,a=.85*j_.fontSize,o=e.node().getBBox(),s=[],c=!1,u=!1,l=0,h=0,f=0,d=0,p=o.height+2*r,y=1;n.forEach((function(t){void 0!==t.attributeKeyType&&(c=!0),void 0!==t.attributeComment&&(u=!0)})),n.forEach((function(n){var i="".concat(e.node().id,"-attr-").concat(y),o=0,g=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-type")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeType),m=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-name")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeName),v={};v.tn=g,v.nn=m;var b=g.node().getBBox(),_=m.node().getBBox();if(l=Math.max(l,b.width),h=Math.max(h,_.width),o=Math.max(b.height,_.height),c){var x=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-key")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeKeyType||"");v.kn=x;var w=x.node().getBBox();f=Math.max(f,w.width),o=Math.max(o,w.height)}if(u){var k=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-comment")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+a+"px").text(n.attributeComment||"");v.cn=k;var T=k.node().getBBox();d=Math.max(d,T.width),o=Math.max(o,T.height)}v.height=o,s.push(v),p+=o+2*r,y+=1}));var g=4;c&&(g+=2),u&&(g+=2);var m=l+h+f+d,v={width:Math.max(j_.minEntityWidth,Math.max(o.width+2*j_.entityPadding,m+i*g)),height:n.length>0?p:Math.max(j_.minEntityHeight,o.height+2*j_.entityPadding)};if(n.length>0){var b=Math.max(0,(v.width-m-i*g)/(g/2));e.attr("transform","translate("+v.width/2+","+(r+o.height/2)+")");var _=o.height+2*r,x="attributeBoxOdd";s.forEach((function(e){var n=_+r+e.height/2;e.tn.attr("transform","translate("+i+","+n+")");var a=t.insert("rect","#"+e.tn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",0).attr("y",_).attr("width",l+2*i+b).attr("height",e.height+2*r),o=parseFloat(a.attr("x"))+parseFloat(a.attr("width"));e.nn.attr("transform","translate("+(o+i)+","+n+")");var s=t.insert("rect","#"+e.nn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",o).attr("y",_).attr("width",h+2*i+b).attr("height",e.height+2*r),p=parseFloat(s.attr("x"))+parseFloat(s.attr("width"));if(c){e.kn.attr("transform","translate("+(p+i)+","+n+")");var y=t.insert("rect","#"+e.kn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",p).attr("y",_).attr("width",f+2*i+b).attr("height",e.height+2*r);p=parseFloat(y.attr("x"))+parseFloat(y.attr("width"))}u&&(e.cn.attr("transform","translate("+(p+i)+","+n+")"),t.insert("rect","#"+e.cn.node().id).attr("class","er ".concat(x)).attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",p).attr("y",_).attr("width",d+2*i+b).attr("height",e.height+2*r)),_+=e.height+2*r,x="attributeBoxOdd"==x?"attributeBoxEven":"attributeBoxOdd"}))}else v.height=Math.max(j_.minEntityHeight,p),e.attr("transform","translate("+v.width/2+","+v.height/2+")");return v}(a,s,e[i].attributes),u=c.width,l=c.height,h=a.insert("rect","#"+o).attr("class","er entityBox").attr("fill",j_.fill).attr("fill-opacity","100%").attr("stroke",j_.stroke).attr("x",0).attr("y",0).attr("width",u).attr("height",l).node().getBBox();n.setNode(i,{width:h.width,height:h.height,shape:"rect",id:i})})),r}(i,L_.getEntities(),r),s=function(t,e){return t.forEach((function(t){e.setEdge(t.entityA,t.entityB,{relationship:t},Y_(t))})),t}(L_.getRelationships(),r);xb().layout(r),function(t,e){e.nodes().forEach((function(n){void 0!==n&&void 0!==e.node(n)&&t.select("#"+n).attr("transform","translate("+(e.node(n).x-e.node(n).width/2)+","+(e.node(n).y-e.node(n).height/2)+" )")}))}(i,r),s.forEach((function(t){!function(t,e,n,r){z_++;var i=n.edge(e.entityA,e.entityB,Y_(e)),a=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(Vu),o=t.insert("path","#"+r).attr("class","er relationshipLine").attr("d",a(i.points)).attr("stroke",j_.stroke).attr("fill","none");e.relSpec.relType===L_.Identification.NON_IDENTIFYING&&o.attr("stroke-dasharray","8,8");var s="";switch(j_.arrowMarkerAbsolute&&(s=(s=(s=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),e.relSpec.cardA){case L_.Cardinality.ZERO_OR_ONE:o.attr("marker-end","url("+s+"#"+P_.ZERO_OR_ONE_END+")");break;case L_.Cardinality.ZERO_OR_MORE:o.attr("marker-end","url("+s+"#"+P_.ZERO_OR_MORE_END+")");break;case L_.Cardinality.ONE_OR_MORE:o.attr("marker-end","url("+s+"#"+P_.ONE_OR_MORE_END+")");break;case L_.Cardinality.ONLY_ONE:o.attr("marker-end","url("+s+"#"+P_.ONLY_ONE_END+")")}switch(e.relSpec.cardB){case L_.Cardinality.ZERO_OR_ONE:o.attr("marker-start","url("+s+"#"+P_.ZERO_OR_ONE_START+")");break;case L_.Cardinality.ZERO_OR_MORE:o.attr("marker-start","url("+s+"#"+P_.ZERO_OR_MORE_START+")");break;case L_.Cardinality.ONE_OR_MORE:o.attr("marker-start","url("+s+"#"+P_.ONE_OR_MORE_START+")");break;case L_.Cardinality.ONLY_ONE:o.attr("marker-start","url("+s+"#"+P_.ONLY_ONE_START+")")}var c=o.node().getTotalLength(),u=o.node().getPointAtLength(.5*c),l="rel"+z_,h=t.append("text").attr("class","er relationshipLabel").attr("id",l).attr("x",u.x).attr("y",u.y).attr("text-anchor","middle").attr("dominant-baseline","middle").attr("style","font-family: "+Jv().fontFamily+"; font-size: "+j_.fontSize+"px").text(e.roleA).node().getBBox();t.insert("rect","#"+l).attr("class","er relationshipLabelBox").attr("x",u.x-h.width/2).attr("y",u.y-h.height/2).attr("width",h.width).attr("height",h.height).attr("fill","white").attr("fill-opacity","85%")}(i,t,r,a)}));var c=j_.diagramPadding,u=i.node().getBBox(),l=u.width+2*c,h=u.height+2*c;zv(i,h,l,j_.useMaxWidth),i.attr("viewBox","".concat(u.x-c," ").concat(u.y-c," ").concat(l," ").concat(h))};function H_(t){return H_="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},H_(t)}function $_(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var W_,V_,G_="flowchart-",X_=0,Z_=Jv(),Q_={},K_=[],J_=[],tx=[],ex={},nx={},rx=0,ix=!0,ax=[],ox=function(t){for(var e=Object.keys(Q_),n=0;n<e.length;n++)if(Q_[e[n]].id===t)return Q_[e[n]].domId;return t},sx=function(t,e,n,r){var i={start:t,end:e,type:void 0,text:""};void 0!==(r=n.text)&&(i.text=Um.sanitizeText(r.trim(),Z_),'"'===i.text[0]&&'"'===i.text[i.text.length-1]&&(i.text=i.text.substring(1,i.text.length-1))),void 0!==n&&(i.type=n.type,i.stroke=n.stroke,i.length=n.length),K_.push(i)},cx=function(t,e){t.split(",").forEach((function(t){var n=t;void 0!==Q_[n]&&Q_[n].classes.push(e),void 0!==ex[n]&&ex[n].classes.push(e)}))},ux=function(t){var e=au(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=au("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),au(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=au(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),au(this).classed("hover",!1)}))};ax.push(ux);var lx=function(t){for(var e=0;e<tx.length;e++)if(tx[e].id===t)return e;return-1},hx=-1,fx=[],dx=function t(e,n){var r=tx[n].nodes;if(!((hx+=1)>2e3)){if(fx[hx]=n,tx[n].id===e)return{result:!0,count:0};for(var i=0,a=1;i<r.length;){var o=lx(r[i]);if(o>=0){var s=t(e,o);if(s.result)return{result:!0,count:a+s.count};a+=s.count}i+=1}return{result:!1,count:a}}},px=function(t,e){var n=!1;return t.forEach((function(t){t.nodes.indexOf(e)>=0&&(n=!0)})),n},yx=function(t,e){var n=[];return t.nodes.forEach((function(r,i){px(e,r)||n.push(t.nodes[i])})),{nodes:n}};const gx={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},defaultConfig:function(){return Vv.flowchart},addVertex:function(t,e,n,r,i,a){var o,s=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{},c=t;void 0!==c&&0!==c.trim().length&&(void 0===Q_[c]&&(Q_[c]={id:c,domId:G_+c+"-"+X_,styles:[],classes:[]}),X_++,void 0!==e?(Z_=Jv(),'"'===(o=Um.sanitizeText(e.trim(),Z_))[0]&&'"'===o[o.length-1]&&(o=o.substring(1,o.length-1)),Q_[c].text=o):void 0===Q_[c].text&&(Q_[c].text=t),void 0!==n&&(Q_[c].type=n),null!=r&&r.forEach((function(t){Q_[c].styles.push(t)})),null!=i&&i.forEach((function(t){Q_[c].classes.push(t)})),void 0!==a&&(Q_[c].dir=a),Q_[c].props=s)},lookUpDomId:ox,addLink:function(t,e,n,r){var i,a;for(i=0;i<t.length;i++)for(a=0;a<e.length;a++)sx(t[i],e[a],n,r)},updateLinkInterpolate:function(t,e){t.forEach((function(t){"default"===t?K_.defaultInterpolate=e:K_[t].interpolate=e}))},updateLink:function(t,e){t.forEach((function(t){"default"===t?K_.defaultStyle=e:(-1===Hv.isSubstringInArray("fill",e)&&e.push("fill:none"),K_[t].style=e)}))},addClass:function(t,e){void 0===J_[t]&&(J_[t]={id:t,styles:[],textStyles:[]}),null!=e&&e.forEach((function(e){if(e.match("color")){var n=e.replace("fill","bgFill").replace("color","fill");J_[t].textStyles.push(n)}J_[t].styles.push(e)}))},setDirection:function(t){(W_=t).match(/.*</)&&(W_="RL"),W_.match(/.*\^/)&&(W_="BT"),W_.match(/.*>/)&&(W_="LR"),W_.match(/.*v/)&&(W_="TB")},setClass:cx,setTooltip:function(t,e){t.split(",").forEach((function(t){void 0!==e&&(nx["gen-1"===V_?ox(t):t]=Um.sanitizeText(e,Z_))}))},getTooltip:function(t){return nx[t]},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){var r=ox(t);if("loose"===Jv().securityLevel&&void 0!==e){var i=[];if("string"==typeof n){i=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var a=0;a<i.length;a++){var o=i[a].trim();'"'===o.charAt(0)&&'"'===o.charAt(o.length-1)&&(o=o.substr(1,o.length-2)),i[a]=o}}0===i.length&&i.push(t),void 0!==Q_[t]&&(Q_[t].haveCallback=!0,ax.push((function(){var t=document.querySelector('[id="'.concat(r,'"]'));null!==t&&t.addEventListener("click",(function(){var t;Hv.runFunc.apply(Hv,[e].concat(function(t){if(Array.isArray(t))return $_(t)}(t=i)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return $_(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?$_(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()))}),!1)})))}}(t,e,n)})),cx(t,"clickable")},setLink:function(t,e,n){t.split(",").forEach((function(t){void 0!==Q_[t]&&(Q_[t].link=Hv.formatUrl(e,Z_),Q_[t].linkTarget=n)})),cx(t,"clickable")},bindFunctions:function(t){ax.forEach((function(e){e(t)}))},getDirection:function(){return W_.trim()},getVertices:function(){return Q_},getEdges:function(){return K_},getClasses:function(){return J_},clear:function(t){Q_={},J_={},K_=[],(ax=[]).push(ux),tx=[],ex={},rx=0,nx=[],ix=!0,V_=t||"gen-1"},setGen:function(t){V_=t||"gen-1"},defaultStyle:function(){return"fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"},addSubGraph:function(t,e,n){var r=t.trim(),i=n;t===n&&n.match(/\s/)&&(r=void 0);var a=[],s=function(t){var e,n={boolean:{},number:{},string:{}},r=[],i=t.filter((function(t){var i=H_(t);return t.stmt&&"dir"===t.stmt?(e=t.value,!1):""!==t.trim()&&(i in n?!n[i].hasOwnProperty(t)&&(n[i][t]=!0):!(r.indexOf(t)>=0)&&r.push(t))}));return{nodeList:i,dir:e}}(a.concat.apply(a,e)),c=s.nodeList,u=s.dir;if(a=c,"gen-1"===V_){o.warn("LOOKING UP");for(var l=0;l<a.length;l++)a[l]=ox(a[l])}r=r||"subGraph"+rx,i=i||"",i=Um.sanitizeText(i,Z_),rx+=1;var h={id:r,nodes:a,title:i.trim(),classes:[],dir:u};return o.info("Adding",h.id,h.nodes,h.dir),h.nodes=yx(h,tx).nodes,tx.push(h),ex[r]=h,r},getDepthFirstPos:function(t){return fx[t]},indexNodes:function(){hx=-1,tx.length>0&&dx("none",tx.length-1)},getSubGraphs:function(){return tx},destructLink:function(t,e){var n,r=function(t){var e=t.trim(),n=e.slice(0,-1),r="arrow_open";switch(e.slice(-1)){case"x":r="arrow_cross","x"===e[0]&&(r="double_"+r,n=n.slice(1));break;case">":r="arrow_point","<"===e[0]&&(r="double_"+r,n=n.slice(1));break;case"o":r="arrow_circle","o"===e[0]&&(r="double_"+r,n=n.slice(1))}var i="normal",a=n.length-1;"="===n[0]&&(i="thick");var o=function(t,e){for(var n=e.length,r=0,i=0;i<n;++i)"."===e[i]&&++r;return r}(0,n);return o&&(i="dotted",a=o),{type:r,stroke:i,length:a}}(t);if(e){if(n=function(t){var e=t.trim(),n="arrow_open";switch(e[0]){case"<":n="arrow_point",e=e.slice(1);break;case"x":n="arrow_cross",e=e.slice(1);break;case"o":n="arrow_circle",e=e.slice(1)}var r="normal";return-1!==e.indexOf("=")&&(r="thick"),-1!==e.indexOf(".")&&(r="dotted"),{type:n,stroke:r}}(e),n.stroke!==r.stroke)return{type:"INVALID",stroke:"INVALID"};if("arrow_open"===n.type)n.type=r.type;else{if(n.type!==r.type)return{type:"INVALID",stroke:"INVALID"};n.type="double_"+n.type}return"double_arrow"===n.type&&(n.type="double_arrow_point"),n.length=r.length,n}return r},lex:{firstGraph:function(){return!!ix&&(ix=!1,!0)}},exists:px,makeUniq:yx};var mx=n(3602),vx=n.n(mx),bx=n(4949),_x=n.n(bx),xx=n(8284),wx=n.n(xx);function kx(t,e,n){var r=.9*(e.width+e.height),i=[{x:r/2,y:0},{x:r,y:-r/2},{x:r/2,y:-r},{x:0,y:-r/2}],a=Lx(t,r,r,i);return n.intersect=function(t){return _x().intersect.polygon(n,i,t)},a}function Tx(t,e,n){var r=e.height,i=r/4,a=e.width+2*i,o=[{x:i,y:0},{x:a-i,y:0},{x:a,y:-r/2},{x:a-i,y:-r},{x:i,y:-r},{x:0,y:-r/2}],s=Lx(t,a,r,o);return n.intersect=function(t){return _x().intersect.polygon(n,o,t)},s}function Ex(t,e,n){var r=e.width,i=e.height,a=[{x:-i/2,y:0},{x:r,y:0},{x:r,y:-i},{x:-i/2,y:-i},{x:0,y:-i/2}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Cx(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Sx(t,e,n){var r=e.width,i=e.height,a=[{x:2*i/6,y:0},{x:r+i/6,y:0},{x:r-2*i/6,y:-i},{x:-i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Ax(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r+2*i/6,y:0},{x:r-i/6,y:-i},{x:i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Mx(t,e,n){var r=e.width,i=e.height,a=[{x:i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:-2*i/6,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Nx(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r+i/2,y:0},{x:r,y:-i/2},{x:r+i/2,y:-i},{x:0,y:-i}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Dx(t,e,n){var r=e.height,i=e.width+r/4,a=t.insert("rect",":first-child").attr("rx",r/2).attr("ry",r/2).attr("x",-i/2).attr("y",-r/2).attr("width",i).attr("height",r);return n.intersect=function(t){return _x().intersect.rect(n,t)},a}function Ox(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r,y:0},{x:r,y:-i},{x:0,y:-i},{x:0,y:0},{x:-8,y:0},{x:r+8,y:0},{x:r+8,y:-i},{x:-8,y:-i},{x:-8,y:0}],o=Lx(t,r,i,a);return n.intersect=function(t){return _x().intersect.polygon(n,a,t)},o}function Bx(t,e,n){var r=e.width,i=r/2,a=i/(2.5+r/50),o=e.height+a,s="M 0,"+a+" a "+i+","+a+" 0,0,0 "+r+" 0 a "+i+","+a+" 0,0,0 "+-r+" 0 l 0,"+o+" a "+i+","+a+" 0,0,0 "+r+" 0 l 0,"+-o,c=t.attr("label-offset-y",a).insert("path",":first-child").attr("d",s).attr("transform","translate("+-r/2+","+-(o/2+a)+")");return n.intersect=function(t){var e=_x().intersect.rect(n,t),r=e.x-n.x;if(0!=i&&(Math.abs(r)<n.width/2||Math.abs(r)==n.width/2&&Math.abs(e.y-n.y)>n.height/2-a)){var o=a*a*(1-r*r/(i*i));0!=o&&(o=Math.sqrt(o)),o=a-o,t.y-n.y>0&&(o=-o),e.y+=o}return e},c}function Lx(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("transform","translate("+-e/2+","+n/2+")")}const Ix=function(t){t.shapes().question=kx,t.shapes().hexagon=Tx,t.shapes().stadium=Dx,t.shapes().subroutine=Ox,t.shapes().cylinder=Bx,t.shapes().rect_left_inv_arrow=Ex,t.shapes().lean_right=Cx,t.shapes().lean_left=Sx,t.shapes().trapezoid=Ax,t.shapes().inv_trapezoid=Mx,t.shapes().rect_right_inv_arrow=Nx};var Rx={};const Fx=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)Rx[e[n]]=t[e[n]]},Px=function(t,e){o.info("Drawing flowchart"),gx.clear(),gx.setGen("gen-1");var n=vx().parser;n.yy=gx,n.parse(t);var r=gx.getDirection();void 0===r&&(r="TD");for(var i,a=Jv().flowchart,s=a.nodeSpacing||50,c=a.rankSpacing||50,u=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:s,ranksep:c,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=gx.getSubGraphs(),h=l.length-1;h>=0;h--)i=l[h],gx.addVertex(i.id,i.title,"group",void 0,i.classes);var f=gx.getVertices();o.warn("Get vertices",f);var d=gx.getEdges(),p=0;for(p=l.length-1;p>=0;p--){i=l[p],ou("cluster").append("text");for(var y=0;y<i.nodes.length;y++)o.warn("Setting subgraph",i.nodes[y],gx.lookUpDomId(i.nodes[y]),gx.lookUpDomId(i.id)),u.setParent(gx.lookUpDomId(i.nodes[y]),gx.lookUpDomId(i.id))}(function(t,e,n){var r=au('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var s,c=Nv(i.styles),u=void 0!==i.text?i.text:i.id;if(zm(Jv().flowchart.htmlLabels)){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(s=wx()(r,l).node()).parentNode.removeChild(s)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",c.labelStyle.replace("color:","fill:"));for(var f=u.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}s=h}var y=0,g="";switch(i.type){case"round":y=5,g="rect";break;case"square":case"group":default:g="rect";break;case"diamond":g="question";break;case"hexagon":g="hexagon";break;case"odd":case"odd_right":g="rect_left_inv_arrow";break;case"lean_right":g="lean_right";break;case"lean_left":g="lean_left";break;case"trapezoid":g="trapezoid";break;case"inv_trapezoid":g="inv_trapezoid";break;case"circle":g="circle";break;case"ellipse":g="ellipse";break;case"stadium":g="stadium";break;case"subroutine":g="subroutine";break;case"cylinder":g="cylinder"}o.warn("Adding node",i.id,i.domId),e.setNode(gx.lookUpDomId(i.id),{labelType:"svg",labelStyle:c.labelStyle,shape:g,label:s,rx:y,ry:y,class:a,style:c.style,id:gx.lookUpDomId(i.id)})}))})(f,u,e),function(t,e){var n,r,i=0;if(void 0!==t.defaultStyle){var a=Nv(t.defaultStyle);n=a.style,r=a.labelStyle}t.forEach((function(a){i++;var o="L-"+a.start+"-"+a.end,s="LS-"+a.start,c="LE-"+a.end,u={};"arrow_open"===a.type?u.arrowhead="none":u.arrowhead="normal";var l="",h="";if(void 0!==a.style){var f=Nv(a.style);l=f.style,h=f.labelStyle}else switch(a.stroke){case"normal":l="fill:none",void 0!==n&&(l=n),void 0!==r&&(h=r);break;case"dotted":l="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":l=" stroke-width: 3.5px;fill:none"}u.style=l,u.labelStyle=h,void 0!==a.interpolate?u.curve=Av(a.interpolate,Pu):void 0!==t.defaultInterpolate?u.curve=Av(t.defaultInterpolate,Pu):u.curve=Av(Rx.curve,Pu),void 0===a.text?void 0!==a.style&&(u.arrowheadStyle="fill: #333"):(u.arrowheadStyle="fill: #333",u.labelpos="c",zm(Jv().flowchart.htmlLabels)?(u.labelType="html",u.label='<span id="L-'.concat(o,'" class="edgeLabel L-').concat(s,"' L-").concat(c,'">').concat(a.text.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),"</span>")):(u.labelType="text",u.label=a.text.replace(Um.lineBreakRegex,"\n"),void 0===a.style&&(u.style=u.style||"stroke: #333; stroke-width: 1.5px;fill:none"),u.labelStyle=u.labelStyle.replace("color:","fill:"))),u.id=o,u.class=s+" "+c,u.minlen=a.length||1,e.setEdge(gx.lookUpDomId(a.start),gx.lookUpDomId(a.end),u,i)}))}(d,u);var g=new(0,_x().render);Ix(g),g.arrows().none=function(t,e,n,r){var i=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 0 0 L 0 0 z");_x().util.applyStyle(i,n[r+"Style"])},g.arrows().normal=function(t,e){t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowheadPath").style("stroke-width",1).style("stroke-dasharray","1,0")};var m=au('[id="'.concat(e,'"]'));m.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),o.warn(u);var v=au("#"+e+" g");g(v,u),v.selectAll("g.node").attr("title",(function(){return gx.getTooltip(this.id)}));var b=a.diagramPadding,_=m.node().getBBox(),x=_.width+2*b,w=_.height+2*b;zv(m,w,x,a.useMaxWidth);var k="".concat(_.x-b," ").concat(_.y-b," ").concat(x," ").concat(w);for(o.debug("viewBox ".concat(k)),m.attr("viewBox",k),gx.indexNodes("subGraph"+p),p=0;p<l.length;p++)if("undefined"!==(i=l[p]).title){var T=document.querySelectorAll("#"+e+' [id="'+gx.lookUpDomId(i.id)+'"] rect'),E=document.querySelectorAll("#"+e+' [id="'+gx.lookUpDomId(i.id)+'"]'),C=T[0].x.baseVal.value,S=T[0].y.baseVal.value,A=T[0].width.baseVal.value,M=au(E[0]).select(".label");M.attr("transform","translate(".concat(C+A/2,", ").concat(S+14,")")),M.attr("id",e+"Text");for(var N=0;N<i.classes.length;N++)E[0].classList.add(i.classes[N])}zm(a.htmlLabels);for(var D=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),O=0;O<D.length;O++){var B=D[O],L=B.getBBox(),I=document.createElementNS("http://www.w3.org/2000/svg","rect");I.setAttribute("rx",0),I.setAttribute("ry",0),I.setAttribute("width",L.width),I.setAttribute("height",L.height),B.insertBefore(I,B.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=au("#"+e+' [id="'+gx.lookUpDomId(t)+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))};var jx={};const Yx=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)jx[e[n]]=t[e[n]]},zx=function(t,e){o.info("Drawing flowchart"),gx.clear(),gx.setGen("gen-2");var n=vx().parser;n.yy=gx,n.parse(t);var r=gx.getDirection();void 0===r&&(r="TD");var i,a=Jv().flowchart,s=a.nodeSpacing||50,c=a.rankSpacing||50,u=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:s,ranksep:c,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=gx.getSubGraphs();o.info("Subgraphs - ",l);for(var h=l.length-1;h>=0;h--)i=l[h],o.info("Subgraph - ",i),gx.addVertex(i.id,i.title,"group",void 0,i.classes,i.dir);var f=gx.getVertices(),d=gx.getEdges();o.info(d);var p=0;for(p=l.length-1;p>=0;p--){i=l[p],ou("cluster").append("text");for(var y=0;y<i.nodes.length;y++)o.info("Setting up subgraphs",i.nodes[y],i.id),u.setParent(i.nodes[y],i.id)}(function(t,e,n){var r=au('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var s,c=Nv(i.styles),u=void 0!==i.text?i.text:i.id;if(zm(Jv().flowchart.htmlLabels)){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(s=wx()(r,l).node()).parentNode.removeChild(s)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",c.labelStyle.replace("color:","fill:"));for(var f=u.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}s=h}var y=0,g="";switch(i.type){case"round":y=5,g="rect";break;case"square":case"group":default:g="rect";break;case"diamond":g="question";break;case"hexagon":g="hexagon";break;case"odd":case"odd_right":g="rect_left_inv_arrow";break;case"lean_right":g="lean_right";break;case"lean_left":g="lean_left";break;case"trapezoid":g="trapezoid";break;case"inv_trapezoid":g="inv_trapezoid";break;case"circle":g="circle";break;case"ellipse":g="ellipse";break;case"stadium":g="stadium";break;case"subroutine":g="subroutine";break;case"cylinder":g="cylinder"}e.setNode(i.id,{labelStyle:c.labelStyle,shape:g,labelText:u,rx:y,ry:y,class:a,style:c.style,id:i.id,link:i.link,linkTarget:i.linkTarget,tooltip:gx.getTooltip(i.id)||"",domId:gx.lookUpDomId(i.id),haveCallback:i.haveCallback,width:"group"===i.type?500:void 0,dir:i.dir,type:i.type,props:i.props,padding:Jv().flowchart.padding}),o.info("setNode",{labelStyle:c.labelStyle,shape:g,labelText:u,rx:y,ry:y,class:a,style:c.style,id:i.id,domId:gx.lookUpDomId(i.id),width:"group"===i.type?500:void 0,type:i.type,dir:i.dir,props:i.props,padding:Jv().flowchart.padding})}))})(f,u,e),function(t,e){o.info("abc78 edges = ",t);var n,r,i=0,a={};if(void 0!==t.defaultStyle){var s=Nv(t.defaultStyle);n=s.style,r=s.labelStyle}t.forEach((function(s){i++;var c="L-"+s.start+"-"+s.end;void 0===a[c]?(a[c]=0,o.info("abc78 new entry",c,a[c])):(a[c]++,o.info("abc78 new entry",c,a[c]));var u=c+"-"+a[c];o.info("abc78 new link id to be used is",c,u,a[c]);var l="LS-"+s.start,h="LE-"+s.end,f={style:"",labelStyle:""};switch(f.minlen=s.length||1,"arrow_open"===s.type?f.arrowhead="none":f.arrowhead="normal",f.arrowTypeStart="arrow_open",f.arrowTypeEnd="arrow_open",s.type){case"double_arrow_cross":f.arrowTypeStart="arrow_cross";case"arrow_cross":f.arrowTypeEnd="arrow_cross";break;case"double_arrow_point":f.arrowTypeStart="arrow_point";case"arrow_point":f.arrowTypeEnd="arrow_point";break;case"double_arrow_circle":f.arrowTypeStart="arrow_circle";case"arrow_circle":f.arrowTypeEnd="arrow_circle"}var d="",p="";switch(s.stroke){case"normal":d="fill:none;",void 0!==n&&(d=n),void 0!==r&&(p=r),f.thickness="normal",f.pattern="solid";break;case"dotted":f.thickness="normal",f.pattern="dotted",f.style="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":f.thickness="thick",f.pattern="solid",f.style="stroke-width: 3.5px;fill:none;"}if(void 0!==s.style){var y=Nv(s.style);d=y.style,p=y.labelStyle}f.style=f.style+=d,f.labelStyle=f.labelStyle+=p,void 0!==s.interpolate?f.curve=Av(s.interpolate,Pu):void 0!==t.defaultInterpolate?f.curve=Av(t.defaultInterpolate,Pu):f.curve=Av(jx.curve,Pu),void 0===s.text?void 0!==s.style&&(f.arrowheadStyle="fill: #333"):(f.arrowheadStyle="fill: #333",f.labelpos="c"),f.labelType="text",f.label=s.text.replace(Um.lineBreakRegex,"\n"),void 0===s.style&&(f.style=f.style||"stroke: #333; stroke-width: 1.5px;fill:none;"),f.labelStyle=f.labelStyle.replace("color:","fill:"),f.id=u,f.classes="flowchart-link "+l+" "+h,e.setEdge(s.start,s.end,f,i)}))}(d,u);var g=au('[id="'.concat(e,'"]'));g.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var m=au("#"+e+" g");S_(m,u,["point","circle","cross"],"flowchart",e);var v=a.diagramPadding,b=g.node().getBBox(),_=b.width+2*v,x=b.height+2*v;if(o.debug("new ViewBox 0 0 ".concat(_," ").concat(x),"translate(".concat(v-u._label.marginx,", ").concat(v-u._label.marginy,")")),zv(g,x,_,a.useMaxWidth),g.attr("viewBox","0 0 ".concat(_," ").concat(x)),g.select("g").attr("transform","translate(".concat(v-u._label.marginx,", ").concat(v-b.y,")")),gx.indexNodes("subGraph"+p),!a.htmlLabels)for(var w=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),k=0;k<w.length;k++){var T=w[k],E=T.getBBox(),C=document.createElementNS("http://www.w3.org/2000/svg","rect");C.setAttribute("rx",0),C.setAttribute("ry",0),C.setAttribute("width",E.width),C.setAttribute("height",E.height),T.insertBefore(C,T.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=au("#"+e+' [id="'+t+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))};function Ux(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var qx,Hx,$x="",Wx="",Vx="",Gx=[],Xx=[],Zx="",Qx=[],Kx=[],Jx="",tw=["active","done","crit","milestone"],ew=[],nw=!1,rw=!1,iw=0,aw=function(t,e,n,r){return!(r.indexOf(t.format(e.trim()))>=0)&&(t.isoWeekday()>=6&&n.indexOf("weekends")>=0||n.indexOf(t.format("dddd").toLowerCase())>=0||n.indexOf(t.format(e.trim()))>=0)},ow=function(t,e,n,r){if(n.length&&!t.manualEndTime){var a=i()(t.startTime,e,!0);a.add(1,"d");var o=i()(t.endTime,e,!0),s=sw(a,o,e,n,r);t.endTime=o.toDate(),t.renderEndTime=s}},sw=function(t,e,n,r,i){for(var a=!1,o=null;t<=e;)a||(o=e.toDate()),(a=aw(t,n,r,i))&&e.add(1,"d"),t.add(1,"d");return o},cw=function(t,e,n){n=n.trim();var r=/^after\s+([\d\w- ]+)/.exec(n.trim());if(null!==r){var a=null;if(r[1].split(" ").forEach((function(t){var e=yw(t);void 0!==e&&(a?e.endTime>a.endTime&&(a=e):a=e)})),a)return a.endTime;var s=new Date;return s.setHours(0,0,0,0),s}var c=i()(n,e.trim(),!0);return c.isValid()?c.toDate():(o.debug("Invalid date:"+n),o.debug("With date format:"+e.trim()),new Date)},uw=function(t,e){if(null!==t)switch(t[2]){case"s":e.add(t[1],"seconds");break;case"m":e.add(t[1],"minutes");break;case"h":e.add(t[1],"hours");break;case"d":e.add(t[1],"days");break;case"w":e.add(t[1],"weeks")}return e.toDate()},lw=function(t,e,n,r){r=r||!1,n=n.trim();var a=i()(n,e.trim(),!0);return a.isValid()?(r&&a.add(1,"d"),a.toDate()):uw(/^([\d]+)([wdhms])/.exec(n.trim()),i()(t))},hw=0,fw=function(t){return void 0===t?"task"+(hw+=1):t},dw=[],pw={},yw=function(t){var e=pw[t];return dw[e]},gw=function(){for(var t=function(t){var e=dw[t],n="";switch(dw[t].raw.startTime.type){case"prevTaskEnd":var r=yw(e.prevTaskId);e.startTime=r.endTime;break;case"getStartDate":(n=cw(0,$x,dw[t].raw.startTime.startData))&&(dw[t].startTime=n)}return dw[t].startTime&&(dw[t].endTime=lw(dw[t].startTime,$x,dw[t].raw.endTime.data,nw),dw[t].endTime&&(dw[t].processed=!0,dw[t].manualEndTime=i()(dw[t].raw.endTime.data,"YYYY-MM-DD",!0).isValid(),ow(dw[t],$x,Xx,Gx))),dw[t].processed},e=!0,n=0;n<dw.length;n++)t(n),e=e&&dw[n].processed;return e},mw=function(t,e){t.split(",").forEach((function(t){var n=yw(t);void 0!==n&&n.classes.push(e)}))},vw=function(t,e){ew.push((function(){var n=document.querySelector('[id="'.concat(t,'"]'));null!==n&&n.addEventListener("click",(function(){e()}))})),ew.push((function(){var n=document.querySelector('[id="'.concat(t,'-text"]'));null!==n&&n.addEventListener("click",(function(){e()}))}))};const bw={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().gantt},clear:function(){Qx=[],Kx=[],Jx="",ew=[],Zx="",hw=0,qx=void 0,Hx=void 0,dw=[],$x="",Wx="",Vx="",Gx=[],Xx=[],nw=!1,rw=!1,iw=0},setDateFormat:function(t){$x=t},getDateFormat:function(){return $x},enableInclusiveEndDates:function(){nw=!0},endDatesAreInclusive:function(){return nw},enableTopAxis:function(){rw=!0},topAxisEnabled:function(){return rw},setAxisFormat:function(t){Wx=t},getAxisFormat:function(){return Wx},setTodayMarker:function(t){Vx=t},getTodayMarker:function(){return Vx},setTitle:function(t){Zx=t},getTitle:function(){return Zx},addSection:function(t){Jx=t,Qx.push(t)},getSections:function(){return Qx},getTasks:function(){for(var t=gw(),e=0;!t&&e<10;)t=gw(),e++;return Kx=dw},addTask:function(t,e){var n={section:Jx,type:Jx,processed:!1,manualEndTime:!1,renderEndTime:null,raw:{data:e},task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};_w(n,r,tw);for(var i=0;i<n.length;i++)n[i]=n[i].trim();switch(n.length){case 1:r.id=fw(),r.startTime={type:"prevTaskEnd",id:t},r.endTime={data:n[0]};break;case 2:r.id=fw(),r.startTime={type:"getStartDate",startData:n[0]},r.endTime={data:n[1]};break;case 3:r.id=fw(n[0]),r.startTime={type:"getStartDate",startData:n[1]},r.endTime={data:n[2]}}return r}(Hx,e);n.raw.startTime=r.startTime,n.raw.endTime=r.endTime,n.id=r.id,n.prevTaskId=Hx,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,n.order=iw,iw++;var i=dw.push(n);Hx=n.id,pw[n.id]=i-1},findTaskById:yw,addTaskOrg:function(t,e){var n={section:Jx,type:Jx,description:t,task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};_w(n,r,tw);for(var a=0;a<n.length;a++)n[a]=n[a].trim();var o="";switch(n.length){case 1:r.id=fw(),r.startTime=t.endTime,o=n[0];break;case 2:r.id=fw(),r.startTime=cw(0,$x,n[0]),o=n[1];break;case 3:r.id=fw(n[0]),r.startTime=cw(0,$x,n[1]),o=n[2]}return o&&(r.endTime=lw(r.startTime,$x,o,nw),r.manualEndTime=i()(o,"YYYY-MM-DD",!0).isValid(),ow(r,$x,Xx,Gx)),r}(qx,e);n.startTime=r.startTime,n.endTime=r.endTime,n.id=r.id,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,qx=n,Kx.push(n)},setIncludes:function(t){Gx=t.toLowerCase().split(/[\s,]+/)},getIncludes:function(){return Gx},setExcludes:function(t){Xx=t.toLowerCase().split(/[\s,]+/)},getExcludes:function(){return Xx},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){if("loose"===Jv().securityLevel&&void 0!==e){var r=[];if("string"==typeof n){r=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var i=0;i<r.length;i++){var a=r[i].trim();'"'===a.charAt(0)&&'"'===a.charAt(a.length-1)&&(a=a.substr(1,a.length-2)),r[i]=a}}0===r.length&&r.push(t),void 0!==yw(t)&&vw(t,(function(){var t;Hv.runFunc.apply(Hv,[e].concat(function(t){if(Array.isArray(t))return Ux(t)}(t=r)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return Ux(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Ux(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()))}))}}(t,e,n)})),mw(t,"clickable")},setLink:function(t,e){var n=e;"loose"!==Jv().securityLevel&&(n=(0,Om.sanitizeUrl)(e)),t.split(",").forEach((function(t){void 0!==yw(t)&&vw(t,(function(){window.open(n,"_self")}))})),mw(t,"clickable")},bindFunctions:function(t){ew.forEach((function(e){e(t)}))},durationToDate:uw,isInvalidDate:aw};function _w(t,e,n){for(var r=!0;r;)r=!1,n.forEach((function(n){var i=new RegExp("^\\s*"+n+"\\s*$");t[0].match(i)&&(e[n]=!0,t.shift(1),r=!0)}))}var xw,ww=n(9959),kw=n.n(ww);ww.parser.yy=bw;const Tw=function(t,e){var n=Jv().gantt;ww.parser.yy.clear(),ww.parser.parse(t);var r=document.getElementById(e);void 0===(xw=r.parentElement.offsetWidth)&&(xw=1200),void 0!==n.useWidth&&(xw=n.useWidth);var a=ww.parser.yy.getTasks(),o=a.length*(n.barHeight+n.barGap)+2*n.topPadding;r.setAttribute("viewBox","0 0 "+xw+" "+o);for(var s=au('[id="'.concat(e,'"]')),c=function(){return Zi.apply(qs(mo,vo,Ga,Wa,Pa,Ra,La,Oa,Na,ko).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}().domain([l(a,(function(t){return t.startTime})),u(a,(function(t){return t.endTime}))]).rangeRound([0,xw-n.leftPadding-n.rightPadding]),h=[],f=0;f<a.length;f++)h.push(a[f].type);var d=h;h=function(t){for(var e={},n=[],r=0,i=t.length;r<i;++r)Object.prototype.hasOwnProperty.call(e,t[r])||(e[t[r]]=!0,n.push(t[r]));return n}(h),a.sort((function(t,e){var n=t.startTime,r=e.startTime,i=0;return n>r?i=1:n<r&&(i=-1),i})),function(t,e,r){var a=n.barHeight,o=a+n.barGap,u=n.topPadding,l=n.leftPadding;fa().domain([0,h.length]).range(["#00B9FA","#F95002"]).interpolate(jr),function(t,e,r,a,o,u,l,h){var f=u.reduce((function(t,e){var n=e.startTime;return t?Math.min(t,n):n}),0),d=u.reduce((function(t,e){var n=e.endTime;return t?Math.max(t,n):n}),0),p=ww.parser.yy.getDateFormat();if(f&&d){for(var y=[],g=null,m=i()(f);m.valueOf()<=d;)ww.parser.yy.isInvalidDate(m,p,l,h)?g?g.end=m.clone():g={start:m.clone(),end:m.clone()}:g&&(y.push(g),g=null),m.add(1,"d");s.append("g").selectAll("rect").data(y).enter().append("rect").attr("id",(function(t){return"exclude-"+t.start.format("YYYY-MM-DD")})).attr("x",(function(t){return c(t.start)+r})).attr("y",n.gridLineStartPadding).attr("width",(function(t){var e=t.end.clone().add(1,"day");return c(e)-c(t.start)})).attr("height",o-e-n.gridLineStartPadding).attr("transform-origin",(function(e,n){return(c(e.start)+r+.5*(c(e.end)-c(e.start))).toString()+"px "+(n*t+.5*o).toString()+"px"})).attr("class","exclude-range")}}(o,u,l,0,r,t,ww.parser.yy.getExcludes(),ww.parser.yy.getIncludes()),function(t,e,r,i){var a,o=(a=c,v(3,a)).tickSize(-i+e+n.gridLineStartPadding).tickFormat(Yl(ww.parser.yy.getAxisFormat()||n.axisFormat||"%Y-%m-%d"));if(s.append("g").attr("class","grid").attr("transform","translate("+t+", "+(i-50)+")").call(o).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10).attr("dy","1em"),bw.topAxisEnabled()||n.topAxis){var u=function(t){return v(1,t)}(c).tickSize(-i+e+n.gridLineStartPadding).tickFormat(Yl(ww.parser.yy.getAxisFormat()||n.axisFormat||"%Y-%m-%d"));s.append("g").attr("class","grid").attr("transform","translate("+t+", "+e+")").call(u).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10)}}(l,u,0,r),function(t,e,r,i,a,o,u){s.append("g").selectAll("rect").data(t).enter().append("rect").attr("x",0).attr("y",(function(t,n){return t.order*e+r-2})).attr("width",(function(){return u-n.rightPadding/2})).attr("height",e).attr("class",(function(t){for(var e=0;e<h.length;e++)if(t.type===h[e])return"section section"+e%n.numberSectionStyles;return"section section0"}));var l=s.append("g").selectAll("rect").data(t).enter();l.append("rect").attr("id",(function(t){return t.id})).attr("rx",3).attr("ry",3).attr("x",(function(t){return t.milestone?c(t.startTime)+i+.5*(c(t.endTime)-c(t.startTime))-.5*a:c(t.startTime)+i})).attr("y",(function(t,n){return t.order*e+r})).attr("width",(function(t){return t.milestone?a:c(t.renderEndTime||t.endTime)-c(t.startTime)})).attr("height",a).attr("transform-origin",(function(t,n){return n=t.order,(c(t.startTime)+i+.5*(c(t.endTime)-c(t.startTime))).toString()+"px "+(n*e+r+.5*a).toString()+"px"})).attr("class",(function(t){var e="";t.classes.length>0&&(e=t.classes.join(" "));for(var r=0,i=0;i<h.length;i++)t.type===h[i]&&(r=i%n.numberSectionStyles);var a="";return t.active?t.crit?a+=" activeCrit":a=" active":t.done?a=t.crit?" doneCrit":" done":t.crit&&(a+=" crit"),0===a.length&&(a=" task"),t.milestone&&(a=" milestone "+a),"task"+(a+=r)+" "+e})),l.append("text").attr("id",(function(t){return t.id+"-text"})).text((function(t){return t.task})).attr("font-size",n.fontSize).attr("x",(function(t){var e=c(t.startTime),r=c(t.renderEndTime||t.endTime);t.milestone&&(e+=.5*(c(t.endTime)-c(t.startTime))-.5*a),t.milestone&&(r=e+a);var o=this.getBBox().width;return o>r-e?r+o+1.5*n.leftPadding>u?e+i-5:r+i+5:(r-e)/2+e+i})).attr("y",(function(t,i){return t.order*e+n.barHeight/2+(n.fontSize/2-2)+r})).attr("text-height",a).attr("class",(function(t){var e=c(t.startTime),r=c(t.endTime);t.milestone&&(r=e+a);var i=this.getBBox().width,o="";t.classes.length>0&&(o=t.classes.join(" "));for(var s=0,l=0;l<h.length;l++)t.type===h[l]&&(s=l%n.numberSectionStyles);var f="";return t.active&&(f=t.crit?"activeCritText"+s:"activeText"+s),t.done?f=t.crit?f+" doneCritText"+s:f+" doneText"+s:t.crit&&(f=f+" critText"+s),t.milestone&&(f+=" milestoneText"),i>r-e?r+i+1.5*n.leftPadding>u?o+" taskTextOutsideLeft taskTextOutside"+s+" "+f:o+" taskTextOutsideRight taskTextOutside"+s+" "+f+" width-"+i:o+" taskText taskText"+s+" "+f+" width-"+i}))}(t,o,u,l,a,0,e),function(t,e){for(var r=[],i=0,a=0;a<h.length;a++)r[a]=[h[a],(o=h[a],c=d,function(t){for(var e=t.length,n={};e;)n[t[--e]]=(n[t[e]]||0)+1;return n}(c)[o]||0)];var o,c;s.append("g").selectAll("text").data(r).enter().append((function(t){var e=t[0].split(Um.lineBreakRegex),n=-(e.length-1)/2,r=document.createElementNS("http://www.w3.org/2000/svg","text");r.setAttribute("dy",n+"em");for(var i=0;i<e.length;i++){var a=document.createElementNS("http://www.w3.org/2000/svg","tspan");a.setAttribute("alignment-baseline","central"),a.setAttribute("x","10"),i>0&&a.setAttribute("dy","1em"),a.textContent=e[i],r.appendChild(a)}return r})).attr("x",10).attr("y",(function(n,a){if(!(a>0))return n[1]*t/2+e;for(var o=0;o<a;o++)return i+=r[a-1][1],n[1]*t/2+i*t+e})).attr("font-size",n.sectionFontSize).attr("font-size",n.sectionFontSize).attr("class",(function(t){for(var e=0;e<h.length;e++)if(t[0]===h[e])return"sectionTitle sectionTitle"+e%n.numberSectionStyles;return"sectionTitle"}))}(o,u),function(t,e,r,i){var a=bw.getTodayMarker();if("off"!==a){var o=s.append("g").attr("class","today"),u=new Date,l=o.append("line");l.attr("x1",c(u)+t).attr("x2",c(u)+t).attr("y1",n.titleTopMargin).attr("y2",i-n.titleTopMargin).attr("class","today"),""!==a&&l.attr("style",a.replace(/,/g,";"))}}(l,0,0,r)}(a,xw,o),zv(s,o,xw,n.useMaxWidth),s.append("text").text(ww.parser.yy.getTitle()).attr("x",xw/2).attr("y",n.titleTopMargin).attr("class","titleText")};var Ew={},Cw=null,Sw={master:Cw},Aw="master",Mw="LR",Nw=0;function Dw(){return Bv({length:7})}function Ow(t,e){o.debug("Entering isfastforwardable:",t.id,e.id);for(var n=0;t.seq<=e.seq&&t!==e&&n<1e3&&(n++,null!=e.parent);){if(Array.isArray(e.parent))return o.debug("In merge commit:",e.parent),Ow(t,Ew[e.parent[0]])||Ow(t,Ew[e.parent[1]]);e=Ew[e.parent]}return o.debug(t.id,e.id),t.id===e.id}var Bw={};function Lw(t,e,n){var r=t.indexOf(e);-1===r?t.push(n):t.splice(r,1,n)}function Iw(t){var e=t.reduce((function(t,e){return t.seq>e.seq?t:e}),t[0]),n="";t.forEach((function(t){n+=t===e?"\t*":"\t|"}));var r,i,a,s=[n,e.id,e.seq];for(var c in Sw)Sw[c]===e.id&&s.push(c);if(o.debug(s.join(" ")),Array.isArray(e.parent)){var u=Ew[e.parent[0]];Lw(t,e,u),t.push(Ew[e.parent[1]])}else{if(null==e.parent)return;var l=Ew[e.parent];Lw(t,e,l)}r=t,i=function(t){return t.id},a=Object.create(null),Iw(t=r.reduce((function(t,e){var n=i(e);return a[n]||(a[n]=!0,t.push(e)),t}),[]))}var Rw=function(){var t=Object.keys(Ew).map((function(t){return Ew[t]}));return t.forEach((function(t){o.debug(t.id)})),t.sort((function(t,e){return e.seq-t.seq})),t};const Fw={setDirection:function(t){Mw=t},setOptions:function(t){o.debug("options str",t),t=(t=t&&t.trim())||"{}";try{Bw=JSON.parse(t)}catch(t){o.error("error while parsing gitGraph options",t.message)}},getOptions:function(){return Bw},commit:function(t){var e={id:Dw(),message:t,seq:Nw++,parent:null==Cw?null:Cw.id};Cw=e,Ew[e.id]=e,Sw[Aw]=e.id,o.debug("in pushCommit "+e.id)},branch:function(t){Sw[t]=null!=Cw?Cw.id:null,o.debug("in createBranch")},merge:function(t){var e=Ew[Sw[Aw]],n=Ew[Sw[t]];if(function(t,e){return t.seq>e.seq&&Ow(e,t)}(e,n))o.debug("Already merged");else{if(Ow(e,n))Sw[Aw]=Sw[t],Cw=Ew[Sw[Aw]];else{var r={id:Dw(),message:"merged branch "+t+" into "+Aw,seq:Nw++,parent:[null==Cw?null:Cw.id,Sw[t]]};Cw=r,Ew[r.id]=r,Sw[Aw]=r.id}o.debug(Sw),o.debug("in mergeBranch")}},checkout:function(t){o.debug("in checkout");var e=Sw[Aw=t];Cw=Ew[e]},reset:function(t){o.debug("in reset",t);var e=t.split(":")[0],n=parseInt(t.split(":")[1]),r="HEAD"===e?Cw:Ew[Sw[e]];for(o.debug(r,n);n>0;)if(n--,!(r=Ew[r.parent])){var i="Critical error - unique parent commit not found during reset";throw o.error(i),i}Cw=r,Sw[Aw]=r.id},prettyPrint:function(){o.debug(Ew),Iw([Rw()[0]])},clear:function(){Ew={},Sw={master:Cw=null},Aw="master",Nw=0},getBranchesAsObjArray:function(){var t=[];for(var e in Sw)t.push({name:e,commit:Ew[Sw[e]]});return t},getBranches:function(){return Sw},getCommits:function(){return Ew},getCommitsArray:Rw,getCurrentBranch:function(){return Aw},getDirection:function(){return Mw},getHead:function(){return Cw}};var Pw,jw=n(2553),Yw=n.n(jw),zw={},Uw={nodeSpacing:150,nodeFillColor:"yellow",nodeStrokeWidth:2,nodeStrokeColor:"grey",lineStrokeWidth:4,branchOffset:50,lineColor:"grey",leftMargin:50,branchColors:["#442f74","#983351","#609732","#AA9A39"],nodeRadius:10,nodeLabel:{width:75,height:100,x:-25,y:0}},qw={};function Hw(t,e,n,r){var i=Av(r,Vu),a=Uw.branchColors[n%Uw.branchColors.length],o=zu().x((function(t){return Math.round(t.x)})).y((function(t){return Math.round(t.y)})).curve(i);t.append("svg:path").attr("d",o(e)).style("stroke",a).style("stroke-width",Uw.lineStrokeWidth).style("fill","none")}function $w(t,e){e=e||t.node().getBBox();var n=t.node().getCTM();return{left:n.e+e.x*n.a,top:n.f+e.y*n.d,width:e.width,height:e.height}}function Ww(t,e,n,r,i){o.debug("svgDrawLineForCommits: ",e,n);var a=$w(t.select("#node-"+e+" circle")),s=$w(t.select("#node-"+n+" circle"));switch(r){case"LR":if(a.left-s.left>Uw.nodeSpacing){var c={x:a.left-Uw.nodeSpacing,y:s.top+s.height/2};Hw(t,[c,{x:s.left+s.width,y:s.top+s.height/2}],i,"linear"),Hw(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:c.y},c],i)}else Hw(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-Uw.nodeSpacing/2,y:s.top+s.height/2},{x:s.left+s.width,y:s.top+s.height/2}],i);break;case"BT":if(s.top-a.top>Uw.nodeSpacing){var u={x:s.left+s.width/2,y:a.top+a.height+Uw.nodeSpacing};Hw(t,[u,{x:s.left+s.width/2,y:s.top}],i,"linear"),Hw(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+a.height+Uw.nodeSpacing/2},{x:s.left+s.width/2,y:u.y-Uw.nodeSpacing/2},u],i)}else Hw(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+Uw.nodeSpacing/2},{x:s.left+s.width/2,y:s.top-Uw.nodeSpacing/2},{x:s.left+s.width/2,y:s.top}],i)}}function Vw(t,e){return t.select(e).node().cloneNode(!0)}function Gw(t,e,n,r){var i,a=Object.keys(zw).length;if("string"==typeof e){var s=0;do{if(s++,i=zw[e],o.debug("in renderCommitHistory",i.id,i.seq),t.select("#node-"+e).size()>0)return;t.append((function(){return Vw(t,"#def-commit")})).attr("class","commit").attr("id",(function(){return"node-"+i.id})).attr("transform",(function(){switch(r){case"LR":return"translate("+(i.seq*Uw.nodeSpacing+Uw.leftMargin)+", "+Pw*Uw.branchOffset+")";case"BT":return"translate("+(Pw*Uw.branchOffset+Uw.leftMargin)+", "+(a-i.seq)*Uw.nodeSpacing+")"}})).attr("fill",Uw.nodeFillColor).attr("stroke",Uw.nodeStrokeColor).attr("stroke-width",Uw.nodeStrokeWidth);var c=void 0;for(var u in n)if(n[u].commit===i){c=n[u];break}c&&(o.debug("found branch ",c.name),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","branch-label").text(c.name+", ")),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-id").text(i.id),""!==i.message&&"BT"===r&&t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-msg").text(", "+i.message),e=i.parent}while(e&&zw[e]&&s<1e3)}Array.isArray(e)&&(o.debug("found merge commmit",e),Gw(t,e[0],n,r),Pw++,Gw(t,e[1],n,r),Pw--)}function Xw(t,e,n,r){r=r||0;for(var i=0;e.seq>0&&!e.lineDrawn&&i<1e3;)i++,"string"==typeof e.parent?(Ww(t,e.id,e.parent,n,r),e.lineDrawn=!0,e=zw[e.parent]):Array.isArray(e.parent)&&(Ww(t,e.id,e.parent[0],n,r),Ww(t,e.id,e.parent[1],n,r+1),Xw(t,zw[e.parent[1]],n,r+1),e.lineDrawn=!0,e=zw[e.parent[0]])}const Zw=function(t){qw=t};var Qw="",Kw=!1;const Jw={setMessage:function(t){o.debug("Setting message to: "+t),Qw=t},getMessage:function(){return Qw},setInfo:function(t){Kw=t},getInfo:function(){return Kw}};var tk=n(6765),ek=n.n(tk),nk={};const rk=function(t){Object.keys(t).forEach((function(e){nk[e]=t[e]}))};var ik=n(7062),ak=n.n(ik),ok={},sk="",ck=!1;const uk={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().pie},addSection:function(t,e){void 0===ok[t]&&(ok[t]=e,o.debug("Added new section :",t))},getSections:function(){return ok},cleanupValue:function(t){return":"===t.substring(0,1)?(t=t.substring(1).trim(),Number(t.trim())):Number(t.trim())},clear:function(){ok={},sk="",ck=!1},setTitle:function(t){sk=t},getTitle:function(){return sk},setShowData:function(t){ck=t},getShowData:function(){return ck}};var lk,hk=Jv();const fk=function(t,e){try{hk=Jv();var n=ak().parser;n.yy=uk,o.debug("Rendering info diagram\n"+t),n.yy.clear(),n.parse(t),o.debug("Parsed info diagram");var r=document.getElementById(e);void 0===(lk=r.parentElement.offsetWidth)&&(lk=1200),void 0!==hk.useWidth&&(lk=hk.useWidth),void 0!==hk.pie.useWidth&&(lk=hk.pie.useWidth);var i=au("#"+e);zv(i,450,lk,hk.pie.useMaxWidth),r.setAttribute("viewBox","0 0 "+lk+" 450");var a=Math.min(lk,450)/2-40,s=i.append("g").attr("transform","translate("+lk/2+",225)"),c=uk.getSections(),u=0;Object.keys(c).forEach((function(t){u+=c[t]}));var l=hk.themeVariables,h=[l.pie1,l.pie2,l.pie3,l.pie4,l.pie5,l.pie6,l.pie7,l.pie8,l.pie9,l.pie10,l.pie11,l.pie12],f=ma().range(h),d=function(){var t=qu,e=Uu,n=null,r=pu(0),i=pu(Eu),a=pu(0);function o(o){var s,c,u,l,h,f=(o=Ru(o)).length,d=0,p=new Array(f),y=new Array(f),g=+r.apply(this,arguments),m=Math.min(Eu,Math.max(-Eu,i.apply(this,arguments)-g)),v=Math.min(Math.abs(m)/f,a.apply(this,arguments)),b=v*(m<0?-1:1);for(s=0;s<f;++s)(h=y[p[s]=s]=+t(o[s],s,o))>0&&(d+=h);for(null!=e?p.sort((function(t,n){return e(y[t],y[n])})):null!=n&&p.sort((function(t,e){return n(o[t],o[e])})),s=0,u=d?(m-f*b)/d:0;s<f;++s,g=l)c=p[s],l=g+((h=y[c])>0?h*u:0)+b,y[c]={data:o[c],index:s,value:h,startAngle:g,endAngle:l,padAngle:v};return y}return o.value=function(e){return arguments.length?(t="function"==typeof e?e:pu(+e),o):t},o.sortValues=function(t){return arguments.length?(e=t,n=null,o):e},o.sort=function(t){return arguments.length?(n=t,e=null,o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:pu(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:pu(+t),o):i},o.padAngle=function(t){return arguments.length?(a="function"==typeof t?t:pu(+t),o):a},o}().value((function(t){return t[1]})),p=d(Object.entries(c)),y=Iu().innerRadius(0).outerRadius(a);s.selectAll("mySlices").data(p).enter().append("path").attr("d",y).attr("fill",(function(t){return f(t.data[0])})).attr("class","pieCircle"),s.selectAll("mySlices").data(p).enter().append("text").text((function(t){return(t.data[1]/u*100).toFixed(0)+"%"})).attr("transform",(function(t){return"translate("+y.centroid(t)+")"})).style("text-anchor","middle").attr("class","slice"),s.append("text").text(n.yy.getTitle()).attr("x",0).attr("y",-200).attr("class","pieTitleText");var g=s.selectAll(".legend").data(f.domain()).enter().append("g").attr("class","legend").attr("transform",(function(t,e){return"translate(216,"+(22*e-22*f.domain().length/2)+")"}));g.append("rect").attr("width",18).attr("height",18).style("fill",f).style("stroke",f),g.data(p).append("text").attr("x",22).attr("y",14).text((function(t){return n.yy.getShowData()||hk.showData||hk.pie.showData?t.data[0]+" ["+t.data[1]+"]":t.data[0]}))}catch(t){o.error("Error while rendering info diagram"),o.error(t)}};var dk=n(3176),pk=n.n(dk),yk=[],gk={},mk={},vk={},bk={};const _k={RequirementType:{REQUIREMENT:"Requirement",FUNCTIONAL_REQUIREMENT:"Functional Requirement",INTERFACE_REQUIREMENT:"Interface Requirement",PERFORMANCE_REQUIREMENT:"Performance Requirement",PHYSICAL_REQUIREMENT:"Physical Requirement",DESIGN_CONSTRAINT:"Design Constraint"},RiskLevel:{LOW_RISK:"Low",MED_RISK:"Medium",HIGH_RISK:"High"},VerifyType:{VERIFY_ANALYSIS:"Analysis",VERIFY_DEMONSTRATION:"Demonstration",VERIFY_INSPECTION:"Inspection",VERIFY_TEST:"Test"},Relationships:{CONTAINS:"contains",COPIES:"copies",DERIVES:"derives",SATISFIES:"satisfies",VERIFIES:"verifies",REFINES:"refines",TRACES:"traces"},parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().req},addRequirement:function(t,e){return void 0===mk[t]&&(mk[t]={name:t,type:e,id:gk.id,text:gk.text,risk:gk.risk,verifyMethod:gk.verifyMethod}),gk={},mk[t]},getRequirements:function(){return mk},setNewReqId:function(t){void 0!==gk&&(gk.id=t)},setNewReqText:function(t){void 0!==gk&&(gk.text=t)},setNewReqRisk:function(t){void 0!==gk&&(gk.risk=t)},setNewReqVerifyMethod:function(t){void 0!==gk&&(gk.verifyMethod=t)},addElement:function(t){return void 0===bk[t]&&(bk[t]={name:t,type:vk.type,docRef:vk.docRef},o.info("Added new requirement: ",t)),vk={},bk[t]},getElements:function(){return bk},setNewElementType:function(t){void 0!==vk&&(vk.type=t)},setNewElementDocRef:function(t){void 0!==vk&&(vk.docRef=t)},addRelationship:function(t,e,n){yk.push({type:t,src:e,dst:n})},getRelationships:function(){return yk},clear:function(){yk=[],gk={},mk={},vk={},bk={}}};var xk={CONTAINS:"contains",ARROW:"arrow"};const wk=xk;var kk={},Tk=0,Ek=function(t,e){return t.insert("rect","#"+e).attr("class","req reqBox").attr("x",0).attr("y",0).attr("width",kk.rect_min_width+"px").attr("height",kk.rect_min_height+"px")},Ck=function(t,e,n){var r=kk.rect_min_width/2,i=t.append("text").attr("class","req reqLabel reqTitle").attr("id",e).attr("x",r).attr("y",kk.rect_padding).attr("dominant-baseline","hanging"),a=0;n.forEach((function(t){0==a?i.append("tspan").attr("text-anchor","middle").attr("x",kk.rect_min_width/2).attr("dy",0).text(t):i.append("tspan").attr("text-anchor","middle").attr("x",kk.rect_min_width/2).attr("dy",.75*kk.line_height).text(t),a++}));var o=1.5*kk.rect_padding+a*kk.line_height*.75;return t.append("line").attr("class","req-title-line").attr("x1","0").attr("x2",kk.rect_min_width).attr("y1",o).attr("y2",o),{titleNode:i,y:o}},Sk=function(t,e,n,r){var i=t.append("text").attr("class","req reqLabel").attr("id",e).attr("x",kk.rect_padding).attr("y",r).attr("dominant-baseline","hanging"),a=0,o=[];return n.forEach((function(t){for(var e=t.length;e>30&&a<3;){var n=t.substring(0,30);e=(t=t.substring(30,t.length)).length,o[o.length]=n,a++}if(3==a){var r=o[o.length-1];o[o.length-1]=r.substring(0,r.length-4)+"..."}else o[o.length]=t;a=0})),o.forEach((function(t){i.append("tspan").attr("x",kk.rect_padding).attr("dy",kk.line_height).text(t)})),i},Ak=function(t){return t.replace(/\s/g,"").replace(/\./g,"_")};const Mk=function(t){if(void 0!==t)for(var e=Object.keys(t),n=0;n<e.length;n++)kk[e[n]]=t[e[n]]},Nk=function(t,e){dk.parser.yy=_k,dk.parser.yy.clear(),dk.parser.parse(t);var n=au("[id='".concat(e,"']"));!function(t,e){var n=t.append("defs").append("marker").attr("id",xk.CONTAINS+"_line_ending").attr("refX",0).attr("refY",e.line_height/2).attr("markerWidth",e.line_height).attr("markerHeight",e.line_height).attr("orient","auto").append("g");n.append("circle").attr("cx",e.line_height/2).attr("cy",e.line_height/2).attr("r",e.line_height/2).attr("fill","none"),n.append("line").attr("x1",0).attr("x2",e.line_height).attr("y1",e.line_height/2).attr("y2",e.line_height/2).attr("stroke-width",1),n.append("line").attr("y1",0).attr("y2",e.line_height).attr("x1",e.line_height/2).attr("x2",e.line_height/2).attr("stroke-width",1),t.append("defs").append("marker").attr("id",xk.ARROW+"_line_ending").attr("refX",e.line_height).attr("refY",.5*e.line_height).attr("markerWidth",e.line_height).attr("markerHeight",e.line_height).attr("orient","auto").append("path").attr("d","M0,0\n      L".concat(e.line_height,",").concat(e.line_height/2,"\n      M").concat(e.line_height,",").concat(e.line_height/2,"\n      L0,").concat(e.line_height)).attr("stroke-width",1)}(n,kk);var r=new(kb().Graph)({multigraph:!1,compound:!1,directed:!0}).setGraph({rankdir:kk.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel((function(){return{}})),i=_k.getRequirements(),a=_k.getElements(),s=_k.getRelationships();!function(t,e,n){Object.keys(t).forEach((function(r){var i=t[r];r=Ak(r),o.info("Added new requirement: ",r);var a=n.append("g").attr("id",r),s=Ek(a,"req-"+r),c=[],u=Ck(a,r+"_title",["<<".concat(i.type,">>"),"".concat(i.name)]);c.push(u.titleNode);var l=Sk(a,r+"_body",["Id: ".concat(i.id),"Text: ".concat(i.text),"Risk: ".concat(i.risk),"Verification: ".concat(i.verifyMethod)],u.y);c.push(l);var h=s.node().getBBox();e.setNode(r,{width:h.width,height:h.height,shape:"rect",id:r})}))}(i,r,n),function(t,e,n){Object.keys(t).forEach((function(r){var i=t[r],a=Ak(r),o=n.append("g").attr("id",a),s="element-"+a,c=Ek(o,s),u=[],l=Ck(o,s+"_title",["<<Element>>","".concat(r)]);u.push(l.titleNode);var h=Sk(o,s+"_body",["Type: ".concat(i.type||"Not Specified"),"Doc Ref: ".concat(i.docRef||"None")],l.y);u.push(h);var f=c.node().getBBox();e.setNode(a,{width:f.width,height:f.height,shape:"rect",id:a})}))}(a,r,n),function(t,e){t.forEach((function(t){var n=Ak(t.src),r=Ak(t.dst);e.setEdge(n,r,{relationship:t})}))}(s,r),xb().layout(r),function(t,e){e.nodes().forEach((function(n){void 0!==n&&void 0!==e.node(n)&&(t.select("#"+n),t.select("#"+n).attr("transform","translate("+(e.node(n).x-e.node(n).width/2)+","+(e.node(n).y-e.node(n).height/2)+" )"))}))}(n,r),s.forEach((function(t){!function(t,e,n,r){var i=n.edge(Ak(e.src),Ak(e.dst)),a=zu().x((function(t){return t.x})).y((function(t){return t.y})),o=t.insert("path","#"+r).attr("class","er relationshipLine").attr("d",a(i.points)).attr("fill","none");e.type==_k.Relationships.CONTAINS?o.attr("marker-start","url("+Um.getUrl(kk.arrowMarkerAbsolute)+"#"+e.type+"_line_ending)"):(o.attr("stroke-dasharray","10,7"),o.attr("marker-end","url("+Um.getUrl(kk.arrowMarkerAbsolute)+"#"+wk.ARROW+"_line_ending)")),function(t,e,n,r){var i=e.node().getTotalLength(),a=e.node().getPointAtLength(.5*i),o="rel"+Tk;Tk++;var s=t.append("text").attr("class","req relationshipLabel").attr("id",o).attr("x",a.x).attr("y",a.y).attr("text-anchor","middle").attr("dominant-baseline","middle").text(r).node().getBBox();t.insert("rect","#"+o).attr("class","req reqLabelBox").attr("x",a.x-s.width/2).attr("y",a.y-s.height/2).attr("width",s.width).attr("height",s.height).attr("fill","white").attr("fill-opacity","85%")}(t,o,0,"<<".concat(e.type,">>"))}(n,t,r,e)}));var c=kk.rect_padding,u=n.node().getBBox(),l=u.width+2*c,h=u.height+2*c;zv(n,h,l,kk.useMaxWidth),n.attr("viewBox","".concat(u.x-c," ").concat(u.y-c," ").concat(l," ").concat(h))};var Dk=n(6876),Ok=n.n(Dk),Bk=void 0,Lk={},Ik=[],Rk=[],Fk="",Pk=!1,jk=!1,Yk=!1,zk=function(t,e,n,r){var i=Lk[t];i&&e===i.name&&null==n||(null!=n&&null!=n.text||(n={text:e,wrap:null,type:r}),null!=r&&null!=n.text||(n={text:e,wrap:null,type:r}),Lk[t]={name:e,description:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,prevActor:Bk,links:{},properties:{},actorCnt:null,rectData:null,type:r||"participant"},Bk&&Lk[Bk]&&(Lk[Bk].nextActor=t),Bk=t)},Uk=function(t){var e,n=0;for(e=0;e<Ik.length;e++)Ik[e].type===Wk.ACTIVE_START&&Ik[e].from.actor===t&&n++,Ik[e].type===Wk.ACTIVE_END&&Ik[e].from.actor===t&&n--;return n},qk=function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{text:void 0,wrap:void 0},r=arguments.length>3?arguments[3]:void 0;if(r===Wk.ACTIVE_END){var i=Uk(t.actor);if(i<1){var a=new Error("Trying to inactivate an inactive participant ("+t.actor+")");throw a.hash={text:"->>-",token:"->>-",line:"1",loc:{first_line:1,last_line:1,first_column:1,last_column:1},expected:["'ACTIVE_PARTICIPANT'"]},a}}return Ik.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,type:r}),!0},Hk=function(t){return Lk[t]},$k=function(){return Yk},Wk={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25},Vk=function(t,e,n){var r={actor:t,placement:e,message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap},i=[].concat(t,t);Rk.push(r),Ik.push({from:i[0],to:i[1],message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,type:Wk.NOTE,placement:e})},Gk=function(t,e){var n=Hk(t);try{var r=Fm(e.text,Jv());r=(r=r.replace(/&amp;/g,"&")).replace(/&equals;/g,"="),Xk(n,JSON.parse(r))}catch(t){o.error("error while parsing actor link text",t)}};function Xk(t,e){if(null==t.links)t.links=e;else for(var n in e)t.links[n]=e[n]}var Zk=function(t,e){var n=Hk(t);try{var r=Fm(e.text,Jv());Qk(n,JSON.parse(r))}catch(t){o.error("error while parsing actor properties text",t)}};function Qk(t,e){if(null==t.properties)t.properties=e;else for(var n in e)t.properties[n]=e[n]}var Kk=function(t,e){var n=Hk(t),r=document.getElementById(e.text);try{var i=r.innerHTML,a=JSON.parse(i);a.properties&&Qk(n,a.properties),a.links&&Xk(n,a.links)}catch(t){o.error("error while parsing actor details text",t)}},Jk=function(t){Fk=t.text,Pk=void 0===t.wrap&&$k()||!!t.wrap};const tT={addActor:zk,addMessage:function(t,e,n,r){Ik.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&$k()||!!n.wrap,answer:r})},addSignal:qk,addLinks:Gk,addDetails:Kk,addProperties:Zk,autoWrap:$k,setWrap:function(t){Yk=t},enableSequenceNumbers:function(){jk=!0},showSequenceNumbers:function(){return jk},getMessages:function(){return Ik},getActors:function(){return Lk},getActor:Hk,getActorKeys:function(){return Object.keys(Lk)},getActorProperty:function(t,e){if(void 0!==t&&void 0!==t.properties)return t.properties[e]},getTitle:function(){return Fk},parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().sequence},getTitleWrapped:function(){return Pk},clear:function(){Lk={},Ik=[]},parseMessage:function(t){var e=t.trim(),n={text:e.replace(/^[:]?(?:no)?wrap:/,"").trim(),wrap:null!==e.match(/^[:]?wrap:/)||null===e.match(/^[:]?nowrap:/)&&void 0};return o.debug("parseMessage:",n),n},LINETYPE:Wk,ARROWTYPE:{FILLED:0,OPEN:1},PLACEMENT:{LEFTOF:0,RIGHTOF:1,OVER:2},addNote:Vk,setTitle:Jk,apply:function t(e){if(e instanceof Array)e.forEach((function(e){t(e)}));else switch(e.type){case"addParticipant":zk(e.actor,e.actor,e.description,"participant");break;case"addActor":zk(e.actor,e.actor,e.description,"actor");break;case"activeStart":case"activeEnd":qk(e.actor,void 0,void 0,e.signalType);break;case"addNote":Vk(e.actor,e.placement,e.text);break;case"addLinks":Gk(e.actor,e.text);break;case"addALink":!function(t,e){var n=Hk(t);try{var r={},i=Fm(e.text,Jv()),a=i.indexOf("@"),s=(i=(i=i.replace(/&amp;/g,"&")).replace(/&equals;/g,"=")).slice(0,a-1).trim(),c=i.slice(a+1).trim();r[s]=c,Xk(n,r)}catch(t){o.error("error while parsing actor link text",t)}}(e.actor,e.text);break;case"addProperties":Zk(e.actor,e.text);break;case"addDetails":Kk(e.actor,e.text);break;case"addMessage":qk(e.from,e.to,e.msg,e.signalType);break;case"loopStart":qk(void 0,void 0,e.loopText,e.signalType);break;case"loopEnd":case"rectEnd":case"optEnd":case"altEnd":case"parEnd":qk(void 0,void 0,void 0,e.signalType);break;case"rectStart":qk(void 0,void 0,e.color,e.signalType);break;case"optStart":qk(void 0,void 0,e.optText,e.signalType);break;case"altStart":case"else":qk(void 0,void 0,e.altText,e.signalType);break;case"setTitle":Jk(e.text);break;case"parStart":case"and":qk(void 0,void 0,e.parText,e.signalType)}}};var eT=[],nT=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},rT=function(t,e){var n;n=function(){var n=document.querySelectorAll(t);n[0].addEventListener("mouseover",(function(){oT("actor"+e+"_popup")})),n[0].addEventListener("mouseout",(function(){sT("actor"+e+"_popup")}))},eT.push(n)},iT=function(t,e,n,r){var i=t.append("image");i.attr("x",e),i.attr("y",n);var a=(0,Om.sanitizeUrl)(r);i.attr("xlink:href",a)},aT=function(t,e,n,r){var i=t.append("use");i.attr("x",e),i.attr("y",n);var a=(0,Om.sanitizeUrl)(r);i.attr("xlink:href","#"+a)},oT=function(t){var e=document.getElementById(t);null!=e&&(e.style.display="block")},sT=function(t){var e=document.getElementById(t);null!=e&&(e.style.display="none")},cT=function(t,e){var n=0,r=0,i=e.text.split(Um.lineBreakRegex),a=[],o=0,s=function(){return e.y};if(void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0)switch(e.valign){case"top":case"start":s=function(){return Math.round(e.y+e.textMargin)};break;case"middle":case"center":s=function(){return Math.round(e.y+(n+r+e.textMargin)/2)};break;case"bottom":case"end":s=function(){return Math.round(e.y+(n+r+2*e.textMargin)-e.textMargin)}}if(void 0!==e.anchor&&void 0!==e.textMargin&&void 0!==e.width)switch(e.anchor){case"left":case"start":e.x=Math.round(e.x+e.textMargin),e.anchor="start",e.dominantBaseline="text-after-edge",e.alignmentBaseline="middle";break;case"middle":case"center":e.x=Math.round(e.x+e.width/2),e.anchor="middle",e.dominantBaseline="middle",e.alignmentBaseline="middle";break;case"right":case"end":e.x=Math.round(e.x+e.width-e.textMargin),e.anchor="end",e.dominantBaseline="text-before-edge",e.alignmentBaseline="middle"}for(var c=0;c<i.length;c++){var u=i[c];void 0!==e.textMargin&&0===e.textMargin&&void 0!==e.fontSize&&(o=c*e.fontSize);var l=t.append("text");if(l.attr("x",e.x),l.attr("y",s()),void 0!==e.anchor&&l.attr("text-anchor",e.anchor).attr("dominant-baseline",e.dominantBaseline).attr("alignment-baseline",e.alignmentBaseline),void 0!==e.fontFamily&&l.style("font-family",e.fontFamily),void 0!==e.fontSize&&l.style("font-size",e.fontSize),void 0!==e.fontWeight&&l.style("font-weight",e.fontWeight),void 0!==e.fill&&l.attr("fill",e.fill),void 0!==e.class&&l.attr("class",e.class),void 0!==e.dy?l.attr("dy",e.dy):0!==o&&l.attr("dy",o),e.tspan){var h=l.append("tspan");h.attr("x",e.x),void 0!==e.fill&&h.attr("fill",e.fill),h.text(u)}else l.text(u);void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0&&(r+=(l._groups||l)[0][0].getBBox().height,n=r),a.push(l)}return a},uT=function(t,e){var n=t.append("polygon");return n.attr("points",function(t,e,n,r,i){return t+","+e+" "+(t+n)+","+e+" "+(t+n)+","+(e+r-7)+" "+(t+n-8.4)+","+(e+r)+" "+t+","+(e+r)}(e.x,e.y,e.width,e.height)),n.attr("class","labelBox"),e.y=e.y+e.height/2,cT(t,e),n},lT=-1,hT=function(t,e){t.selectAll&&t.selectAll(".actor-line").attr("class","200").attr("y2",e-55)},fT=function(){return{x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}},dT=function(){return{x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0}},pT=function(){function t(t,e,n,i,a,o,s){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c){for(var u=c.actorFontSize,l=c.actorFontFamily,h=c.actorFontWeight,f=t.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=d*u-u*(f.length-1)/2,y=e.append("text").attr("x",n+a/2).attr("y",i).style("text-anchor","middle").style("font-size",u).style("font-weight",h).style("font-family",l);y.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),y.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(y,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).append("xhtml:div").style("display","table").style("height","100%").style("width","100%");h.append("div").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)e.hasOwnProperty(n)&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}(),yT=function(){function t(t,e,n,i,a,o,s){r(e.append("text").attr("x",n).attr("y",i).style("text-anchor","start").text(t),s)}function e(t,e,n,i,a,o,s,c){for(var u=c.actorFontSize,l=c.actorFontFamily,h=c.actorFontWeight,f=t.split(Um.lineBreakRegex),d=0;d<f.length;d++){var p=d*u-u*(f.length-1)/2,y=e.append("text").attr("x",n).attr("y",i).style("text-anchor","start").style("font-size",u).style("font-weight",h).style("font-family",l);y.append("tspan").attr("x",n).attr("dy",p).text(f[d]),y.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(y,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).append("xhtml:div").style("display","table").style("height","100%").style("width","100%");h.append("div").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,0,s,c,u),r(h,c)}function r(t,e){for(var n in e)e.hasOwnProperty(n)&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}();const gT=nT,mT=function(t,e,n){switch(e.type){case"actor":return function(t,e,n){var r=e.x+e.width/2;0===e.y&&(lT++,t.append("line").attr("id","actor"+lT).attr("x1",r).attr("y1",80).attr("x2",r).attr("y2",2e3).attr("class","actor-line").attr("stroke-width","0.5px").attr("stroke","#999"));var i=t.append("g");i.attr("class","actor-man");var a={x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0};a.x=e.x,a.y=e.y,a.fill="#eaeaea",a.width=e.width,a.height=e.height,a.class="actor",a.rx=3,a.ry=3,i.append("line").attr("id","actor-man-torso"+lT).attr("x1",r).attr("y1",e.y+25).attr("x2",r).attr("y2",e.y+45),i.append("line").attr("id","actor-man-arms"+lT).attr("x1",r-18).attr("y1",e.y+33).attr("x2",r+18).attr("y2",e.y+33),i.append("line").attr("x1",r-18).attr("y1",e.y+60).attr("x2",r).attr("y2",e.y+45),i.append("line").attr("x1",r).attr("y1",e.y+45).attr("x2",r+16).attr("y2",e.y+60);var o=i.append("circle");o.attr("cx",e.x+e.width/2),o.attr("cy",e.y+10),o.attr("r",15),o.attr("width",e.width),o.attr("height",e.height);var s=i.node().getBBox();return e.height=s.height,pT(n)(e.description,i,a.x,a.y+35,a.width,a.height,{class:"actor"},n),e.height}(t,e,n);case"participant":return function(t,e,n){var r=e.x+e.width/2,i=t.append("g"),a=i;0===e.y&&(lT++,a.append("line").attr("id","actor"+lT).attr("x1",r).attr("y1",5).attr("x2",r).attr("y2",2e3).attr("class","actor-line").attr("stroke-width","0.5px").attr("stroke","#999"),a=i.append("g"),e.actorCnt=lT,null!=e.links&&(a.attr("id","root-"+lT),rT("#root-"+lT,lT)));var o={x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0},s="actor";null!=e.properties&&e.properties.class?s=e.properties.class:o.fill="#eaeaea",o.x=e.x,o.y=e.y,o.width=e.width,o.height=e.height,o.class=s,o.rx=3,o.ry=3;var c=nT(a,o);if(e.rectData=o,null!=e.properties&&e.properties.icon){var u=e.properties.icon.trim();"@"===u.charAt(0)?aT(a,o.x+o.width-20,o.y+10,u.substr(1)):iT(a,o.x+o.width-20,o.y+10,u)}pT(n)(e.description,a,o.x,o.y,o.width,o.height,{class:"actor"},n);var l=e.height;if(c.node){var h=c.node().getBBox();e.height=h.height,l=h.height}return l}(t,e,n)}},vT=function(t,e,n,r,i){if(void 0===e.links||null===e.links||0===Object.keys(e.links).length)return{height:0,width:0};var a=e.links,o=e.actorCnt,s=e.rectData,c="none";i&&(c="block !important");var u=t.append("g");u.attr("id","actor"+o+"_popup"),u.attr("class","actorPopupMenu"),u.attr("display",c),rT("#actor"+o+"_popup",o);var l="";void 0!==s.class&&(l=" "+s.class);var h=s.width>n?s.width:n,f=u.append("rect");if(f.attr("class","actorPopupMenuPanel"+l),f.attr("x",s.x),f.attr("y",s.height),f.attr("fill",s.fill),f.attr("stroke",s.stroke),f.attr("width",h),f.attr("height",s.height),f.attr("rx",s.rx),f.attr("ry",s.ry),null!=a){var d=20;for(var p in a){var y=u.append("a"),g=(0,Om.sanitizeUrl)(a[p]);y.attr("xlink:href",g),y.attr("target","_blank"),yT(r)(p,y,s.x+10,s.height+d,h,20,{class:"actor"},r),d+=30}}return f.attr("height",d),{height:s.height+d,width:h}},bT=function(t){return t.append("g")},_T=function(t,e,n,r,i){var a={x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0},o=e.anchored;a.x=e.startx,a.y=e.starty,a.class="activation"+i%3,a.width=e.stopx-e.startx,a.height=n-e.starty,nT(o,a)},xT=function(t,e,n,r){var i=r.boxMargin,a=r.boxTextMargin,o=r.labelBoxHeight,s=r.labelBoxWidth,c=r.messageFontFamily,u=r.messageFontSize,l=r.messageFontWeight,h=t.append("g"),f=function(t,e,n,r){return h.append("line").attr("x1",t).attr("y1",e).attr("x2",n).attr("y2",r).attr("class","loopLine")};f(e.startx,e.starty,e.stopx,e.starty),f(e.stopx,e.starty,e.stopx,e.stopy),f(e.startx,e.stopy,e.stopx,e.stopy),f(e.startx,e.starty,e.startx,e.stopy),void 0!==e.sections&&e.sections.forEach((function(t){f(e.startx,t.y,e.stopx,t.y).style("stroke-dasharray","3, 3")}));var d={x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0};d.text=n,d.x=e.startx,d.y=e.starty,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.anchor="middle",d.valign="middle",d.tspan=!1,d.width=s||50,d.height=o||20,d.textMargin=a,d.class="labelText",uT(h,d),(d={x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}).text=e.title,d.x=e.startx+s/2+(e.stopx-e.startx)/2,d.y=e.starty+i+a,d.anchor="middle",d.valign="middle",d.textMargin=a,d.class="loopText",d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=!0;var p=cT(h,d);return void 0!==e.sectionTitles&&e.sectionTitles.forEach((function(t,n){if(t.message){d.text=t.message,d.x=e.startx+(e.stopx-e.startx)/2,d.y=e.sections[n].y+i+a,d.class="loopText",d.anchor="middle",d.valign="middle",d.tspan=!1,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=e.wrap,p=cT(h,d);var r=Math.round(p.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));e.sections[n].height+=r-(i+a)}})),e.height=Math.round(e.stopy-e.starty),h},wT=function(t,e){nT(t,{x:e.startx,y:e.starty,width:e.stopx-e.startx,height:e.stopy-e.starty,fill:e.fill,class:"rect"}).lower()},kT=function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z")},TT=function(t){t.append("defs").append("marker").attr("id","filled-head").attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},ET=function(t){t.append("defs").append("marker").attr("id","sequencenumber").attr("refX",15).attr("refY",15).attr("markerWidth",60).attr("markerHeight",40).attr("orient","auto").append("circle").attr("cx",15).attr("cy",15).attr("r",6)},CT=function(t){var e=t.append("defs").append("marker").attr("id","crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",16).attr("refY",4);e.append("path").attr("fill","black").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 9,2 V 6 L16,4 Z"),e.append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 0,1 L 6,7 M 6,1 L 0,7")},ST=function(t){t.append("defs").append("symbol").attr("id","database").attr("fill-rule","evenodd").attr("clip-rule","evenodd").append("path").attr("transform","scale(.5)").attr("d","M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z")},AT=function(t){t.append("defs").append("symbol").attr("id","computer").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z")},MT=function(t){t.append("defs").append("symbol").attr("id","clock").attr("width","24").attr("height","24").append("path").attr("transform","scale(.5)").attr("d","M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z")},NT=fT,DT=dT;Om.sanitizeUrl;Dk.parser.yy=tT;var OT={},BT={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],activations:[],models:{getHeight:function(){return Math.max.apply(null,0===this.actors.length?[0]:this.actors.map((function(t){return t.height||0})))+(0===this.loops.length?0:this.loops.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.messages.length?0:this.messages.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.notes.length?0:this.notes.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))},clear:function(){this.actors=[],this.loops=[],this.messages=[],this.notes=[]},addActor:function(t){this.actors.push(t)},addLoop:function(t){this.loops.push(t)},addMessage:function(t){this.messages.push(t)},addNote:function(t){this.notes.push(t)},lastActor:function(){return this.actors[this.actors.length-1]},lastLoop:function(){return this.loops[this.loops.length-1]},lastMessage:function(){return this.messages[this.messages.length-1]},lastNote:function(){return this.notes[this.notes.length-1]},actors:[],loops:[],messages:[],notes:[]},init:function(){this.sequenceItems=[],this.activations=[],this.models.clear(),this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0,jT(Dk.parser.yy.getConfig())},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i=this,a=0;function o(o){return function(s){a++;var c=i.sequenceItems.length-a+1;i.updateVal(s,"starty",e-c*OT.boxMargin,Math.min),i.updateVal(s,"stopy",r+c*OT.boxMargin,Math.max),i.updateVal(BT.data,"startx",t-c*OT.boxMargin,Math.min),i.updateVal(BT.data,"stopx",n+c*OT.boxMargin,Math.max),"activation"!==o&&(i.updateVal(s,"startx",t-c*OT.boxMargin,Math.min),i.updateVal(s,"stopx",n+c*OT.boxMargin,Math.max),i.updateVal(BT.data,"starty",e-c*OT.boxMargin,Math.min),i.updateVal(BT.data,"stopy",r+c*OT.boxMargin,Math.max))}}this.sequenceItems.forEach(o()),this.activations.forEach(o("activation"))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(BT.data,"startx",i,Math.min),this.updateVal(BT.data,"starty",o,Math.min),this.updateVal(BT.data,"stopx",a,Math.max),this.updateVal(BT.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},newActivation:function(t,e,n){var r=n[t.from.actor],i=YT(t.from.actor).length||0,a=r.x+r.width/2+(i-1)*OT.activationWidth/2;this.activations.push({startx:a,starty:this.verticalPos+2,stopx:a+OT.activationWidth,stopy:void 0,actor:t.from.actor,anchored:bT(e)})},endActivation:function(t){var e=this.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);return this.activations.splice(e,1)[0]},createLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;return{startx:void 0,starty:this.verticalPos,stopx:void 0,stopy:void 0,title:t.message,wrap:t.wrap,width:t.width,height:0,fill:e}},newLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;this.sequenceItems.push(this.createLoop(t,e))},endLoop:function(){return this.sequenceItems.pop()},addSectionToLoop:function(t){var e=this.sequenceItems.pop();e.sections=e.sections||[],e.sectionTitles=e.sectionTitles||[],e.sections.push({y:BT.getVerticalPos(),height:0}),e.sectionTitles.push(t),this.sequenceItems.push(e)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return{bounds:this.data,models:this.models}}},LT=function(t){return{fontFamily:t.messageFontFamily,fontSize:t.messageFontSize,fontWeight:t.messageFontWeight}},IT=function(t){return{fontFamily:t.noteFontFamily,fontSize:t.noteFontSize,fontWeight:t.noteFontWeight}},RT=function(t){return{fontFamily:t.actorFontFamily,fontSize:t.actorFontSize,fontWeight:t.actorFontWeight}},FT=function(t,e,n,r){for(var i=0,a=0,o=0,s=0;s<n.length;s++){var c=e[n[s]];c.width=c.width||OT.width,c.height=Math.max(c.height||OT.height,OT.height),c.margin=c.margin||OT.actorMargin,c.x=i+a,c.y=r;var u=mT(t,c,OT);o=Math.max(o,u),BT.insert(c.x,r,c.x+c.width,c.height),i+=c.width,a+=c.margin,BT.models.addActor(c)}BT.bumpVerticalPos(o)},PT=function(t,e,n){for(var r=0,i=0,a=0;a<n.length;a++){var o=e[n[a]],s=qT(o),c=vT(t,o,s,OT,OT.forceMenus);c.height>r&&(r=c.height),c.width+o.x>i&&(i=c.width+o.x)}return{maxHeight:r,maxWidth:i}},jT=function(t){Lv(OT,t),t.fontFamily&&(OT.actorFontFamily=OT.noteFontFamily=OT.messageFontFamily=t.fontFamily),t.fontSize&&(OT.actorFontSize=OT.noteFontSize=OT.messageFontSize=t.fontSize),t.fontWeight&&(OT.actorFontWeight=OT.noteFontWeight=OT.messageFontWeight=t.fontWeight)},YT=function(t){return BT.activations.filter((function(e){return e.actor===t}))},zT=function(t,e){var n=e[t],r=YT(t);return[r.reduce((function(t,e){return Math.min(t,e.startx)}),n.x+n.width/2),r.reduce((function(t,e){return Math.max(t,e.stopx)}),n.x+n.width/2)]};function UT(t,e,n,r,i){BT.bumpVerticalPos(n);var a=r;if(e.id&&e.message&&t[e.id]){var s=t[e.id].width,c=LT(OT);e.message=Hv.wrapLabel("[".concat(e.message,"]"),s-2*OT.wrapPadding,c),e.width=s,e.wrap=!0;var u=Hv.calculateTextDimensions(e.message,c),l=Math.max(u.height,OT.labelBoxHeight);a=r+l,o.debug("".concat(l," - ").concat(e.message))}i(e),BT.bumpVerticalPos(a)}var qT=function(t){var e=0,n=RT(OT);for(var r in t.links){var i=Hv.calculateTextDimensions(r,n).width+2*OT.wrapPadding+2*OT.boxMargin;e<i&&(e=i)}return e};const HT={bounds:BT,drawActors:FT,drawActorsPopup:PT,setConf:jT,draw:function(t,e){OT=Jv().sequence,Dk.parser.yy.clear(),Dk.parser.yy.setWrap(OT.wrap),Dk.parser.parse(t+"\n"),BT.init(),o.debug("C:".concat(JSON.stringify(OT,null,2)));var n=au('[id="'.concat(e,'"]')),r=Dk.parser.yy.getActors(),i=Dk.parser.yy.getActorKeys(),a=Dk.parser.yy.getMessages(),s=Dk.parser.yy.getTitle(),c=function(t,e){var n={};return e.forEach((function(e){if(t[e.to]&&t[e.from]){var r=t[e.to];if(e.placement===Dk.parser.yy.PLACEMENT.LEFTOF&&!r.prevActor)return;if(e.placement===Dk.parser.yy.PLACEMENT.RIGHTOF&&!r.nextActor)return;var i=void 0!==e.placement,a=!i,o=i?IT(OT):LT(OT),s=e.wrap?Hv.wrapLabel(e.message,OT.width-2*OT.wrapPadding,o):e.message,c=Hv.calculateTextDimensions(s,o).width+2*OT.wrapPadding;a&&e.from===r.nextActor?n[e.to]=Math.max(n[e.to]||0,c):a&&e.from===r.prevActor?n[e.from]=Math.max(n[e.from]||0,c):a&&e.from===e.to?(n[e.from]=Math.max(n[e.from]||0,c/2),n[e.to]=Math.max(n[e.to]||0,c/2)):e.placement===Dk.parser.yy.PLACEMENT.RIGHTOF?n[e.from]=Math.max(n[e.from]||0,c):e.placement===Dk.parser.yy.PLACEMENT.LEFTOF?n[r.prevActor]=Math.max(n[r.prevActor]||0,c):e.placement===Dk.parser.yy.PLACEMENT.OVER&&(r.prevActor&&(n[r.prevActor]=Math.max(n[r.prevActor]||0,c/2)),r.nextActor&&(n[e.from]=Math.max(n[e.from]||0,c/2)))}})),o.debug("maxMessageWidthPerActor:",n),n}(r,a);OT.height=function(t,e){var n=0;for(var r in Object.keys(t).forEach((function(e){var r=t[e];r.wrap&&(r.description=Hv.wrapLabel(r.description,OT.width-2*OT.wrapPadding,RT(OT)));var i=Hv.calculateTextDimensions(r.description,RT(OT));r.width=r.wrap?OT.width:Math.max(OT.width,i.width+2*OT.wrapPadding),r.height=r.wrap?Math.max(i.height,OT.height):OT.height,n=Math.max(n,r.height)})),e){var i=t[r];if(i){var a=t[i.nextActor];if(a){var o=e[r]+OT.actorMargin-i.width/2-a.width/2;i.margin=Math.max(o,OT.actorMargin)}}}return Math.max(n,OT.height)}(r,c),AT(n),ST(n),MT(n),FT(n,r,i,0);var u=function(t,e){var n,r,i,a={},s=[];return t.forEach((function(t){switch(t.id=Hv.random({length:10}),t.type){case Dk.parser.yy.LINETYPE.LOOP_START:case Dk.parser.yy.LINETYPE.ALT_START:case Dk.parser.yy.LINETYPE.OPT_START:case Dk.parser.yy.LINETYPE.PAR_START:s.push({id:t.id,msg:t.message,from:Number.MAX_SAFE_INTEGER,to:Number.MIN_SAFE_INTEGER,width:0});break;case Dk.parser.yy.LINETYPE.ALT_ELSE:case Dk.parser.yy.LINETYPE.PAR_AND:t.message&&(n=s.pop(),a[n.id]=n,a[t.id]=n,s.push(n));break;case Dk.parser.yy.LINETYPE.LOOP_END:case Dk.parser.yy.LINETYPE.ALT_END:case Dk.parser.yy.LINETYPE.OPT_END:case Dk.parser.yy.LINETYPE.PAR_END:n=s.pop(),a[n.id]=n;break;case Dk.parser.yy.LINETYPE.ACTIVE_START:var c=e[t.from?t.from.actor:t.to.actor],u=YT(t.from?t.from.actor:t.to.actor).length,l=c.x+c.width/2+(u-1)*OT.activationWidth/2,h={startx:l,stopx:l+OT.activationWidth,actor:t.from.actor,enabled:!0};BT.activations.push(h);break;case Dk.parser.yy.LINETYPE.ACTIVE_END:var f=BT.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);delete BT.activations.splice(f,1)[0]}void 0!==t.placement?(r=function(t,e){var n=e[t.from].x,r=e[t.to].x,i=t.wrap&&t.message,a=Hv.calculateTextDimensions(i?Hv.wrapLabel(t.message,OT.width,IT(OT)):t.message,IT(OT)),s={width:i?OT.width:Math.max(OT.width,a.width+2*OT.noteMargin),height:0,startx:e[t.from].x,stopx:0,starty:0,stopy:0,message:t.message};return t.placement===Dk.parser.yy.PLACEMENT.RIGHTOF?(s.width=i?Math.max(OT.width,a.width):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*OT.noteMargin),s.startx=n+(e[t.from].width+OT.actorMargin)/2):t.placement===Dk.parser.yy.PLACEMENT.LEFTOF?(s.width=i?Math.max(OT.width,a.width+2*OT.noteMargin):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*OT.noteMargin),s.startx=n-s.width+(e[t.from].width-OT.actorMargin)/2):t.to===t.from?(a=Hv.calculateTextDimensions(i?Hv.wrapLabel(t.message,Math.max(OT.width,e[t.from].width),IT(OT)):t.message,IT(OT)),s.width=i?Math.max(OT.width,e[t.from].width):Math.max(e[t.from].width,OT.width,a.width+2*OT.noteMargin),s.startx=n+(e[t.from].width-s.width)/2):(s.width=Math.abs(n+e[t.from].width/2-(r+e[t.to].width/2))+OT.actorMargin,s.startx=n<r?n+e[t.from].width/2-OT.actorMargin/2:r+e[t.to].width/2-OT.actorMargin/2),i&&(s.message=Hv.wrapLabel(t.message,s.width-2*OT.wrapPadding,IT(OT))),o.debug("NM:[".concat(s.startx,",").concat(s.stopx,",").concat(s.starty,",").concat(s.stopy,":").concat(s.width,",").concat(s.height,"=").concat(t.message,"]")),s}(t,e),t.noteModel=r,s.forEach((function(t){(n=t).from=Math.min(n.from,r.startx),n.to=Math.max(n.to,r.startx+r.width),n.width=Math.max(n.width,Math.abs(n.from-n.to))-OT.labelBoxWidth}))):(i=function(t,e){var n=!1;if([Dk.parser.yy.LINETYPE.SOLID_OPEN,Dk.parser.yy.LINETYPE.DOTTED_OPEN,Dk.parser.yy.LINETYPE.SOLID,Dk.parser.yy.LINETYPE.DOTTED,Dk.parser.yy.LINETYPE.SOLID_CROSS,Dk.parser.yy.LINETYPE.DOTTED_CROSS,Dk.parser.yy.LINETYPE.SOLID_POINT,Dk.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&(n=!0),!n)return{};var r=zT(t.from,e),i=zT(t.to,e),a=r[0]<=i[0]?1:0,o=r[0]<i[0]?0:1,s=r.concat(i),c=Math.abs(i[o]-r[a]);t.wrap&&t.message&&(t.message=Hv.wrapLabel(t.message,Math.max(c+2*OT.wrapPadding,OT.width),LT(OT)));var u=Hv.calculateTextDimensions(t.message,LT(OT));return{width:Math.max(t.wrap?0:u.width+2*OT.wrapPadding,c+2*OT.wrapPadding,OT.width),height:0,startx:r[a],stopx:i[o],starty:0,stopy:0,message:t.message,type:t.type,wrap:t.wrap,fromBounds:Math.min.apply(null,s),toBounds:Math.max.apply(null,s)}}(t,e),t.msgModel=i,i.startx&&i.stopx&&s.length>0&&s.forEach((function(r){if(n=r,i.startx===i.stopx){var a=e[t.from],o=e[t.to];n.from=Math.min(a.x-i.width/2,a.x-a.width/2,n.from),n.to=Math.max(o.x+i.width/2,o.x+a.width/2,n.to),n.width=Math.max(n.width,Math.abs(n.to-n.from))-OT.labelBoxWidth}else n.from=Math.min(i.startx,n.from),n.to=Math.max(i.stopx,n.to),n.width=Math.max(n.width,i.width)-OT.labelBoxWidth})))})),BT.activations=[],o.debug("Loop type widths:",a),a}(a,r);kT(n),CT(n),TT(n),ET(n);var l=1;a.forEach((function(t){var e,i,a;switch(t.type){case Dk.parser.yy.LINETYPE.NOTE:i=t.noteModel,function(t,e){BT.bumpVerticalPos(OT.boxMargin),e.height=OT.boxMargin,e.starty=BT.getVerticalPos();var n=DT();n.x=e.startx,n.y=e.starty,n.width=e.width||OT.width,n.class="note";var r=t.append("g"),i=gT(r,n),a=NT();a.x=e.startx,a.y=e.starty,a.width=n.width,a.dy="1em",a.text=e.message,a.class="noteText",a.fontFamily=OT.noteFontFamily,a.fontSize=OT.noteFontSize,a.fontWeight=OT.noteFontWeight,a.anchor=OT.noteAlign,a.textMargin=OT.noteMargin,a.valign=OT.noteAlign;var o=cT(r,a),s=Math.round(o.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));i.attr("height",s+2*OT.noteMargin),e.height+=s+2*OT.noteMargin,BT.bumpVerticalPos(s+2*OT.noteMargin),e.stopy=e.starty+s+2*OT.noteMargin,e.stopx=e.startx+n.width,BT.insert(e.startx,e.starty,e.stopx,e.stopy),BT.models.addNote(e)}(n,i);break;case Dk.parser.yy.LINETYPE.ACTIVE_START:BT.newActivation(t,n,r);break;case Dk.parser.yy.LINETYPE.ACTIVE_END:!function(t,e){var r=BT.endActivation(t);r.starty+18>e&&(r.starty=e-6,e+=12),_T(n,r,e,OT,YT(t.from.actor).length),BT.insert(r.startx,e-10,r.stopx,e)}(t,BT.getVerticalPos());break;case Dk.parser.yy.LINETYPE.LOOP_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.LOOP_END:e=BT.endLoop(),xT(n,e,"loop",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;case Dk.parser.yy.LINETYPE.RECT_START:UT(u,t,OT.boxMargin,OT.boxMargin,(function(t){return BT.newLoop(void 0,t.message)}));break;case Dk.parser.yy.LINETYPE.RECT_END:e=BT.endLoop(),wT(n,e),BT.models.addLoop(e),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos());break;case Dk.parser.yy.LINETYPE.OPT_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.OPT_END:e=BT.endLoop(),xT(n,e,"opt",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;case Dk.parser.yy.LINETYPE.ALT_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.ALT_ELSE:UT(u,t,OT.boxMargin+OT.boxTextMargin,OT.boxMargin,(function(t){return BT.addSectionToLoop(t)}));break;case Dk.parser.yy.LINETYPE.ALT_END:e=BT.endLoop(),xT(n,e,"alt",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;case Dk.parser.yy.LINETYPE.PAR_START:UT(u,t,OT.boxMargin,OT.boxMargin+OT.boxTextMargin,(function(t){return BT.newLoop(t)}));break;case Dk.parser.yy.LINETYPE.PAR_AND:UT(u,t,OT.boxMargin+OT.boxTextMargin,OT.boxMargin,(function(t){return BT.addSectionToLoop(t)}));break;case Dk.parser.yy.LINETYPE.PAR_END:e=BT.endLoop(),xT(n,e,"par",OT),BT.bumpVerticalPos(e.stopy-BT.getVerticalPos()),BT.models.addLoop(e);break;default:try{(a=t.msgModel).starty=BT.getVerticalPos(),a.sequenceIndex=l,function(t,e){BT.bumpVerticalPos(10);var n=e.startx,r=e.stopx,i=e.starty,a=e.message,o=e.type,s=e.sequenceIndex,c=Um.splitBreaks(a).length,u=Hv.calculateTextDimensions(a,LT(OT)),l=u.height/c;e.height+=l,BT.bumpVerticalPos(l);var h=NT();h.x=n,h.y=i+10,h.width=r-n,h.class="messageText",h.dy="1em",h.text=a,h.fontFamily=OT.messageFontFamily,h.fontSize=OT.messageFontSize,h.fontWeight=OT.messageFontWeight,h.anchor=OT.messageAlign,h.valign=OT.messageAlign,h.textMargin=OT.wrapPadding,h.tspan=!1,cT(t,h);var f,d,p=u.height-10,y=u.width;if(n===r){d=BT.getVerticalPos()+p,OT.rightAngles?f=t.append("path").attr("d","M  ".concat(n,",").concat(d," H ").concat(n+Math.max(OT.width/2,y/2)," V ").concat(d+25," H ").concat(n)):(p+=OT.boxMargin,d=BT.getVerticalPos()+p,f=t.append("path").attr("d","M "+n+","+d+" C "+(n+60)+","+(d-10)+" "+(n+60)+","+(d+30)+" "+n+","+(d+20))),p+=30;var g=Math.max(y/2,OT.width/2);BT.insert(n-g,BT.getVerticalPos()-10+p,r+g,BT.getVerticalPos()+30+p)}else p+=OT.boxMargin,d=BT.getVerticalPos()+p,(f=t.append("line")).attr("x1",n),f.attr("y1",d),f.attr("x2",r),f.attr("y2",d),BT.insert(n,d-10,r,d);o===Dk.parser.yy.LINETYPE.DOTTED||o===Dk.parser.yy.LINETYPE.DOTTED_CROSS||o===Dk.parser.yy.LINETYPE.DOTTED_POINT||o===Dk.parser.yy.LINETYPE.DOTTED_OPEN?(f.style("stroke-dasharray","3, 3"),f.attr("class","messageLine1")):f.attr("class","messageLine0");var m="";OT.arrowMarkerAbsolute&&(m=(m=(m=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),f.attr("stroke-width",2),f.attr("stroke","none"),f.style("fill","none"),o!==Dk.parser.yy.LINETYPE.SOLID&&o!==Dk.parser.yy.LINETYPE.DOTTED||f.attr("marker-end","url("+m+"#arrowhead)"),o!==Dk.parser.yy.LINETYPE.SOLID_POINT&&o!==Dk.parser.yy.LINETYPE.DOTTED_POINT||f.attr("marker-end","url("+m+"#filled-head)"),o!==Dk.parser.yy.LINETYPE.SOLID_CROSS&&o!==Dk.parser.yy.LINETYPE.DOTTED_CROSS||f.attr("marker-end","url("+m+"#crosshead)"),(tT.showSequenceNumbers()||OT.showSequenceNumbers)&&(f.attr("marker-start","url("+m+"#sequencenumber)"),t.append("text").attr("x",n).attr("y",d+4).attr("font-family","sans-serif").attr("font-size","12px").attr("text-anchor","middle").attr("textLength","16px").attr("class","sequenceNumber").text(s)),BT.bumpVerticalPos(p),e.height+=p,e.stopy=e.starty+e.height,BT.insert(e.fromBounds,e.starty,e.toBounds,e.stopy)}(n,a),BT.models.addMessage(a)}catch(t){o.error("error while drawing message",t)}}[Dk.parser.yy.LINETYPE.SOLID_OPEN,Dk.parser.yy.LINETYPE.DOTTED_OPEN,Dk.parser.yy.LINETYPE.SOLID,Dk.parser.yy.LINETYPE.DOTTED,Dk.parser.yy.LINETYPE.SOLID_CROSS,Dk.parser.yy.LINETYPE.DOTTED_CROSS,Dk.parser.yy.LINETYPE.SOLID_POINT,Dk.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&l++})),OT.mirrorActors&&(BT.bumpVerticalPos(2*OT.boxMargin),FT(n,r,i,BT.getVerticalPos()),BT.bumpVerticalPos(OT.boxMargin),hT(n,BT.getVerticalPos()));var h=PT(n,r,i),f=BT.getBounds().bounds;o.debug("For line height fix Querying: #"+e+" .actor-line"),ou("#"+e+" .actor-line").attr("y2",f.stopy);var d=f.stopy-f.starty;d<h.maxHeight&&(d=h.maxHeight);var p=d+2*OT.diagramMarginY;OT.mirrorActors&&(p=p-OT.boxMargin+OT.bottomMarginAdj);var y=f.stopx-f.startx;y<h.maxWidth&&(y=h.maxWidth);var g=y+2*OT.diagramMarginX;s&&n.append("text").text(s).attr("x",(f.stopx-f.startx)/2-2*OT.diagramMarginX).attr("y",-25),zv(n,p,g,OT.useMaxWidth);var m=s?40:0;n.attr("viewBox",f.startx-OT.diagramMarginX+" -"+(OT.diagramMarginY+m)+" "+g+" "+(p+m)),o.debug("models:",BT.models)}};var $T=n(3584),WT=n.n($T);function VT(t){return VT="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},VT(t)}var GT=function(t){return JSON.parse(JSON.stringify(t))},XT=[],ZT=function t(e,n,r){if("relation"===n.stmt)t(e,n.state1,!0),t(e,n.state2,!1);else if("state"===n.stmt&&"[*]"===n.id&&(n.id=r?e.id+"_start":e.id+"_end",n.start=r),n.doc){var i=[],a=0,o=[];for(a=0;a<n.doc.length;a++)if("divider"===n.doc[a].type){var s=GT(n.doc[a]);s.doc=GT(o),i.push(s),o=[]}else o.push(n.doc[a]);if(i.length>0&&o.length>0){var c={stmt:"state",id:Ov(),type:"divider",doc:GT(o)};i.push(GT(c)),n.doc=i}n.doc.forEach((function(e){return t(n,e,!0)}))}},QT={root:{relations:[],states:{},documents:{}}},KT=QT.root,JT=0,tE=function(t,e,n,r,i){void 0===KT.states[t]?KT.states[t]={id:t,descriptions:[],type:e,doc:n,note:i}:(KT.states[t].doc||(KT.states[t].doc=n),KT.states[t].type||(KT.states[t].type=e)),r&&(o.info("Adding state ",t,r),"string"==typeof r&&rE(t,r.trim()),"object"===VT(r)&&r.forEach((function(e){return rE(t,e.trim())}))),i&&(KT.states[t].note=i,KT.states[t].note.text=Um.sanitizeText(KT.states[t].note.text,Jv()))},eE=function(){KT=(QT={root:{relations:[],states:{},documents:{}}}).root,KT=QT.root,JT=0,aE=[]},nE=function(t,e,n){var r=t,i=e,a="default",o="default";"[*]"===t&&(r="start"+ ++JT,a="start"),"[*]"===e&&(i="end"+JT,o="end"),tE(r,a),tE(i,o),KT.relations.push({id1:r,id2:i,title:Um.sanitizeText(n,Jv())})},rE=function(t,e){var n=KT.states[t],r=e;":"===r[0]&&(r=r.substr(1).trim()),n.descriptions.push(Um.sanitizeText(r,Jv()))},iE=0,aE=[],oE="TB";const sE={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().state},addState:tE,clear:eE,getState:function(t){return KT.states[t]},getStates:function(){return KT.states},getRelations:function(){return KT.relations},getClasses:function(){return aE},getDirection:function(){return oE},addRelation:nE,getDividerId:function(){return"divider-id-"+ ++iE},setDirection:function(t){oE=t},cleanupLabel:function(t){return":"===t.substring(0,1)?t.substr(2).trim():t.trim()},lineType:{LINE:0,DOTTED_LINE:1},relationType:{AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},logDocuments:function(){o.info("Documents = ",QT)},getRootDoc:function(){return XT},setRootDoc:function(t){o.info("Setting root doc",t),XT=t},getRootDocV2:function(){return ZT({id:"root"},{id:"root",doc:XT},!0),{id:"root",doc:XT}},extract:function(t){var e;e=t.doc?t.doc:t,o.info(e),eE(),o.info("Extract",e),e.forEach((function(t){"state"===t.stmt&&tE(t.id,t.type,t.doc,t.description,t.note),"relation"===t.stmt&&nE(t.state1.id,t.state2.id,t.description)}))},trimColon:function(t){return t&&":"===t[0]?t.substr(1).trim():t.trim()}};var cE={};function uE(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var lE,hE=function(t,e,n){var r,i=Jv().state.padding,a=2*Jv().state.padding,o=t.node().getBBox(),s=o.width,c=o.x,u=t.append("text").attr("x",0).attr("y",Jv().state.titleShift).attr("font-size",Jv().state.fontSize).attr("class","state-title").text(e.id),l=u.node().getBBox().width+a,h=Math.max(l,s);h===s&&(h+=a);var f=t.node().getBBox();e.doc,r=c-i,l>s&&(r=(s-h)/2+i),Math.abs(c-f.x)<i&&l>s&&(r=c-(l-s)/2);var d=1-Jv().state.textHeight;return t.insert("rect",":first-child").attr("x",r).attr("y",d).attr("class",n?"alt-composit":"composit").attr("width",h).attr("height",f.height+Jv().state.textHeight+Jv().state.titleShift+1).attr("rx","0"),u.attr("x",r+i),l<=s&&u.attr("x",c+(h-a)/2-l/2+i),t.insert("rect",":first-child").attr("x",r).attr("y",Jv().state.titleShift-Jv().state.textHeight-Jv().state.padding).attr("width",h).attr("height",3*Jv().state.textHeight).attr("rx",Jv().state.radius),t.insert("rect",":first-child").attr("x",r).attr("y",Jv().state.titleShift-Jv().state.textHeight-Jv().state.padding).attr("width",h).attr("height",f.height+3+2*Jv().state.textHeight).attr("rx",Jv().state.radius),t},fE=function(t,e){e.attr("class","state-note");var n=e.append("rect").attr("x",0).attr("y",Jv().state.padding),r=function(t,e,n,r){var i=0,a=r.append("text");a.style("text-anchor","start"),a.attr("class","noteText");var o,s=t.replace(/\r\n/g,"<br/>"),c=(s=s.replace(/\n/g,"<br/>")).split(Um.lineBreakRegex),u=1.25*Jv().state.noteMargin,l=function(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!n){if(Array.isArray(t)||(n=function(t,e){if(t){if("string"==typeof t)return uE(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?uE(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var r=0,i=function(){};return{s:i,n:function(){return r>=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,s=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return o=t.done,t},e:function(t){s=!0,a=t},f:function(){try{o||null==n.return||n.return()}finally{if(s)throw a}}}}(c);try{for(l.s();!(o=l.n()).done;){var h=o.value.trim();if(h.length>0){var f=a.append("tspan");f.text(h),0===u&&(u+=f.node().getBBox().height),i+=u,f.attr("x",0+Jv().state.noteMargin),f.attr("y",0+i+1.25*Jv().state.noteMargin)}}}catch(t){l.e(t)}finally{l.f()}return{textWidth:a.node().getBBox().width,textHeight:i}}(t,0,0,e.append("g")),i=r.textWidth,a=r.textHeight;return n.attr("height",a+2*Jv().state.noteMargin),n.attr("width",i+2*Jv().state.noteMargin),n},dE=function(t,e){var n=e.id,r={id:n,label:e.id,width:0,height:0},i=t.append("g").attr("id",n).attr("class","stateGroup");"start"===e.type&&function(t){t.append("circle").attr("class","start-state").attr("r",Jv().state.sizeUnit).attr("cx",Jv().state.padding+Jv().state.sizeUnit).attr("cy",Jv().state.padding+Jv().state.sizeUnit)}(i),"end"===e.type&&function(t){t.append("circle").attr("class","end-state-outer").attr("r",Jv().state.sizeUnit+Jv().state.miniPadding).attr("cx",Jv().state.padding+Jv().state.sizeUnit+Jv().state.miniPadding).attr("cy",Jv().state.padding+Jv().state.sizeUnit+Jv().state.miniPadding),t.append("circle").attr("class","end-state-inner").attr("r",Jv().state.sizeUnit).attr("cx",Jv().state.padding+Jv().state.sizeUnit+2).attr("cy",Jv().state.padding+Jv().state.sizeUnit+2)}(i),"fork"!==e.type&&"join"!==e.type||function(t,e){var n=Jv().state.forkWidth,r=Jv().state.forkHeight;if(e.parentId){var i=n;n=r,r=i}t.append("rect").style("stroke","black").style("fill","black").attr("width",n).attr("height",r).attr("x",Jv().state.padding).attr("y",Jv().state.padding)}(i,e),"note"===e.type&&fE(e.note.text,i),"divider"===e.type&&function(t){t.append("line").style("stroke","grey").style("stroke-dasharray","3").attr("x1",Jv().state.textHeight).attr("class","divider").attr("x2",2*Jv().state.textHeight).attr("y1",0).attr("y2",0)}(i),"default"===e.type&&0===e.descriptions.length&&function(t,e){var n=t.append("text").attr("x",2*Jv().state.padding).attr("y",Jv().state.textHeight+2*Jv().state.padding).attr("font-size",Jv().state.fontSize).attr("class","state-title").text(e.id).node().getBBox();t.insert("rect",":first-child").attr("x",Jv().state.padding).attr("y",Jv().state.padding).attr("width",n.width+2*Jv().state.padding).attr("height",n.height+2*Jv().state.padding).attr("rx",Jv().state.radius)}(i,e),"default"===e.type&&e.descriptions.length>0&&function(t,e){var n=t.append("text").attr("x",2*Jv().state.padding).attr("y",Jv().state.textHeight+1.3*Jv().state.padding).attr("font-size",Jv().state.fontSize).attr("class","state-title").text(e.descriptions[0]).node().getBBox(),r=n.height,i=t.append("text").attr("x",Jv().state.padding).attr("y",r+.4*Jv().state.padding+Jv().state.dividerMargin+Jv().state.textHeight).attr("class","state-description"),a=!0,o=!0;e.descriptions.forEach((function(t){a||(function(t,e,n){var r=t.append("tspan").attr("x",2*Jv().state.padding).text(e);n||r.attr("dy",Jv().state.textHeight)}(i,t,o),o=!1),a=!1}));var s=t.append("line").attr("x1",Jv().state.padding).attr("y1",Jv().state.padding+r+Jv().state.dividerMargin/2).attr("y2",Jv().state.padding+r+Jv().state.dividerMargin/2).attr("class","descr-divider"),c=i.node().getBBox(),u=Math.max(c.width,n.width);s.attr("x2",u+3*Jv().state.padding),t.insert("rect",":first-child").attr("x",Jv().state.padding).attr("y",Jv().state.padding).attr("width",u+2*Jv().state.padding).attr("height",c.height+r+2*Jv().state.padding).attr("rx",Jv().state.radius)}(i,e);var a,o=i.node().getBBox();return r.width=o.width+2*Jv().state.padding,r.height=o.height+2*Jv().state.padding,a=r,cE[n]=a,r},pE=0;$T.parser.yy=sE;var yE={},gE=function t(e,n,r,i){var a,s=new(kb().Graph)({compound:!0,multigraph:!0}),c=!0;for(a=0;a<e.length;a++)if("relation"===e[a].stmt){c=!1;break}r?s.setGraph({rankdir:"LR",multigraph:!0,compound:!0,ranker:"tight-tree",ranksep:c?1:lE.edgeLengthFactor,nodeSep:c?1:50,isMultiGraph:!0}):s.setGraph({rankdir:"TB",multigraph:!0,compound:!0,ranksep:c?1:lE.edgeLengthFactor,nodeSep:c?1:50,ranker:"tight-tree",isMultiGraph:!0}),s.setDefaultEdgeLabel((function(){return{}})),sE.extract(e);for(var u=sE.getStates(),l=sE.getRelations(),h=Object.keys(u),f=0;f<h.length;f++){var d=u[h[f]];r&&(d.parentId=r);var p=void 0;if(d.doc){var y=n.append("g").attr("id",d.id).attr("class","stateGroup");p=t(d.doc,y,d.id,!i);var g=(y=hE(y,d,i)).node().getBBox();p.width=g.width,p.height=g.height+lE.padding/2,yE[d.id]={y:lE.compositTitleSize}}else p=dE(n,d);if(d.note){var m={descriptions:[],id:d.id+"-note",note:d.note,type:"note"},v=dE(n,m);"left of"===d.note.position?(s.setNode(p.id+"-note",v),s.setNode(p.id,p)):(s.setNode(p.id,p),s.setNode(p.id+"-note",v)),s.setParent(p.id,p.id+"-group"),s.setParent(p.id+"-note",p.id+"-group")}else s.setNode(p.id,p)}o.debug("Count=",s.nodeCount(),s);var b=0;l.forEach((function(t){var e;b++,o.debug("Setting edge",t),s.setEdge(t.id1,t.id2,{relation:t,width:(e=t.title,e?e.length*lE.fontSizeFactor:1),height:lE.labelHeight*Um.getRows(t.title).length,labelpos:"c"},"id"+b)})),xb().layout(s),o.debug("Graph after layout",s.nodes());var _=n.node();s.nodes().forEach((function(t){void 0!==t&&void 0!==s.node(t)?(o.warn("Node "+t+": "+JSON.stringify(s.node(t))),au("#"+_.id+" #"+t).attr("transform","translate("+(s.node(t).x-s.node(t).width/2)+","+(s.node(t).y+(yE[t]?yE[t].y:0)-s.node(t).height/2)+" )"),au("#"+_.id+" #"+t).attr("data-x-shift",s.node(t).x-s.node(t).width/2),document.querySelectorAll("#"+_.id+" #"+t+" .divider").forEach((function(t){var e=t.parentElement,n=0,r=0;e&&(e.parentElement&&(n=e.parentElement.getBBox().width),r=parseInt(e.getAttribute("data-x-shift"),10),Number.isNaN(r)&&(r=0)),t.setAttribute("x1",0-r+8),t.setAttribute("x2",n-r-8)}))):o.debug("No Node "+t+": "+JSON.stringify(s.node(t)))}));var x=_.getBBox();s.edges().forEach((function(t){void 0!==t&&void 0!==s.edge(t)&&(o.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(s.edge(t))),function(t,e,n){e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var r=e.points,i=zu().x((function(t){return t.x})).y((function(t){return t.y})).curve(Vu),a=t.append("path").attr("d",i(r)).attr("id","edge"+pE).attr("class","transition"),s="";if(Jv().state.arrowMarkerAbsolute&&(s=(s=(s=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),a.attr("marker-end","url("+s+"#"+function(t){switch(t){case sE.relationType.AGGREGATION:return"aggregation";case sE.relationType.EXTENSION:return"extension";case sE.relationType.COMPOSITION:return"composition";case sE.relationType.DEPENDENCY:return"dependency"}}(sE.relationType.DEPENDENCY)+"End)"),void 0!==n.title){for(var c=t.append("g").attr("class","stateLabel"),u=Hv.calcLabelPosition(e.points),l=u.x,h=u.y,f=Um.getRows(n.title),d=0,p=[],y=0,g=0,m=0;m<=f.length;m++){var v=c.append("text").attr("text-anchor","middle").text(f[m]).attr("x",l).attr("y",h+d),b=v.node().getBBox();if(y=Math.max(y,b.width),g=Math.min(g,b.x),o.info(b.x,l,h+d),0===d){var _=v.node().getBBox();d=_.height,o.info("Title height",d,h)}p.push(v)}var x=d*f.length;if(f.length>1){var w=(f.length-1)*d*.5;p.forEach((function(t,e){return t.attr("y",h+e*d-w)})),x=d*f.length}var k=c.node().getBBox();c.insert("rect",":first-child").attr("class","box").attr("x",l-y/2-Jv().state.padding/2).attr("y",h-x/2-Jv().state.padding/2-3.5).attr("width",y+Jv().state.padding).attr("height",x+Jv().state.padding),o.info(k)}pE++}(n,s.edge(t),s.edge(t).relation))})),x=_.getBBox();var w={id:r||"root",label:r||"root",width:0,height:0};return w.width=x.width+2*lE.padding,w.height=x.height+2*lE.padding,o.debug("Doc rendered",w,s),w};const mE=function(t,e){lE=Jv().state,$T.parser.yy.clear(),$T.parser.parse(t),o.debug("Rendering diagram "+t);var n=au("[id='".concat(e,"']"));n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z"),new(kb().Graph)({multigraph:!0,compound:!0,rankdir:"RL"}).setDefaultEdgeLabel((function(){return{}}));var r=sE.getRootDoc();gE(r,n,void 0,!1);var i=lE.padding,a=n.node().getBBox(),s=a.width+2*i,c=a.height+2*i;zv(n,c,1.75*s,lE.useMaxWidth),n.attr("viewBox","".concat(a.x-lE.padding,"  ").concat(a.y-lE.padding," ")+s+" "+c)};var vE={},bE={},_E=function(t,e,n,r){if("root"!==n.id){var i="rect";!0===n.start&&(i="start"),!1===n.start&&(i="end"),"default"!==n.type&&(i=n.type),bE[n.id]||(bE[n.id]={id:n.id,shape:i,description:Um.sanitizeText(n.id,Jv()),classes:"statediagram-state"}),n.description&&(Array.isArray(bE[n.id].description)?(bE[n.id].shape="rectWithTitle",bE[n.id].description.push(n.description)):bE[n.id].description.length>0?(bE[n.id].shape="rectWithTitle",bE[n.id].description===n.id?bE[n.id].description=[n.description]:bE[n.id].description=[bE[n.id].description,n.description]):(bE[n.id].shape="rect",bE[n.id].description=n.description),bE[n.id].description=Um.sanitizeTextOrArray(bE[n.id].description,Jv())),1===bE[n.id].description.length&&"rectWithTitle"===bE[n.id].shape&&(bE[n.id].shape="rect"),!bE[n.id].type&&n.doc&&(o.info("Setting cluster for ",n.id,kE(n)),bE[n.id].type="group",bE[n.id].dir=kE(n),bE[n.id].shape="divider"===n.type?"divider":"roundedWithTitle",bE[n.id].classes=bE[n.id].classes+" "+(r?"statediagram-cluster statediagram-cluster-alt":"statediagram-cluster"));var a={labelStyle:"",shape:bE[n.id].shape,labelText:bE[n.id].description,classes:bE[n.id].classes,style:"",id:n.id,dir:bE[n.id].dir,domId:"state-"+n.id+"-"+xE,type:bE[n.id].type,padding:15};if(n.note){var s={labelStyle:"",shape:"note",labelText:n.note.text,classes:"statediagram-note",style:"",id:n.id+"----note-"+xE,domId:"state-"+n.id+"----note-"+xE,type:bE[n.id].type,padding:15},c={labelStyle:"",shape:"noteGroup",labelText:n.note.text,classes:bE[n.id].classes,style:"",id:n.id+"----parent",domId:"state-"+n.id+"----parent-"+xE,type:"group",padding:0};xE++,t.setNode(n.id+"----parent",c),t.setNode(s.id,s),t.setNode(n.id,a),t.setParent(n.id,n.id+"----parent"),t.setParent(s.id,n.id+"----parent");var u=n.id,l=s.id;"left of"===n.note.position&&(u=s.id,l=n.id),t.setEdge(u,l,{arrowhead:"none",arrowType:"",style:"fill:none",labelStyle:"",classes:"transition note-edge",arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal"})}else t.setNode(n.id,a)}e&&"root"!==e.id&&(o.trace("Setting node ",n.id," to be child of its parent ",e.id),t.setParent(n.id,e.id)),n.doc&&(o.trace("Adding nodes children "),wE(t,n,n.doc,!r))},xE=0,wE=function(t,e,n,r){o.trace("items",n),n.forEach((function(n){if("state"===n.stmt||"default"===n.stmt)_E(t,e,n,r);else if("relation"===n.stmt){_E(t,e,n.state1,r),_E(t,e,n.state2,r);var i={id:"edge"+xE,arrowhead:"normal",arrowTypeEnd:"arrow_barb",style:"fill:none",labelStyle:"",label:Um.sanitizeText(n.description,Jv()),arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal",classes:"transition"},a=n.state1.id,o=n.state2.id;t.setEdge(a,o,i,xE),xE++}}))},kE=function(t,e){var n=e||"TB";if(t.doc)for(var r=0;r<t.doc.length;r++){var i=t.doc[r];"dir"===i.stmt&&(n=i.value)}return n};const TE=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)vE[e[n]]=t[e[n]]};function EE(t){return function(t){if(Array.isArray(t))return CE(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return CE(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?CE(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function CE(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}var SE="",AE="",ME=[],NE=[],DE=[],OE=function(){for(var t=!0,e=0;e<DE.length;e++)DE[e].processed,t=t&&DE[e].processed;return t};const BE={parseDirective:function(t,e,n){cC.parseDirective(this,t,e,n)},getConfig:function(){return Jv().journey},clear:function(){ME.length=0,NE.length=0,AE="",SE="",DE.length=0},setTitle:function(t){SE=t},getTitle:function(){return SE},addSection:function(t){AE=t,ME.push(t)},getSections:function(){return ME},getTasks:function(){for(var t=OE(),e=0;!t&&e<100;)t=OE(),e++;return NE.push.apply(NE,DE),NE},addTask:function(t,e){var n=e.substr(1).split(":"),r=0,i=[];1===n.length?(r=Number(n[0]),i=[]):(r=Number(n[0]),i=n[1].split(","));var a=i.map((function(t){return t.trim()})),o={section:AE,type:AE,people:a,task:t,score:r};DE.push(o)},addTaskOrg:function(t){var e={section:AE,type:AE,description:t,task:t,classes:[]};NE.push(e)},getActors:function(){return t=[],NE.forEach((function(e){e.people&&t.push.apply(t,EE(e.people))})),EE(new Set(t)).sort();var t}};var LE=n(9763),IE=n.n(LE),RE=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},FE=function(t,e){var n=t.append("circle");return n.attr("cx",e.cx),n.attr("cy",e.cy),n.attr("class","actor-"+e.pos),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("r",e.r),void 0!==n.class&&n.attr("class",n.class),void 0!==e.title&&n.append("title").text(e.title),n},PE=-1,jE=function(){function t(t,e,n,i,a,o,s,c){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("font-color",c).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c,u){for(var l=c.taskFontSize,h=c.taskFontFamily,f=t.split(/<br\s*\/?>/gi),d=0;d<f.length;d++){var p=d*l-l*(f.length-1)/2,y=e.append("text").attr("x",n+a/2).attr("y",i).attr("fill",u).style("text-anchor","middle").style("font-size",l).style("font-family",h);y.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),y.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(y,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).attr("position","fixed").append("xhtml:div").style("display","table").style("height","100%").style("width","100%");h.append("div").attr("class","label").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)n in e&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}();const YE=FE,zE=function(t,e,n){var r=t.append("g"),i={x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0};i.x=e.x,i.y=e.y,i.fill=e.fill,i.width=n.width,i.height=n.height,i.class="journey-section section-type-"+e.num,i.rx=3,i.ry=3,RE(r,i),jE(n)(e.text,r,i.x,i.y,i.width,i.height,{class:"journey-section section-type-"+e.num},n,e.colour)},UE=function(t,e){var n=e.text.replace(/<br\s*\/?>/gi," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.attr("class","legend"),r.style("text-anchor",e.anchor),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.text(n),r},qE=function(t,e,n){var r,i,a,o=e.x+n.width/2,s=t.append("g");PE++,s.append("line").attr("id","task"+PE).attr("x1",o).attr("y1",e.y).attr("x2",o).attr("y2",450).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),r=s,i={cx:o,cy:300+30*(5-e.score),score:e.score},r.append("circle").attr("cx",i.cx).attr("cy",i.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),(a=r.append("g")).append("circle").attr("cx",i.cx-5).attr("cy",i.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),a.append("circle").attr("cx",i.cx+5).attr("cy",i.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),i.score>3?function(t){var e=Iu().startAngle(Math.PI/2).endAngle(Math.PI/2*3).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+i.cx+","+(i.cy+2)+")")}(a):i.score<3?function(t){var e=Iu().startAngle(3*Math.PI/2).endAngle(Math.PI/2*5).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+i.cx+","+(i.cy+7)+")")}(a):function(t){t.append("line").attr("class","mouth").attr("stroke",2).attr("x1",i.cx-5).attr("y1",i.cy+7).attr("x2",i.cx+5).attr("y2",i.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}(a);var c={x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0};c.x=e.x,c.y=e.y,c.fill=e.fill,c.width=n.width,c.height=n.height,c.class="task task-type-"+e.num,c.rx=3,c.ry=3,RE(s,c);var u=e.x+14;e.people.forEach((function(t){var n=e.actors[t].color,r={cx:u,cy:e.y,r:7,fill:n,stroke:"#000",title:t,pos:e.actors[t].position};FE(s,r),u+=10})),jE(n)(e.task,s,c.x,c.y,c.width,c.height,{class:"task"},n,e.colour)};LE.parser.yy=BE;var HE={},$E=Jv().journey,WE=Jv().journey.leftMargin,VE={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],init:function(){this.sequenceItems=[],this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i=Jv().journey,a=this,o=0;this.sequenceItems.forEach((function(s){o++;var c=a.sequenceItems.length-o+1;a.updateVal(s,"starty",e-c*i.boxMargin,Math.min),a.updateVal(s,"stopy",r+c*i.boxMargin,Math.max),a.updateVal(VE.data,"startx",t-c*i.boxMargin,Math.min),a.updateVal(VE.data,"stopx",n+c*i.boxMargin,Math.max),a.updateVal(s,"startx",t-c*i.boxMargin,Math.min),a.updateVal(s,"stopx",n+c*i.boxMargin,Math.max),a.updateVal(VE.data,"starty",e-c*i.boxMargin,Math.min),a.updateVal(VE.data,"stopy",r+c*i.boxMargin,Math.max)}))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(VE.data,"startx",i,Math.min),this.updateVal(VE.data,"starty",o,Math.min),this.updateVal(VE.data,"stopx",a,Math.max),this.updateVal(VE.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return this.data}},GE=$E.sectionFills,XE=$E.sectionColours;const ZE=function(t){Object.keys(t).forEach((function(e){$E[e]=t[e]}))},QE=function(t,e){var n=Jv().journey;LE.parser.yy.clear(),LE.parser.parse(t+"\n"),VE.init();var r=au("#"+e);r.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),r.append("defs").append("marker").attr("id","arrowhead").attr("refX",5).attr("refY",2).attr("markerWidth",6).attr("markerHeight",4).attr("orient","auto").append("path").attr("d","M 0,0 V 4 L6,2 Z");var i=LE.parser.yy.getTasks(),a=LE.parser.yy.getTitle(),o=LE.parser.yy.getActors();for(var s in HE)delete HE[s];var c=0;o.forEach((function(t){HE[t]={color:n.actorColours[c%n.actorColours.length],position:c},c++})),function(t){var e=Jv().journey,n=60;Object.keys(HE).forEach((function(r){var i=HE[r].color,a={cx:20,cy:n,r:7,fill:i,stroke:"#000",pos:HE[r].position};YE(t,a);var o={x:40,y:n+7,fill:"#666",text:r,textMargin:5|e.boxTextMargin};UE(t,o),n+=20}))}(r),VE.insert(0,0,WE,50*Object.keys(HE).length),function(t,e,n){for(var r=Jv().journey,i="",a=n+(2*r.height+r.diagramMarginY),o=0,s="#CCC",c="black",u=0,l=0;l<e.length;l++){var h=e[l];if(i!==h.section){s=GE[o%GE.length],u=o%GE.length,c=XE[o%XE.length];var f={x:l*r.taskMargin+l*r.width+WE,y:50,text:h.section,fill:s,num:u,colour:c};zE(t,f,r),i=h.section,o++}var d=h.people.reduce((function(t,e){return HE[e]&&(t[e]=HE[e]),t}),{});h.x=l*r.taskMargin+l*r.width+WE,h.y=a,h.width=r.diagramMarginX,h.height=r.diagramMarginY,h.colour=c,h.fill=s,h.num=u,h.actors=d,qE(t,h,r),VE.insert(h.x,h.y,h.x+h.width+r.taskMargin,450)}}(r,i,0);var u=VE.getBounds();a&&r.append("text").text(a).attr("x",WE).attr("font-size","4ex").attr("font-weight","bold").attr("y",25);var l=u.stopy-u.starty+2*n.diagramMarginY,h=WE+u.stopx+2*n.diagramMarginX;zv(r,l,h,n.useMaxWidth),r.append("line").attr("x1",WE).attr("y1",4*n.height).attr("x2",h-WE-4).attr("y2",4*n.height).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#arrowhead)");var f=a?70:0;r.attr("viewBox","".concat(u.startx," -25 ").concat(h," ").concat(l+f)),r.attr("preserveAspectRatio","xMinYMin meet"),r.attr("height",l+f+25)};var KE={};const JE=function(t){return"g.classGroup text {\n  fill: ".concat(t.nodeBorder,";\n  fill: ").concat(t.classText,";\n  stroke: none;\n  font-family: ").concat(t.fontFamily,";\n  font-size: 10px;\n\n  .title {\n    font-weight: bolder;\n  }\n\n}\n\n.nodeLabel, .edgeLabel {\n  color: ").concat(t.classText,";\n}\n.edgeLabel .label rect {\n  fill: ").concat(t.mainBkg,";\n}\n.label text {\n  fill: ").concat(t.classText,";\n}\n.edgeLabel .label span {\n  background: ").concat(t.mainBkg,";\n}\n\n.classTitle {\n  font-weight: bolder;\n}\n.node rect,\n  .node circle,\n  .node ellipse,\n  .node polygon,\n  .node path {\n    fill: ").concat(t.mainBkg,";\n    stroke: ").concat(t.nodeBorder,";\n    stroke-width: 1px;\n  }\n\n\n.divider {\n  stroke: ").concat(t.nodeBorder,";\n  stroke: 1;\n}\n\ng.clickable {\n  cursor: pointer;\n}\n\ng.classGroup rect {\n  fill: ").concat(t.mainBkg,";\n  stroke: ").concat(t.nodeBorder,";\n}\n\ng.classGroup line {\n  stroke: ").concat(t.nodeBorder,";\n  stroke-width: 1;\n}\n\n.classLabel .box {\n  stroke: none;\n  stroke-width: 0;\n  fill: ").concat(t.mainBkg,";\n  opacity: 0.5;\n}\n\n.classLabel .label {\n  fill: ").concat(t.nodeBorder,";\n  font-size: 10px;\n}\n\n.relation {\n  stroke: ").concat(t.lineColor,";\n  stroke-width: 1;\n  fill: none;\n}\n\n.dashed-line{\n  stroke-dasharray: 3;\n}\n\n#compositionStart, .composition {\n  fill: ").concat(t.lineColor," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n#compositionEnd, .composition {\n  fill: ").concat(t.lineColor," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n  fill: ").concat(t.lineColor," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n  fill: ").concat(t.lineColor," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n#extensionStart, .extension {\n  fill: ").concat(t.lineColor," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n#extensionEnd, .extension {\n  fill: ").concat(t.lineColor," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n#aggregationStart, .aggregation {\n  fill: ").concat(t.mainBkg," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n#aggregationEnd, .aggregation {\n  fill: ").concat(t.mainBkg," !important;\n  stroke: ").concat(t.lineColor," !important;\n  stroke-width: 1;\n}\n\n.edgeTerminals {\n  font-size: 11px;\n}\n\n")},tC=function(t){return".label {\n    font-family: ".concat(t.fontFamily,";\n    color: ").concat(t.nodeTextColor||t.textColor,";\n  }\n  .cluster-label text {\n    fill: ").concat(t.titleColor,";\n  }\n  .cluster-label span {\n    color: ").concat(t.titleColor,";\n  }\n\n  .label text,span {\n    fill: ").concat(t.nodeTextColor||t.textColor,";\n    color: ").concat(t.nodeTextColor||t.textColor,";\n  }\n\n  .node rect,\n  .node circle,\n  .node ellipse,\n  .node polygon,\n  .node path {\n    fill: ").concat(t.mainBkg,";\n    stroke: ").concat(t.nodeBorder,";\n    stroke-width: 1px;\n  }\n\n  .node .label {\n    text-align: center;\n  }\n  .node.clickable {\n    cursor: pointer;\n  }\n\n  .arrowheadPath {\n    fill: ").concat(t.arrowheadColor,";\n  }\n\n  .edgePath .path {\n    stroke: ").concat(t.lineColor,";\n    stroke-width: 2.0px;\n  }\n\n  .flowchart-link {\n    stroke: ").concat(t.lineColor,";\n    fill: none;\n  }\n\n  .edgeLabel {\n    background-color: ").concat(t.edgeLabelBackground,";\n    rect {\n      opacity: 0.5;\n      background-color: ").concat(t.edgeLabelBackground,";\n      fill: ").concat(t.edgeLabelBackground,";\n    }\n    text-align: center;\n  }\n\n  .cluster rect {\n    fill: ").concat(t.clusterBkg,";\n    stroke: ").concat(t.clusterBorder,";\n    stroke-width: 1px;\n  }\n\n  .cluster text {\n    fill: ").concat(t.titleColor,";\n  }\n\n  .cluster span {\n    color: ").concat(t.titleColor,";\n  }\n  /* .cluster div {\n    color: ").concat(t.titleColor,";\n  } */\n\n  div.mermaidTooltip {\n    position: absolute;\n    text-align: center;\n    max-width: 200px;\n    padding: 2px;\n    font-family: ").concat(t.fontFamily,";\n    font-size: 12px;\n    background: ").concat(t.tertiaryColor,";\n    border: 1px solid ").concat(t.border2,";\n    border-radius: 2px;\n    pointer-events: none;\n    z-index: 100;\n  }\n")},eC=function(t){return"\ndefs #statediagram-barbEnd {\n    fill: ".concat(t.transitionColor,";\n    stroke: ").concat(t.transitionColor,";\n  }\ng.stateGroup text {\n  fill: ").concat(t.nodeBorder,";\n  stroke: none;\n  font-size: 10px;\n}\ng.stateGroup text {\n  fill: ").concat(t.textColor,";\n  stroke: none;\n  font-size: 10px;\n\n}\ng.stateGroup .state-title {\n  font-weight: bolder;\n  fill: ").concat(t.stateLabelColor,";\n}\n\ng.stateGroup rect {\n  fill: ").concat(t.mainBkg,";\n  stroke: ").concat(t.nodeBorder,";\n}\n\ng.stateGroup line {\n  stroke: ").concat(t.lineColor,";\n  stroke-width: 1;\n}\n\n.transition {\n  stroke: ").concat(t.transitionColor,";\n  stroke-width: 1;\n  fill: none;\n}\n\n.stateGroup .composit {\n  fill: ").concat(t.background,";\n  border-bottom: 1px\n}\n\n.stateGroup .alt-composit {\n  fill: #e0e0e0;\n  border-bottom: 1px\n}\n\n.state-note {\n  stroke: ").concat(t.noteBorderColor,";\n  fill: ").concat(t.noteBkgColor,";\n\n  text {\n    fill: ").concat(t.noteTextColor,";\n    stroke: none;\n    font-size: 10px;\n  }\n}\n\n.stateLabel .box {\n  stroke: none;\n  stroke-width: 0;\n  fill: ").concat(t.mainBkg,";\n  opacity: 0.5;\n}\n\n.edgeLabel .label rect {\n  fill: ").concat(t.labelBackgroundColor,";\n  opacity: 0.5;\n}\n.edgeLabel .label text {\n  fill: ").concat(t.transitionLabelColor||t.tertiaryTextColor,";\n}\n.label div .edgeLabel {\n  color: ").concat(t.transitionLabelColor||t.tertiaryTextColor,";\n}\n\n.stateLabel text {\n  fill: ").concat(t.stateLabelColor,";\n  font-size: 10px;\n  font-weight: bold;\n}\n\n.node circle.state-start {\n  fill: ").concat(t.specialStateColor,";\n  stroke: ").concat(t.specialStateColor,";\n}\n\n.node .fork-join {\n  fill: ").concat(t.specialStateColor,";\n  stroke: ").concat(t.specialStateColor,";\n}\n\n.node circle.state-end {\n  fill: ").concat(t.innerEndBackground,";\n  stroke: ").concat(t.background,";\n  stroke-width: 1.5\n}\n.end-state-inner {\n  fill: ").concat(t.compositeBackground||t.background,";\n  // stroke: ").concat(t.background,";\n  stroke-width: 1.5\n}\n\n.node rect {\n  fill: ").concat(t.stateBkg||t.mainBkg,";\n  stroke: ").concat(t.stateBorder||t.nodeBorder,";\n  stroke-width: 1px;\n}\n.node polygon {\n  fill: ").concat(t.mainBkg,";\n  stroke: ").concat(t.stateBorder||t.nodeBorder,";;\n  stroke-width: 1px;\n}\n#statediagram-barbEnd {\n  fill: ").concat(t.lineColor,";\n}\n\n.statediagram-cluster rect {\n  fill: ").concat(t.compositeTitleBackground,";\n  stroke: ").concat(t.stateBorder||t.nodeBorder,";\n  stroke-width: 1px;\n}\n\n.cluster-label, .nodeLabel {\n  color: ").concat(t.stateLabelColor,";\n}\n\n.statediagram-cluster rect.outer {\n  rx: 5px;\n  ry: 5px;\n}\n.statediagram-state .divider {\n  stroke: ").concat(t.stateBorder||t.nodeBorder,";\n}\n\n.statediagram-state .title-state {\n  rx: 5px;\n  ry: 5px;\n}\n.statediagram-cluster.statediagram-cluster .inner {\n  fill: ").concat(t.compositeBackground||t.background,";\n}\n.statediagram-cluster.statediagram-cluster-alt .inner {\n  fill: ").concat(t.altBackground?t.altBackground:"#efefef",";\n}\n\n.statediagram-cluster .inner {\n  rx:0;\n  ry:0;\n}\n\n.statediagram-state rect.basic {\n  rx: 5px;\n  ry: 5px;\n}\n.statediagram-state rect.divider {\n  stroke-dasharray: 10,10;\n  fill: ").concat(t.altBackground?t.altBackground:"#efefef",";\n}\n\n.note-edge {\n  stroke-dasharray: 5;\n}\n\n.statediagram-note rect {\n  fill: ").concat(t.noteBkgColor,";\n  stroke: ").concat(t.noteBorderColor,";\n  stroke-width: 1px;\n  rx: 0;\n  ry: 0;\n}\n.statediagram-note rect {\n  fill: ").concat(t.noteBkgColor,";\n  stroke: ").concat(t.noteBorderColor,";\n  stroke-width: 1px;\n  rx: 0;\n  ry: 0;\n}\n\n.statediagram-note text {\n  fill: ").concat(t.noteTextColor,";\n}\n\n.statediagram-note .nodeLabel {\n  color: ").concat(t.noteTextColor,";\n}\n.statediagram .edgeLabel {\n  color: red; // ").concat(t.noteTextColor,";\n}\n\n#dependencyStart, #dependencyEnd {\n  fill: ").concat(t.lineColor,";\n  stroke: ").concat(t.lineColor,";\n  stroke-width: 1;\n}\n")};var nC={flowchart:tC,"flowchart-v2":tC,sequence:function(t){return".actor {\n    stroke: ".concat(t.actorBorder,";\n    fill: ").concat(t.actorBkg,";\n  }\n\n  text.actor > tspan {\n    fill: ").concat(t.actorTextColor,";\n    stroke: none;\n  }\n\n  .actor-line {\n    stroke: ").concat(t.actorLineColor,";\n  }\n\n  .messageLine0 {\n    stroke-width: 1.5;\n    stroke-dasharray: none;\n    stroke: ").concat(t.signalColor,";\n  }\n\n  .messageLine1 {\n    stroke-width: 1.5;\n    stroke-dasharray: 2, 2;\n    stroke: ").concat(t.signalColor,";\n  }\n\n  #arrowhead path {\n    fill: ").concat(t.signalColor,";\n    stroke: ").concat(t.signalColor,";\n  }\n\n  .sequenceNumber {\n    fill: ").concat(t.sequenceNumberColor,";\n  }\n\n  #sequencenumber {\n    fill: ").concat(t.signalColor,";\n  }\n\n  #crosshead path {\n    fill: ").concat(t.signalColor,";\n    stroke: ").concat(t.signalColor,";\n  }\n\n  .messageText {\n    fill: ").concat(t.signalTextColor,";\n    stroke: ").concat(t.signalTextColor,";\n  }\n\n  .labelBox {\n    stroke: ").concat(t.labelBoxBorderColor,";\n    fill: ").concat(t.labelBoxBkgColor,";\n  }\n\n  .labelText, .labelText > tspan {\n    fill: ").concat(t.labelTextColor,";\n    stroke: none;\n  }\n\n  .loopText, .loopText > tspan {\n    fill: ").concat(t.loopTextColor,";\n    stroke: none;\n  }\n\n  .loopLine {\n    stroke-width: 2px;\n    stroke-dasharray: 2, 2;\n    stroke: ").concat(t.labelBoxBorderColor,";\n    fill: ").concat(t.labelBoxBorderColor,";\n  }\n\n  .note {\n    //stroke: #decc93;\n    stroke: ").concat(t.noteBorderColor,";\n    fill: ").concat(t.noteBkgColor,";\n  }\n\n  .noteText, .noteText > tspan {\n    fill: ").concat(t.noteTextColor,";\n    stroke: none;\n  }\n\n  .activation0 {\n    fill: ").concat(t.activationBkgColor,";\n    stroke: ").concat(t.activationBorderColor,";\n  }\n\n  .activation1 {\n    fill: ").concat(t.activationBkgColor,";\n    stroke: ").concat(t.activationBorderColor,";\n  }\n\n  .activation2 {\n    fill: ").concat(t.activationBkgColor,";\n    stroke: ").concat(t.activationBorderColor,";\n  }\n\n  .actorPopupMenu {\n    position: absolute;\n  }\n\n  .actorPopupMenuPanel {\n    position: absolute;\n    fill: ").concat(t.actorBkg,";\n    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);\n    filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));\n}\n  .actor-man line {\n    stroke: ").concat(t.actorBorder,";\n    fill: ").concat(t.actorBkg,";\n  }\n  .actor-man circle, line {\n    stroke: ").concat(t.actorBorder,";\n    fill: ").concat(t.actorBkg,";\n    stroke-width: 2px;\n  }\n")},gantt:function(t){return'\n  .mermaid-main-font {\n    font-family: "trebuchet ms", verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n  }\n  .exclude-range {\n    fill: '.concat(t.excludeBkgColor,";\n  }\n\n  .section {\n    stroke: none;\n    opacity: 0.2;\n  }\n\n  .section0 {\n    fill: ").concat(t.sectionBkgColor,";\n  }\n\n  .section2 {\n    fill: ").concat(t.sectionBkgColor2,";\n  }\n\n  .section1,\n  .section3 {\n    fill: ").concat(t.altSectionBkgColor,";\n    opacity: 0.2;\n  }\n\n  .sectionTitle0 {\n    fill: ").concat(t.titleColor,";\n  }\n\n  .sectionTitle1 {\n    fill: ").concat(t.titleColor,";\n  }\n\n  .sectionTitle2 {\n    fill: ").concat(t.titleColor,";\n  }\n\n  .sectionTitle3 {\n    fill: ").concat(t.titleColor,";\n  }\n\n  .sectionTitle {\n    text-anchor: start;\n    // font-size: ").concat(t.ganttFontSize,";\n    // text-height: 14px;\n    font-family: 'trebuchet ms', verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n\n  }\n\n\n  /* Grid and axis */\n\n  .grid .tick {\n    stroke: ").concat(t.gridColor,";\n    opacity: 0.8;\n    shape-rendering: crispEdges;\n    text {\n      font-family: ").concat(t.fontFamily,";\n      fill: ").concat(t.textColor,";\n    }\n  }\n\n  .grid path {\n    stroke-width: 0;\n  }\n\n\n  /* Today line */\n\n  .today {\n    fill: none;\n    stroke: ").concat(t.todayLineColor,";\n    stroke-width: 2px;\n  }\n\n\n  /* Task styling */\n\n  /* Default task */\n\n  .task {\n    stroke-width: 2;\n  }\n\n  .taskText {\n    text-anchor: middle;\n    font-family: 'trebuchet ms', verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n  }\n\n  // .taskText:not([font-size]) {\n  //   font-size: ").concat(t.ganttFontSize,";\n  // }\n\n  .taskTextOutsideRight {\n    fill: ").concat(t.taskTextDarkColor,";\n    text-anchor: start;\n    // font-size: ").concat(t.ganttFontSize,";\n    font-family: 'trebuchet ms', verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n\n  }\n\n  .taskTextOutsideLeft {\n    fill: ").concat(t.taskTextDarkColor,";\n    text-anchor: end;\n    // font-size: ").concat(t.ganttFontSize,";\n  }\n\n  /* Special case clickable */\n  .task.clickable {\n    cursor: pointer;\n  }\n  .taskText.clickable {\n    cursor: pointer;\n    fill: ").concat(t.taskTextClickableColor," !important;\n    font-weight: bold;\n  }\n\n  .taskTextOutsideLeft.clickable {\n    cursor: pointer;\n    fill: ").concat(t.taskTextClickableColor," !important;\n    font-weight: bold;\n  }\n\n  .taskTextOutsideRight.clickable {\n    cursor: pointer;\n    fill: ").concat(t.taskTextClickableColor," !important;\n    font-weight: bold;\n  }\n\n  /* Specific task settings for the sections*/\n\n  .taskText0,\n  .taskText1,\n  .taskText2,\n  .taskText3 {\n    fill: ").concat(t.taskTextColor,";\n  }\n\n  .task0,\n  .task1,\n  .task2,\n  .task3 {\n    fill: ").concat(t.taskBkgColor,";\n    stroke: ").concat(t.taskBorderColor,";\n  }\n\n  .taskTextOutside0,\n  .taskTextOutside2\n  {\n    fill: ").concat(t.taskTextOutsideColor,";\n  }\n\n  .taskTextOutside1,\n  .taskTextOutside3 {\n    fill: ").concat(t.taskTextOutsideColor,";\n  }\n\n\n  /* Active task */\n\n  .active0,\n  .active1,\n  .active2,\n  .active3 {\n    fill: ").concat(t.activeTaskBkgColor,";\n    stroke: ").concat(t.activeTaskBorderColor,";\n  }\n\n  .activeText0,\n  .activeText1,\n  .activeText2,\n  .activeText3 {\n    fill: ").concat(t.taskTextDarkColor," !important;\n  }\n\n\n  /* Completed task */\n\n  .done0,\n  .done1,\n  .done2,\n  .done3 {\n    stroke: ").concat(t.doneTaskBorderColor,";\n    fill: ").concat(t.doneTaskBkgColor,";\n    stroke-width: 2;\n  }\n\n  .doneText0,\n  .doneText1,\n  .doneText2,\n  .doneText3 {\n    fill: ").concat(t.taskTextDarkColor," !important;\n  }\n\n\n  /* Tasks on the critical line */\n\n  .crit0,\n  .crit1,\n  .crit2,\n  .crit3 {\n    stroke: ").concat(t.critBorderColor,";\n    fill: ").concat(t.critBkgColor,";\n    stroke-width: 2;\n  }\n\n  .activeCrit0,\n  .activeCrit1,\n  .activeCrit2,\n  .activeCrit3 {\n    stroke: ").concat(t.critBorderColor,";\n    fill: ").concat(t.activeTaskBkgColor,";\n    stroke-width: 2;\n  }\n\n  .doneCrit0,\n  .doneCrit1,\n  .doneCrit2,\n  .doneCrit3 {\n    stroke: ").concat(t.critBorderColor,";\n    fill: ").concat(t.doneTaskBkgColor,";\n    stroke-width: 2;\n    cursor: pointer;\n    shape-rendering: crispEdges;\n  }\n\n  .milestone {\n    transform: rotate(45deg) scale(0.8,0.8);\n  }\n\n  .milestoneText {\n    font-style: italic;\n  }\n  .doneCritText0,\n  .doneCritText1,\n  .doneCritText2,\n  .doneCritText3 {\n    fill: ").concat(t.taskTextDarkColor," !important;\n  }\n\n  .activeCritText0,\n  .activeCritText1,\n  .activeCritText2,\n  .activeCritText3 {\n    fill: ").concat(t.taskTextDarkColor," !important;\n  }\n\n  .titleText {\n    text-anchor: middle;\n    font-size: 18px;\n    fill: ").concat(t.textColor,"    ;\n    font-family: 'trebuchet ms', verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n  }\n")},classDiagram:JE,"classDiagram-v2":JE,class:JE,stateDiagram:eC,state:eC,git:function(){return"\n  .commit-id,\n  .commit-msg,\n  .branch-label {\n    fill: lightgrey;\n    color: lightgrey;\n    font-family: 'trebuchet ms', verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n  }\n"},info:function(){return""},pie:function(t){return"\n  .pieCircle{\n    stroke: ".concat(t.pieStrokeColor,";\n    stroke-width : ").concat(t.pieStrokeWidth,";\n    opacity : ").concat(t.pieOpacity,";\n  }\n  .pieTitleText {\n    text-anchor: middle;\n    font-size: ").concat(t.pieTitleTextSize,";\n    fill: ").concat(t.pieTitleTextColor,";\n    font-family: ").concat(t.fontFamily,";\n  }\n  .slice {\n    font-family: ").concat(t.fontFamily,";\n    fill: ").concat(t.pieSectionTextColor,";\n    font-size:").concat(t.pieSectionTextSize,";\n    // fill: white;\n  }\n  .legend text {\n    fill: ").concat(t.pieLegendTextColor,";\n    font-family: ").concat(t.fontFamily,";\n    font-size: ").concat(t.pieLegendTextSize,";\n  }\n")},er:function(t){return"\n  .entityBox {\n    fill: ".concat(t.mainBkg,";\n    stroke: ").concat(t.nodeBorder,";\n  }\n\n  .attributeBoxOdd {\n    fill: #ffffff;\n    stroke: ").concat(t.nodeBorder,";\n  }\n\n  .attributeBoxEven {\n    fill: #f2f2f2;\n    stroke: ").concat(t.nodeBorder,";\n  }\n\n  .relationshipLabelBox {\n    fill: ").concat(t.tertiaryColor,";\n    opacity: 0.7;\n    background-color: ").concat(t.tertiaryColor,";\n      rect {\n        opacity: 0.5;\n      }\n  }\n\n    .relationshipLine {\n      stroke: ").concat(t.lineColor,";\n    }\n")},journey:function(t){return".label {\n    font-family: 'trebuchet ms', verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n    color: ".concat(t.textColor,";\n  }\n  .mouth {\n    stroke: #666;\n  }\n\n  line {\n    stroke: ").concat(t.textColor,"\n  }\n\n  .legend {\n    fill: ").concat(t.textColor,";\n  }\n\n  .label text {\n    fill: #333;\n  }\n  .label {\n    color: ").concat(t.textColor,"\n  }\n\n  .face {\n    ").concat(t.faceColor?"fill: ".concat(t.faceColor):"fill: #FFF8DC",";\n    stroke: #999;\n  }\n\n  .node rect,\n  .node circle,\n  .node ellipse,\n  .node polygon,\n  .node path {\n    fill: ").concat(t.mainBkg,";\n    stroke: ").concat(t.nodeBorder,";\n    stroke-width: 1px;\n  }\n\n  .node .label {\n    text-align: center;\n  }\n  .node.clickable {\n    cursor: pointer;\n  }\n\n  .arrowheadPath {\n    fill: ").concat(t.arrowheadColor,";\n  }\n\n  .edgePath .path {\n    stroke: ").concat(t.lineColor,";\n    stroke-width: 1.5px;\n  }\n\n  .flowchart-link {\n    stroke: ").concat(t.lineColor,";\n    fill: none;\n  }\n\n  .edgeLabel {\n    background-color: ").concat(t.edgeLabelBackground,";\n    rect {\n      opacity: 0.5;\n    }\n    text-align: center;\n  }\n\n  .cluster rect {\n  }\n\n  .cluster text {\n    fill: ").concat(t.titleColor,";\n  }\n\n  div.mermaidTooltip {\n    position: absolute;\n    text-align: center;\n    max-width: 200px;\n    padding: 2px;\n    font-family: 'trebuchet ms', verdana, arial, sans-serif;\n    font-family: var(--mermaid-font-family);\n    font-size: 12px;\n    background: ").concat(t.tertiaryColor,";\n    border: 1px solid ").concat(t.border2,";\n    border-radius: 2px;\n    pointer-events: none;\n    z-index: 100;\n  }\n\n  .task-type-0, .section-type-0  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType0):"",";\n  }\n  .task-type-1, .section-type-1  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType1):"",";\n  }\n  .task-type-2, .section-type-2  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType2):"",";\n  }\n  .task-type-3, .section-type-3  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType3):"",";\n  }\n  .task-type-4, .section-type-4  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType4):"",";\n  }\n  .task-type-5, .section-type-5  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType5):"",";\n  }\n  .task-type-6, .section-type-6  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType6):"",";\n  }\n  .task-type-7, .section-type-7  {\n    ").concat(t.fillType0?"fill: ".concat(t.fillType7):"",";\n  }\n\n  .actor-0 {\n    ").concat(t.actor0?"fill: ".concat(t.actor0):"",";\n  }\n  .actor-1 {\n    ").concat(t.actor1?"fill: ".concat(t.actor1):"",";\n  }\n  .actor-2 {\n    ").concat(t.actor2?"fill: ".concat(t.actor2):"",";\n  }\n  .actor-3 {\n    ").concat(t.actor3?"fill: ".concat(t.actor3):"",";\n  }\n  .actor-4 {\n    ").concat(t.actor4?"fill: ".concat(t.actor4):"",";\n  }\n  .actor-5 {\n    ").concat(t.actor5?"fill: ".concat(t.actor5):"",";\n  }\n\n  }\n")},requirement:function(t){return"\n\n  marker {\n    fill: ".concat(t.relationColor,";\n    stroke: ").concat(t.relationColor,";\n  }\n\n  marker.cross {\n    stroke: ").concat(t.lineColor,";\n  }\n\n  svg {\n    font-family: ").concat(t.fontFamily,";\n    font-size: ").concat(t.fontSize,";\n  }\n\n  .reqBox {\n    fill: ").concat(t.requirementBackground,";\n    fill-opacity: 100%;\n    stroke: ").concat(t.requirementBorderColor,";\n    stroke-width: ").concat(t.requirementBorderSize,";\n  }\n  \n  .reqTitle, .reqLabel{\n    fill:  ").concat(t.requirementTextColor,";\n  }\n  .reqLabelBox {\n    fill: ").concat(t.relationLabelBackground,";\n    fill-opacity: 100%;\n  }\n\n  .req-title-line {\n    stroke: ").concat(t.requirementBorderColor,";\n    stroke-width: ").concat(t.requirementBorderSize,";\n  }\n  .relationshipLine {\n    stroke: ").concat(t.relationColor,";\n    stroke-width: 1;\n  }\n  .relationshipLabel {\n    fill: ").concat(t.relationLabelColor,";\n  }\n\n")}};function rC(t){return rC="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},rC(t)}var iC=function(t){var e=t;return(e=(e=e.replace(/fl°°/g,(function(){return"&#"}))).replace(/fl°/g,(function(){return"&"}))).replace(/¶ß/g,(function(){return";"}))},aC={};function oC(t){var e;Zw(t.git),Fx(t.flowchart),Yx(t.flowchart),void 0!==t.sequenceDiagram&&HT.setConf(Lv(t.sequence,t.sequenceDiagram)),HT.setConf(t.sequence),t.gantt,Pb(t.class),t.state,TE(t.state),rk(t.class),U_(t.er),ZE(t.journey),Mk(t.requirement),e=t.class,Object.keys(e).forEach((function(t){KE[t]=e[t]}))}var sC=Object.freeze({render:function(t,e,n,r){nb();var i=e,a=Hv.detectInit(i);a&&(Uv(a),eb(a));var s=Jv();if(e.length>s.maxTextSize&&(i="graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa"),void 0!==r)r.innerHTML="",au(r).append("div").attr("id","d"+t).attr("style","font-family: "+s.fontFamily).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g");else{var c=document.getElementById(t);c&&c.remove();var u=document.querySelector("#d"+t);u&&u.remove(),au("body").append("div").attr("id","d"+t).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g")}window.txt=i,i=i.replace(/style.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)})).replace(/classDef.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)})).replace(/#\w+;/g,(function(t){var e=t.substring(1,t.length-1);return/^\+?\d+$/.test(e)?"fl°°"+e+"¶ß":"fl°"+e+"¶ß"}));var l=au("#d"+t).node(),h=Hv.detectType(i,s),f=l.firstChild,d=f.firstChild,p="";if(void 0!==s.themeCSS&&(p+="\n".concat(s.themeCSS)),void 0!==s.fontFamily&&(p+="\n:root { --mermaid-font-family: ".concat(s.fontFamily,"}")),void 0!==s.altFontFamily&&(p+="\n:root { --mermaid-alt-font-family: ".concat(s.altFontFamily,"}")),"flowchart"===h||"flowchart-v2"===h||"graph"===h){var y=function(t){o.info("Extracting classes"),gx.clear();try{var e=vx().parser;return e.yy=gx,e.parse(t),gx.getClasses()}catch(t){return}}(i),g=s.htmlLabels||s.flowchart.htmlLabels;for(var m in y)g?(p+="\n.".concat(m," > * { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," span { ").concat(y[m].styles.join(" !important; ")," !important; }")):(p+="\n.".concat(m," path { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," rect { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," polygon { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," ellipse { ").concat(y[m].styles.join(" !important; ")," !important; }"),p+="\n.".concat(m," circle { ").concat(y[m].styles.join(" !important; ")," !important; }"),y[m].textStyles&&(p+="\n.".concat(m," tspan { ").concat(y[m].textStyles.join(" !important; ")," !important; }")))}var v=function(t,e){return am(Cm("".concat(t,"{").concat(e,"}")),om)}("#".concat(t),function(t,e,n){return" {\n    font-family: ".concat(n.fontFamily,";\n    font-size: ").concat(n.fontSize,";\n    fill: ").concat(n.textColor,"\n  }\n\n  /* Classes common for multiple diagrams */\n\n  .error-icon {\n    fill: ").concat(n.errorBkgColor,";\n  }\n  .error-text {\n    fill: ").concat(n.errorTextColor,";\n    stroke: ").concat(n.errorTextColor,";\n  }\n\n  .edge-thickness-normal {\n    stroke-width: 2px;\n  }\n  .edge-thickness-thick {\n    stroke-width: 3.5px\n  }\n  .edge-pattern-solid {\n    stroke-dasharray: 0;\n  }\n\n  .edge-pattern-dashed{\n    stroke-dasharray: 3;\n  }\n  .edge-pattern-dotted {\n    stroke-dasharray: 2;\n  }\n\n  .marker {\n    fill: ").concat(n.lineColor,";\n    stroke: ").concat(n.lineColor,";\n  }\n  .marker.cross {\n    stroke: ").concat(n.lineColor,";\n  }\n\n  svg {\n    font-family: ").concat(n.fontFamily,";\n    font-size: ").concat(n.fontSize,";\n  }\n\n  ").concat(nC[t](n),"\n\n  ").concat(e,"\n")}(h,p,s.themeVariables)),b=document.createElement("style");b.innerHTML="#".concat(t," ")+v,f.insertBefore(b,d);try{switch(h){case"git":s.flowchart.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Zw(s.git),function(t,e,n){try{var r=Yw().parser;r.yy=Fw,r.yy.clear(),o.debug("in gitgraph renderer",t+"\n","id:",e,n),r.parse(t+"\n"),Uw=Object.assign(Uw,qw,Fw.getOptions()),o.debug("effective options",Uw);var i=Fw.getDirection();zw=Fw.getCommits();var a=Fw.getBranchesAsObjArray();"BT"===i&&(Uw.nodeLabel.x=a.length*Uw.branchOffset,Uw.nodeLabel.width="100%",Uw.nodeLabel.y=-2*Uw.nodeRadius);var s=au('[id="'.concat(e,'"]'));for(var c in function(t){t.append("defs").append("g").attr("id","def-commit").append("circle").attr("r",Uw.nodeRadius).attr("cx",0).attr("cy",0),t.select("#def-commit").append("foreignObject").attr("width",Uw.nodeLabel.width).attr("height",Uw.nodeLabel.height).attr("x",Uw.nodeLabel.x).attr("y",Uw.nodeLabel.y).attr("class","node-label").attr("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility").append("p").html("")}(s),Pw=1,a){var u=a[c];Gw(s,u.commit.id,a,i),Xw(s,u.commit,i),Pw++}s.attr("height",(function(){return"BT"===i?Object.keys(zw).length*Uw.nodeSpacing:(a.length+1)*Uw.branchOffset}))}catch(t){o.error("Error while rendering gitgraph"),o.error(t.message)}}(i,t,!1);break;case"flowchart":s.flowchart.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Fx(s.flowchart),Px(i,t);break;case"flowchart-v2":s.flowchart.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Yx(s.flowchart),zx(i,t);break;case"sequence":s.sequence.arrowMarkerAbsolute=s.arrowMarkerAbsolute,s.sequenceDiagram?(HT.setConf(Object.assign(s.sequence,s.sequenceDiagram)),console.error("`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.")):HT.setConf(s.sequence),HT.draw(i,t);break;case"gantt":s.gantt.arrowMarkerAbsolute=s.arrowMarkerAbsolute,s.gantt,Tw(i,t);break;case"class":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,Pb(s.class),jb(i,t);break;case"classDiagram":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,function(t){Object.keys(t).forEach((function(e){A_[e]=t[e]}))}(s.class),function(t,e){o.info("Drawing class"),bb.clear(),Tb.parser.parse(t);var n=Jv().flowchart;o.info("config:",n);var r=n.nodeSpacing||50,i=n.rankSpacing||50,a=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:bb.getDirection(),nodesep:r,ranksep:i,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),s=bb.getClasses(),c=bb.getRelations();o.info(c),function(t,e){var n=Object.keys(t);o.info("keys:",n),o.info(t),n.forEach((function(n){var r=t[n],i="";r.cssClasses.length>0&&(i=i+" "+r.cssClasses.join(" "));var a,s,c={labelStyle:""},u=void 0!==r.text?r.text:r.id;r.type,s="class_box",e.setNode(r.id,{labelStyle:c.labelStyle,shape:s,labelText:(a=u,Um.sanitizeText(a,Jv())),classData:r,rx:0,ry:0,class:i,style:c.style,id:r.id,domId:r.domId,haveCallback:r.haveCallback,link:r.link,width:"group"===r.type?500:void 0,type:r.type,padding:Jv().flowchart.padding}),o.info("setNode",{labelStyle:c.labelStyle,shape:s,labelText:u,rx:0,ry:0,class:i,style:c.style,id:r.id,width:"group"===r.type?500:void 0,type:r.type,padding:Jv().flowchart.padding})}))}(s,a),function(t,e){var n=0;t.forEach((function(r){n++;var i={classes:"relation"};i.pattern=1==r.relation.lineType?"dashed":"solid",i.id="id"+n,"arrow_open"===r.type?i.arrowhead="none":i.arrowhead="normal",o.info(i,r),i.startLabelRight="none"===r.relationTitle1?"":r.relationTitle1,i.endLabelLeft="none"===r.relationTitle2?"":r.relationTitle2,i.arrowTypeStart=M_(r.relation.type1),i.arrowTypeEnd=M_(r.relation.type2);var a="",s="";if(void 0!==r.style){var c=Nv(r.style);a=c.style,s=c.labelStyle}else a="fill:none";i.style=a,i.labelStyle=s,void 0!==r.interpolate?i.curve=Av(r.interpolate,Pu):void 0!==t.defaultInterpolate?i.curve=Av(t.defaultInterpolate,Pu):i.curve=Av(A_.curve,Pu),r.text=r.title,void 0===r.text?void 0!==r.style&&(i.arrowheadStyle="fill: #333"):(i.arrowheadStyle="fill: #333",i.labelpos="c",Jv().flowchart.htmlLabels?(i.labelType="html",i.label='<span class="edgeLabel">'+r.text+"</span>"):(i.labelType="text",i.label=r.text.replace(Um.lineBreakRegex,"\n"),void 0===r.style&&(i.style=i.style||"stroke: #333; stroke-width: 1.5px;fill:none"),i.labelStyle=i.labelStyle.replace("color:","fill:"))),e.setEdge(r.id1,r.id2,i,n)}))}(c,a);var u=au('[id="'.concat(e,'"]'));u.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var l=au("#"+e+" g");S_(l,a,["aggregation","extension","composition","dependency"],"classDiagram",e);var h=u.node().getBBox(),f=h.width+16,d=h.height+16;if(o.debug("new ViewBox 0 0 ".concat(f," ").concat(d),"translate(".concat(8-a._label.marginx,", ").concat(8-a._label.marginy,")")),zv(u,d,f,n.useMaxWidth),u.attr("viewBox","0 0 ".concat(f," ").concat(d)),u.select("g").attr("transform","translate(".concat(8-a._label.marginx,", ").concat(8-h.y,")")),!n.htmlLabels)for(var p=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),y=0;y<p.length;y++){var g=p[y],m=g.getBBox(),v=document.createElementNS("http://www.w3.org/2000/svg","rect");v.setAttribute("rx",0),v.setAttribute("ry",0),v.setAttribute("width",m.width),v.setAttribute("height",m.height),g.insertBefore(v,g.firstChild)}}(i,t);break;case"state":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,s.state,mE(i,t);break;case"stateDiagram":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,TE(s.state),function(t,e){o.info("Drawing state diagram (v2)",e),sE.clear(),bE={};var n=WT().parser;n.yy=sE,n.parse(t);var r=sE.getDirection();void 0===r&&(r="LR");var i=Jv().state,a=i.nodeSpacing||50,s=i.rankSpacing||50;o.info(sE.getRootDocV2()),sE.extract(sE.getRootDocV2()),o.info(sE.getRootDocV2());var c=new(kb().Graph)({multigraph:!0,compound:!0}).setGraph({rankdir:kE(sE.getRootDocV2()),nodesep:a,ranksep:s,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));_E(c,void 0,sE.getRootDocV2(),!0);var u=au('[id="'.concat(e,'"]')),l=au("#"+e+" g");S_(l,c,["barb"],"statediagram",e);var h=u.node().getBBox(),f=h.width+16,d=h.height+16;u.attr("class","statediagram");var p=u.node().getBBox();zv(u,d,1.75*f,i.useMaxWidth);var y="".concat(p.x-8," ").concat(p.y-8," ").concat(f," ").concat(d);o.debug("viewBox ".concat(y)),u.attr("viewBox",y);for(var g=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),m=0;m<g.length;m++){var v=g[m],b=v.getBBox(),_=document.createElementNS("http://www.w3.org/2000/svg","rect");_.setAttribute("rx",0),_.setAttribute("ry",0),_.setAttribute("width",b.width),_.setAttribute("height",b.height),v.insertBefore(_,v.firstChild)}}(i,t);break;case"info":s.class.arrowMarkerAbsolute=s.arrowMarkerAbsolute,rk(s.class),function(t,e,n){try{var r=ek().parser;r.yy=Jw,o.debug("Renering info diagram\n"+t),r.parse(t),o.debug("Parsed info diagram");var i=au("#"+e);i.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size","32px").style("text-anchor","middle").text("v "+n),i.attr("height",100),i.attr("width",400)}catch(t){o.error("Error while rendering info diagram"),o.error(t.message)}}(i,t,Dm);break;case"pie":fk(i,t);break;case"er":U_(s.er),q_(i,t);break;case"journey":ZE(s.journey),QE(i,t);break;case"requirement":Mk(s.requirement),Nk(i,t)}}catch(e){throw function(t,e){try{o.debug("Renering svg for syntax error\n");var n=au("#"+t),r=n.append("g");r.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"),r.append("path").attr("class","error-icon").attr("d","m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"),r.append("path").attr("class","error-icon").attr("d","m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"),r.append("path").attr("class","error-icon").attr("d","m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"),r.append("path").attr("class","error-icon").attr("d","m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"),r.append("path").attr("class","error-icon").attr("d","m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"),r.append("text").attr("class","error-text").attr("x",1240).attr("y",250).attr("font-size","150px").style("text-anchor","middle").text("Syntax error in graph"),r.append("text").attr("class","error-text").attr("x",1050).attr("y",400).attr("font-size","100px").style("text-anchor","middle").text("mermaid version "+e),n.attr("height",100),n.attr("width",400),n.attr("viewBox","768 0 512 512")}catch(t){o.error("Error while rendering info diagram"),o.error(t.message)}}(t,Dm),e}au('[id="'.concat(t,'"]')).selectAll("foreignobject > *").attr("xmlns","http://www.w3.org/1999/xhtml");var _=au("#d"+t).node().innerHTML;if(o.debug("cnf.arrowMarkerAbsolute",s.arrowMarkerAbsolute),s.arrowMarkerAbsolute&&"false"!==s.arrowMarkerAbsolute||(_=_.replace(/marker-end="url\(.*?#/g,'marker-end="url(#',"g")),_=(_=iC(_)).replace(/<br>/g,"<br/>"),void 0!==n)switch(h){case"flowchart":case"flowchart-v2":n(_,gx.bindFunctions);break;case"gantt":n(_,bw.bindFunctions);break;case"class":case"classDiagram":n(_,bb.bindFunctions);break;default:n(_)}else o.debug("CB = undefined!");eT.forEach((function(t){t()})),eT=[];var x=au("#d"+t).node();return null!==x&&"function"==typeof x.remove&&au("#d"+t).node().remove(),_},parse:function(t){var e=Jv(),n=Hv.detectInit(t,e);n&&o.debug("reinit ",n);var r,i=Hv.detectType(t,e);switch(o.debug("Type "+i),i){case"git":(r=Yw()).parser.yy=Fw;break;case"flowchart":case"flowchart-v2":gx.clear(),(r=vx()).parser.yy=gx;break;case"sequence":(r=Ok()).parser.yy=tT;break;case"gantt":(r=kw()).parser.yy=bw;break;case"class":case"classDiagram":(r=Eb()).parser.yy=bb;break;case"state":case"stateDiagram":(r=WT()).parser.yy=sE;break;case"info":o.debug("info info info"),(r=ek()).parser.yy=Jw;break;case"pie":o.debug("pie"),(r=ak()).parser.yy=uk;break;case"er":o.debug("er"),(r=R_()).parser.yy=L_;break;case"journey":o.debug("Journey"),(r=IE()).parser.yy=BE;break;case"requirement":case"requirementDiagram":o.debug("RequirementDiagram"),(r=pk()).parser.yy=_k}return r.parser.yy.graphType=i,r.parser.yy.parseError=function(t,e){throw{str:t,hash:e}},r.parse(t),r},parseDirective:function(t,e,n,r){try{if(void 0!==e)switch(e=e.trim(),n){case"open_directive":aC={};break;case"type_directive":aC.type=e.toLowerCase();break;case"arg_directive":aC.args=JSON.parse(e);break;case"close_directive":(function(t,e,n){switch(o.debug("Directive type=".concat(e.type," with args:"),e.args),e.type){case"init":case"initialize":["config"].forEach((function(t){void 0!==e.args[t]&&("flowchart-v2"===n&&(n="flowchart"),e.args[n]=e.args[t],delete e.args[t])})),o.debug("sanitize in handleDirective",e.args),Uv(e.args),o.debug("sanitize in handleDirective (done)",e.args),e.args,eb(e.args);break;case"wrap":case"nowrap":t&&t.setWrap&&t.setWrap("wrap"===e.type);break;case"themeCss":o.warn("themeCss encountered");break;default:o.warn("Unhandled directive: source: '%%{".concat(e.type,": ").concat(JSON.stringify(e.args?e.args:{}),"}%%"),e)}})(t,aC,r),aC=null}}catch(t){o.error("Error while rendering sequenceDiagram directive: ".concat(e," jison context: ").concat(n)),o.error(t.message)}},initialize:function(t){t&&t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),function(t){Wv=Lv({},t)}(t),t&&t.theme&&ov[t.theme]?t.themeVariables=ov[t.theme].getThemeVariables(t.themeVariables):t&&(t.themeVariables=ov.default.getThemeVariables(t.themeVariables));var e="object"===rC(t)?function(t){return Gv=Lv({},Vv),Gv=Lv(Gv,t),t.theme&&(Gv.themeVariables=ov[t.theme].getThemeVariables(t.themeVariables)),Zv=Qv(Gv,Xv),Gv}(t):Kv();oC(e),s(e.logLevel)},reinitialize:function(){},getConfig:Jv,setConfig:function(t){return Lv(Zv,t),Jv()},getSiteConfig:Kv,updateSiteConfig:function(t){return Gv=Lv(Gv,t),Qv(Gv,Xv),Gv},reset:function(){nb()},globalReset:function(){nb(),oC(Jv())},defaultConfig:Vv});s(Jv().logLevel),nb(Jv());const cC=sC;var uC=function(){lC.startOnLoad?cC.getConfig().startOnLoad&&lC.init():void 0===lC.startOnLoad&&(o.debug("In start, no config"),cC.getConfig().startOnLoad&&lC.init())};"undefined"!=typeof document&&window.addEventListener("load",(function(){uC()}),!1);var lC={startOnLoad:!0,htmlLabels:!0,mermaidAPI:cC,parse:cC.parse,render:cC.render,init:function(){var t,e,n=this,r=cC.getConfig();arguments.length>=2?(void 0!==arguments[0]&&(lC.sequenceConfig=arguments[0]),t=arguments[1]):t=arguments[0],"function"==typeof arguments[arguments.length-1]?(e=arguments[arguments.length-1],o.debug("Callback function found")):void 0!==r.mermaid&&("function"==typeof r.mermaid.callback?(e=r.mermaid.callback,o.debug("Callback function found")):o.debug("No Callback function found")),t=void 0===t?document.querySelectorAll(".mermaid"):"string"==typeof t?document.querySelectorAll(t):t instanceof window.Node?[t]:t,o.debug("Start On Load before: "+lC.startOnLoad),void 0!==lC.startOnLoad&&(o.debug("Start On Load inner: "+lC.startOnLoad),cC.updateSiteConfig({startOnLoad:lC.startOnLoad})),void 0!==lC.ganttConfig&&cC.updateSiteConfig({gantt:lC.ganttConfig});for(var i,a=new Hv.initIdGeneratior(r.deterministicIds,r.deterministicIDSeed),s=function(r){var s=t[r];if(s.getAttribute("data-processed"))return"continue";s.setAttribute("data-processed",!0);var c="mermaid-".concat(a.next());i=s.innerHTML,i=Hv.entityDecode(i).trim().replace(/<br\s*\/?>/gi,"<br/>");var u=Hv.detectInit(i);u&&o.debug("Detected early reinit: ",u);try{cC.render(c,i,(function(t,n){s.innerHTML=t,void 0!==e&&e(c),n&&n(s)}),s)}catch(t){o.warn("Syntax Error rendering"),o.warn(t),n.parseError&&n.parseError(t)}},c=0;c<t.length;c++)s(c)},initialize:function(t){void 0!==t.mermaid&&(void 0!==t.mermaid.startOnLoad&&(lC.startOnLoad=t.mermaid.startOnLoad),void 0!==t.mermaid.htmlLabels&&(lC.htmlLabels="false"!==t.mermaid.htmlLabels&&!1!==t.mermaid.htmlLabels)),cC.initialize(t)},contentLoaded:uC};const hC=lC},4949:(t,e,n)=>{t.exports={graphlib:n(6614),dagre:n(1463),intersect:n(8114),render:n(5787),util:n(8355),version:n(5689)}},9144:(t,e,n)=>{var r=n(8355);function i(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}t.exports={default:i,normal:i,vee:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 L 4 5 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])},undirected:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 5 L 10 5").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}}},5632:(t,e,n)=>{var r=n(8355),i=n(4322),a=n(1322);t.exports=function(t,e){var n,o=e.nodes().filter((function(t){return r.isSubgraph(e,t)})),s=t.selectAll("g.cluster").data(o,(function(t){return t}));return s.selectAll("*").remove(),s.enter().append("g").attr("class","cluster").attr("id",(function(t){return e.node(t).id})).style("opacity",0),s=t.selectAll("g.cluster"),r.applyTransition(s,e).style("opacity",1),s.each((function(t){var n=e.node(t),r=i.select(this);i.select(this).append("rect");var o=r.append("g").attr("class","label");a(o,n,n.clusterLabelPos)})),s.selectAll("rect").each((function(t){var n=e.node(t),a=i.select(this);r.applyStyle(a,n.style)})),n=s.exit?s.exit():s.selectAll(null),r.applyTransition(n,e).style("opacity",0).remove(),s}},6315:(t,e,n)=>{"use strict";var r=n(1034),i=n(1322),a=n(8355),o=n(4322);t.exports=function(t,e){var n,s=t.selectAll("g.edgeLabel").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0);return s.exit().remove(),s.enter().append("g").classed("edgeLabel",!0).style("opacity",0),(s=t.selectAll("g.edgeLabel")).each((function(t){var n=o.select(this);n.select(".label").remove();var a=e.edge(t),s=i(n,e.edge(t),0,0).classed("label",!0),c=s.node().getBBox();a.labelId&&s.attr("id",a.labelId),r.has(a,"width")||(a.width=c.width),r.has(a,"height")||(a.height=c.height)})),n=s.exit?s.exit():s.selectAll(null),a.applyTransition(n,e).style("opacity",0).remove(),s}},940:(t,e,n)=>{"use strict";var r=n(1034),i=n(7584),a=n(8355),o=n(4322);function s(t,e){var n=(o.line||o.svg.line)().x((function(t){return t.x})).y((function(t){return t.y}));return(n.curve||n.interpolate)(t.curve),n(e)}t.exports=function(t,e,n){var c=t.selectAll("g.edgePath").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0),u=function(t,e){var n=t.enter().append("g").attr("class","edgePath").style("opacity",0);return n.append("path").attr("class","path").attr("d",(function(t){var n=e.edge(t),i=e.node(t.v).elem;return s(n,r.range(n.points.length).map((function(){return e=(t=i).getBBox(),{x:(n=t.ownerSVGElement.getScreenCTM().inverse().multiply(t.getScreenCTM()).translate(e.width/2,e.height/2)).e,y:n.f};var t,e,n})))})),n.append("defs"),n}(c,e);!function(t,e){var n=t.exit();a.applyTransition(n,e).style("opacity",0).remove()}(c,e);var l=void 0!==c.merge?c.merge(u):c;return a.applyTransition(l,e).style("opacity",1),l.each((function(t){var n=o.select(this),r=e.edge(t);r.elem=this,r.id&&n.attr("id",r.id),a.applyClass(n,r.class,(n.classed("update")?"update ":"")+"edgePath")})),l.selectAll("path.path").each((function(t){var n=e.edge(t);n.arrowheadId=r.uniqueId("arrowhead");var c=o.select(this).attr("marker-end",(function(){return"url("+(t=location.href,e=n.arrowheadId,t.split("#")[0]+"#"+e+")");var t,e})).style("fill","none");a.applyTransition(c,e).attr("d",(function(t){return function(t,e){var n=t.edge(e),r=t.node(e.v),a=t.node(e.w),o=n.points.slice(1,n.points.length-1);return o.unshift(i(r,o[0])),o.push(i(a,o[o.length-1])),s(n,o)}(e,t)})),a.applyStyle(c,n.style)})),l.selectAll("defs *").remove(),l.selectAll("defs").each((function(t){var r=e.edge(t);(0,n[r.arrowhead])(o.select(this),r.arrowheadId,r,"arrowhead")})),l}},607:(t,e,n)=>{"use strict";var r=n(1034),i=n(1322),a=n(8355),o=n(4322);t.exports=function(t,e,n){var s,c=e.nodes().filter((function(t){return!a.isSubgraph(e,t)})),u=t.selectAll("g.node").data(c,(function(t){return t})).classed("update",!0);return u.exit().remove(),u.enter().append("g").attr("class","node").style("opacity",0),(u=t.selectAll("g.node")).each((function(t){var s=e.node(t),c=o.select(this);a.applyClass(c,s.class,(c.classed("update")?"update ":"")+"node"),c.select("g.label").remove();var u=c.append("g").attr("class","label"),l=i(u,s),h=n[s.shape],f=r.pick(l.node().getBBox(),"width","height");s.elem=this,s.id&&c.attr("id",s.id),s.labelId&&u.attr("id",s.labelId),r.has(s,"width")&&(f.width=s.width),r.has(s,"height")&&(f.height=s.height),f.width+=s.paddingLeft+s.paddingRight,f.height+=s.paddingTop+s.paddingBottom,u.attr("transform","translate("+(s.paddingLeft-s.paddingRight)/2+","+(s.paddingTop-s.paddingBottom)/2+")");var d=o.select(this);d.select(".label-container").remove();var p=h(d,f,s).classed("label-container",!0);a.applyStyle(p,s.style);var y=p.node().getBBox();s.width=y.width,s.height=y.height})),s=u.exit?u.exit():u.selectAll(null),a.applyTransition(s,e).style("opacity",0).remove(),u}},4322:(t,e,n)=>{var r;if(!r)try{r=n(7188)}catch(t){}r||(r=window.d3),t.exports=r},1463:(t,e,n)=>{var r;try{r=n(681)}catch(t){}r||(r=window.dagre),t.exports=r},6614:(t,e,n)=>{var r;try{r=n(8282)}catch(t){}r||(r=window.graphlib),t.exports=r},8114:(t,e,n)=>{t.exports={node:n(7584),circle:n(6587),ellipse:n(3260),polygon:n(5337),rect:n(8049)}},6587:(t,e,n)=>{var r=n(3260);t.exports=function(t,e,n){return r(t,e,e,n)}},3260:t=>{t.exports=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);return r.y<a&&(l=-l),{x:i+u,y:a+l}}},6808:t=>{function e(t,e){return t*e>0}t.exports=function(t,n,r,i){var a,o,s,c,u,l,h,f,d,p,y,g,m;if(!(a=n.y-t.y,s=t.x-n.x,u=n.x*t.y-t.x*n.y,d=a*r.x+s*r.y+u,p=a*i.x+s*i.y+u,0!==d&&0!==p&&e(d,p)||(o=i.y-r.y,c=r.x-i.x,l=i.x*r.y-r.x*i.y,h=o*t.x+c*t.y+l,f=o*n.x+c*n.y+l,0!==h&&0!==f&&e(h,f)||0==(y=a*c-o*s))))return g=Math.abs(y/2),{x:(m=s*l-c*u)<0?(m-g)/y:(m+g)/y,y:(m=o*u-a*l)<0?(m-g)/y:(m+g)/y}}},7584:t=>{t.exports=function(t,e){return t.intersect(e)}},5337:(t,e,n)=>{var r=n(6808);t.exports=function(t,e,n){var i=t.x,a=t.y,o=[],s=Number.POSITIVE_INFINITY,c=Number.POSITIVE_INFINITY;e.forEach((function(t){s=Math.min(s,t.x),c=Math.min(c,t.y)}));for(var u=i-t.width/2-s,l=a-t.height/2-c,h=0;h<e.length;h++){var f=e[h],d=e[h<e.length-1?h+1:0],p=r(t,n,{x:u+f.x,y:l+f.y},{x:u+d.x,y:l+d.y});p&&o.push(p)}return o.length?(o.length>1&&o.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1})),o[0]):(console.log("NO INTERSECTION FOUND, RETURN NODE CENTER",t),t)}},8049:t=>{t.exports=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o),{x:i+n,y:a+r}}},8284:(t,e,n)=>{var r=n(8355);t.exports=function(t,e){var n=t.append("foreignObject").attr("width","100000"),i=n.append("xhtml:div");i.attr("xmlns","http://www.w3.org/1999/xhtml");var a=e.label;switch(typeof a){case"function":i.insert(a);break;case"object":i.insert((function(){return a}));break;default:i.html(a)}r.applyStyle(i,e.labelStyle),i.style("display","inline-block"),i.style("white-space","nowrap");var o=i.node().getBoundingClientRect();return n.attr("width",o.width).attr("height",o.height),n}},1322:(t,e,n)=>{var r=n(7318),i=n(8284),a=n(8287);t.exports=function(t,e,n){var o=e.label,s=t.append("g");"svg"===e.labelType?a(s,e):"string"!=typeof o||"html"===e.labelType?i(s,e):r(s,e);var c,u=s.node().getBBox();switch(n){case"top":c=-e.height/2;break;case"bottom":c=e.height/2-u.height;break;default:c=-u.height/2}return s.attr("transform","translate("+-u.width/2+","+c+")"),s}},8287:(t,e,n)=>{var r=n(8355);t.exports=function(t,e){var n=t;return n.node().appendChild(e.label),r.applyStyle(n,e.labelStyle),n}},7318:(t,e,n)=>{var r=n(8355);t.exports=function(t,e){for(var n=t.append("text"),i=function(t){for(var e,n="",r=!1,i=0;i<t.length;++i)e=t[i],r?(n+="n"===e?"\n":e,r=!1):"\\"===e?r=!0:n+=e;return n}(e.label).split("\n"),a=0;a<i.length;a++)n.append("tspan").attr("xml:space","preserve").attr("dy","1em").attr("x","1").text(i[a]);return r.applyStyle(n,e.labelStyle),n}},1034:(t,e,n)=>{var r;try{r={defaults:n(1747),each:n(6073),isFunction:n(3560),isPlainObject:n(8630),pick:n(9722),has:n(8721),range:n(6026),uniqueId:n(3955)}}catch(t){}r||(r=window._),t.exports=r},6381:(t,e,n)=>{"use strict";var r=n(8355),i=n(4322);t.exports=function(t,e){var n=t.filter((function(){return!i.select(this).classed("update")}));function a(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}n.attr("transform",a),r.applyTransition(t,e).style("opacity",1).attr("transform",a),r.applyTransition(n.selectAll("rect"),e).attr("width",(function(t){return e.node(t).width})).attr("height",(function(t){return e.node(t).height})).attr("x",(function(t){return-e.node(t).width/2})).attr("y",(function(t){return-e.node(t).height/2}))}},4577:(t,e,n)=>{"use strict";var r=n(8355),i=n(4322),a=n(1034);t.exports=function(t,e){function n(t){var n=e.edge(t);return a.has(n,"x")?"translate("+n.x+","+n.y+")":""}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},4849:(t,e,n)=>{"use strict";var r=n(8355),i=n(4322);t.exports=function(t,e){function n(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},5787:(t,e,n)=>{var r=n(1034),i=n(4322),a=n(1463).layout;t.exports=function(){var t=n(607),e=n(5632),i=n(6315),u=n(940),l=n(4849),h=n(4577),f=n(6381),d=n(4418),p=n(9144),y=function(n,y){!function(t){t.nodes().forEach((function(e){var n=t.node(e);r.has(n,"label")||t.children(e).length||(n.label=e),r.has(n,"paddingX")&&r.defaults(n,{paddingLeft:n.paddingX,paddingRight:n.paddingX}),r.has(n,"paddingY")&&r.defaults(n,{paddingTop:n.paddingY,paddingBottom:n.paddingY}),r.has(n,"padding")&&r.defaults(n,{paddingLeft:n.padding,paddingRight:n.padding,paddingTop:n.padding,paddingBottom:n.padding}),r.defaults(n,o),r.each(["paddingLeft","paddingRight","paddingTop","paddingBottom"],(function(t){n[t]=Number(n[t])})),r.has(n,"width")&&(n._prevWidth=n.width),r.has(n,"height")&&(n._prevHeight=n.height)})),t.edges().forEach((function(e){var n=t.edge(e);r.has(n,"label")||(n.label=""),r.defaults(n,s)}))}(y);var g=c(n,"output"),m=c(g,"clusters"),v=c(g,"edgePaths"),b=i(c(g,"edgeLabels"),y),_=t(c(g,"nodes"),y,d);a(y),l(_,y),h(b,y),u(v,y,p);var x=e(m,y);f(x,y),function(t){r.each(t.nodes(),(function(e){var n=t.node(e);r.has(n,"_prevWidth")?n.width=n._prevWidth:delete n.width,r.has(n,"_prevHeight")?n.height=n._prevHeight:delete n.height,delete n._prevWidth,delete n._prevHeight}))}(y)};return y.createNodes=function(e){return arguments.length?(t=e,y):t},y.createClusters=function(t){return arguments.length?(e=t,y):e},y.createEdgeLabels=function(t){return arguments.length?(i=t,y):i},y.createEdgePaths=function(t){return arguments.length?(u=t,y):u},y.shapes=function(t){return arguments.length?(d=t,y):d},y.arrows=function(t){return arguments.length?(p=t,y):p},y};var o={paddingLeft:10,paddingRight:10,paddingTop:10,paddingBottom:10,rx:0,ry:0,shape:"rect"},s={arrowhead:"normal",curve:i.curveLinear};function c(t,e){var n=t.select("g."+e);return n.empty()&&(n=t.append("g").attr("class",e)),n}},4418:(t,e,n)=>{"use strict";var r=n(8049),i=n(3260),a=n(6587),o=n(5337);t.exports={rect:function(t,e,n){var i=t.insert("rect",":first-child").attr("rx",n.rx).attr("ry",n.ry).attr("x",-e.width/2).attr("y",-e.height/2).attr("width",e.width).attr("height",e.height);return n.intersect=function(t){return r(n,t)},i},ellipse:function(t,e,n){var r=e.width/2,a=e.height/2,o=t.insert("ellipse",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("rx",r).attr("ry",a);return n.intersect=function(t){return i(n,r,a,t)},o},circle:function(t,e,n){var r=Math.max(e.width,e.height)/2,i=t.insert("circle",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("r",r);return n.intersect=function(t){return a(n,r,t)},i},diamond:function(t,e,n){var r=e.width*Math.SQRT2/2,i=e.height*Math.SQRT2/2,a=[{x:0,y:-i},{x:-r,y:0},{x:0,y:i},{x:r,y:0}],s=t.insert("polygon",":first-child").attr("points",a.map((function(t){return t.x+","+t.y})).join(" "));return n.intersect=function(t){return o(n,a,t)},s}}},8355:(t,e,n)=>{var r=n(1034);t.exports={isSubgraph:function(t,e){return!!t.children(e).length},edgeToId:function(t){return a(t.v)+":"+a(t.w)+":"+a(t.name)},applyStyle:function(t,e){e&&t.attr("style",e)},applyClass:function(t,e,n){e&&t.attr("class",e).attr("class",n+" "+t.attr("class"))},applyTransition:function(t,e){var n=e.graph();if(r.isPlainObject(n)){var i=n.transition;if(r.isFunction(i))return i(t)}return t}};var i=/:/g;function a(t){return t?String(t).replace(i,"\\:"):""}},5689:t=>{t.exports="0.6.4"},7188:(t,e,n)=>{"use strict";n.r(e),n.d(e,{FormatSpecifier:()=>uc,active:()=>Jr,arc:()=>fx,area:()=>vx,areaRadial:()=>Sx,ascending:()=>i,autoType:()=>Fo,axisBottom:()=>it,axisLeft:()=>at,axisRight:()=>rt,axisTop:()=>nt,bisect:()=>u,bisectLeft:()=>c,bisectRight:()=>s,bisector:()=>a,blob:()=>ms,brush:()=>Ai,brushSelection:()=>Ei,brushX:()=>Ci,brushY:()=>Si,buffer:()=>bs,chord:()=>Fi,clientPoint:()=>Dn,cluster:()=>Sd,color:()=>Ve,contourDensity:()=>oo,contours:()=>to,create:()=>j_,creator:()=>ie,cross:()=>f,csv:()=>Ts,csvFormat:()=>To,csvFormatBody:()=>Eo,csvFormatRow:()=>So,csvFormatRows:()=>Co,csvFormatValue:()=>Ao,csvParse:()=>wo,csvParseRows:()=>ko,cubehelix:()=>Ha,curveBasis:()=>sw,curveBasisClosed:()=>uw,curveBasisOpen:()=>hw,curveBundle:()=>dw,curveCardinal:()=>gw,curveCardinalClosed:()=>vw,curveCardinalOpen:()=>_w,curveCatmullRom:()=>kw,curveCatmullRomClosed:()=>Ew,curveCatmullRomOpen:()=>Sw,curveLinear:()=>px,curveLinearClosed:()=>Mw,curveMonotoneX:()=>Fw,curveMonotoneY:()=>Pw,curveNatural:()=>zw,curveStep:()=>qw,curveStepAfter:()=>$w,curveStepBefore:()=>Hw,customEvent:()=>ge,descending:()=>d,deviation:()=>g,dispatch:()=>ft,drag:()=>po,dragDisable:()=>Se,dragEnable:()=>Ae,dsv:()=>ks,dsvFormat:()=>_o,easeBack:()=>hs,easeBackIn:()=>us,easeBackInOut:()=>hs,easeBackOut:()=>ls,easeBounce:()=>os,easeBounceIn:()=>as,easeBounceInOut:()=>ss,easeBounceOut:()=>os,easeCircle:()=>rs,easeCircleIn:()=>es,easeCircleInOut:()=>rs,easeCircleOut:()=>ns,easeCubic:()=>Xr,easeCubicIn:()=>Vr,easeCubicInOut:()=>Xr,easeCubicOut:()=>Gr,easeElastic:()=>ps,easeElasticIn:()=>ds,easeElasticInOut:()=>ys,easeElasticOut:()=>ps,easeExp:()=>ts,easeExpIn:()=>Ko,easeExpInOut:()=>ts,easeExpOut:()=>Jo,easeLinear:()=>jo,easePoly:()=>$o,easePolyIn:()=>qo,easePolyInOut:()=>$o,easePolyOut:()=>Ho,easeQuad:()=>Uo,easeQuadIn:()=>Yo,easeQuadInOut:()=>Uo,easeQuadOut:()=>zo,easeSin:()=>Zo,easeSinIn:()=>Go,easeSinInOut:()=>Zo,easeSinOut:()=>Xo,entries:()=>pa,event:()=>le,extent:()=>m,forceCenter:()=>Bs,forceCollide:()=>Ws,forceLink:()=>Xs,forceManyBody:()=>tc,forceRadial:()=>ec,forceSimulation:()=>Js,forceX:()=>nc,forceY:()=>rc,format:()=>pc,formatDefaultLocale:()=>bc,formatLocale:()=>vc,formatPrefix:()=>yc,formatSpecifier:()=>cc,geoAlbers:()=>Uf,geoAlbersUsa:()=>qf,geoArea:()=>yu,geoAzimuthalEqualArea:()=>Vf,geoAzimuthalEqualAreaRaw:()=>Wf,geoAzimuthalEquidistant:()=>Xf,geoAzimuthalEquidistantRaw:()=>Gf,geoBounds:()=>sl,geoCentroid:()=>bl,geoCircle:()=>Nl,geoClipAntimeridian:()=>Ul,geoClipCircle:()=>ql,geoClipExtent:()=>Vl,geoClipRectangle:()=>Wl,geoConicConformal:()=>ed,geoConicConformalRaw:()=>td,geoConicEqualArea:()=>zf,geoConicEqualAreaRaw:()=>Yf,geoConicEquidistant:()=>ad,geoConicEquidistantRaw:()=>id,geoContains:()=>ph,geoDistance:()=>ah,geoEqualEarth:()=>fd,geoEqualEarthRaw:()=>hd,geoEquirectangular:()=>rd,geoEquirectangularRaw:()=>nd,geoGnomonic:()=>pd,geoGnomonicRaw:()=>dd,geoGraticule:()=>mh,geoGraticule10:()=>vh,geoIdentity:()=>yd,geoInterpolate:()=>bh,geoLength:()=>nh,geoMercator:()=>Qf,geoMercatorRaw:()=>Zf,geoNaturalEarth1:()=>md,geoNaturalEarth1Raw:()=>gd,geoOrthographic:()=>bd,geoOrthographicRaw:()=>vd,geoPath:()=>kf,geoProjection:()=>Ff,geoProjectionMutator:()=>Pf,geoRotation:()=>Sl,geoStereographic:()=>xd,geoStereographicRaw:()=>_d,geoStream:()=>nu,geoTransform:()=>Tf,geoTransverseMercator:()=>kd,geoTransverseMercatorRaw:()=>wd,gray:()=>ka,hcl:()=>Oa,hierarchy:()=>Md,histogram:()=>D,hsl:()=>an,html:()=>Ds,image:()=>Cs,interpolate:()=>Mn,interpolateArray:()=>xn,interpolateBasis:()=>un,interpolateBasisClosed:()=>ln,interpolateBlues:()=>f_,interpolateBrBG:()=>Tb,interpolateBuGn:()=>Ub,interpolateBuPu:()=>Hb,interpolateCividis:()=>k_,interpolateCool:()=>C_,interpolateCubehelix:()=>zp,interpolateCubehelixDefault:()=>T_,interpolateCubehelixLong:()=>Up,interpolateDate:()=>kn,interpolateDiscrete:()=>Sp,interpolateGnBu:()=>Wb,interpolateGreens:()=>p_,interpolateGreys:()=>g_,interpolateHcl:()=>Pp,interpolateHclLong:()=>jp,interpolateHsl:()=>Lp,interpolateHslLong:()=>Ip,interpolateHue:()=>Ap,interpolateInferno:()=>F_,interpolateLab:()=>Rp,interpolateMagma:()=>R_,interpolateNumber:()=>Tn,interpolateNumberArray:()=>bn,interpolateObject:()=>En,interpolateOrRd:()=>Gb,interpolateOranges:()=>w_,interpolatePRGn:()=>Cb,interpolatePiYG:()=>Ab,interpolatePlasma:()=>P_,interpolatePuBu:()=>Kb,interpolatePuBuGn:()=>Zb,interpolatePuOr:()=>Nb,interpolatePuRd:()=>t_,interpolatePurples:()=>v_,interpolateRainbow:()=>A_,interpolateRdBu:()=>Ob,interpolateRdGy:()=>Lb,interpolateRdPu:()=>n_,interpolateRdYlBu:()=>Rb,interpolateRdYlGn:()=>Pb,interpolateReds:()=>__,interpolateRgb:()=>yn,interpolateRgbBasis:()=>mn,interpolateRgbBasisClosed:()=>vn,interpolateRound:()=>Mp,interpolateSinebow:()=>O_,interpolateSpectral:()=>Yb,interpolateString:()=>An,interpolateTransformCss:()=>pr,interpolateTransformSvg:()=>yr,interpolateTurbo:()=>B_,interpolateViridis:()=>I_,interpolateWarm:()=>E_,interpolateYlGn:()=>o_,interpolateYlGnBu:()=>i_,interpolateYlOrBr:()=>c_,interpolateYlOrRd:()=>l_,interpolateZoom:()=>Op,interrupt:()=>ar,interval:()=>fk,isoFormat:()=>uk,isoParse:()=>hk,json:()=>As,keys:()=>fa,lab:()=>Ta,lch:()=>Da,line:()=>mx,lineRadial:()=>Cx,linkHorizontal:()=>Rx,linkRadial:()=>Px,linkVertical:()=>Fx,local:()=>z_,map:()=>na,matcher:()=>mt,max:()=>I,mean:()=>R,median:()=>F,merge:()=>P,min:()=>j,mouse:()=>Bn,namespace:()=>Et,namespaces:()=>Tt,nest:()=>ra,now:()=>Hn,pack:()=>tp,packEnclose:()=>Id,packSiblings:()=>Gd,pairs:()=>l,partition:()=>op,path:()=>Wi,permute:()=>Y,pie:()=>xx,piecewise:()=>qp,pointRadial:()=>Ax,polygonArea:()=>$p,polygonCentroid:()=>Wp,polygonContains:()=>Qp,polygonHull:()=>Zp,polygonLength:()=>Kp,precisionFixed:()=>_c,precisionPrefix:()=>xc,precisionRound:()=>wc,quadtree:()=>Ys,quantile:()=>O,quantize:()=>Hp,radialArea:()=>Sx,radialLine:()=>Cx,randomBates:()=>iy,randomExponential:()=>ay,randomIrwinHall:()=>ry,randomLogNormal:()=>ny,randomNormal:()=>ey,randomUniform:()=>ty,range:()=>k,rgb:()=>Qe,ribbon:()=>Ki,scaleBand:()=>dy,scaleDiverging:()=>ob,scaleDivergingLog:()=>sb,scaleDivergingPow:()=>ub,scaleDivergingSqrt:()=>lb,scaleDivergingSymlog:()=>cb,scaleIdentity:()=>My,scaleImplicit:()=>hy,scaleLinear:()=>Ay,scaleLog:()=>Py,scaleOrdinal:()=>fy,scalePoint:()=>yy,scalePow:()=>Vy,scaleQuantile:()=>Xy,scaleQuantize:()=>Zy,scaleSequential:()=>Jv,scaleSequentialLog:()=>tb,scaleSequentialPow:()=>nb,scaleSequentialQuantile:()=>ib,scaleSequentialSqrt:()=>rb,scaleSequentialSymlog:()=>eb,scaleSqrt:()=>Gy,scaleSymlog:()=>Uy,scaleThreshold:()=>Qy,scaleTime:()=>Yv,scaleUtc:()=>Zv,scan:()=>z,schemeAccent:()=>db,schemeBlues:()=>h_,schemeBrBG:()=>kb,schemeBuGn:()=>zb,schemeBuPu:()=>qb,schemeCategory10:()=>fb,schemeDark2:()=>pb,schemeGnBu:()=>$b,schemeGreens:()=>d_,schemeGreys:()=>y_,schemeOrRd:()=>Vb,schemeOranges:()=>x_,schemePRGn:()=>Eb,schemePaired:()=>yb,schemePastel1:()=>gb,schemePastel2:()=>mb,schemePiYG:()=>Sb,schemePuBu:()=>Qb,schemePuBuGn:()=>Xb,schemePuOr:()=>Mb,schemePuRd:()=>Jb,schemePurples:()=>m_,schemeRdBu:()=>Db,schemeRdGy:()=>Bb,schemeRdPu:()=>e_,schemeRdYlBu:()=>Ib,schemeRdYlGn:()=>Fb,schemeReds:()=>b_,schemeSet1:()=>vb,schemeSet2:()=>bb,schemeSet3:()=>_b,schemeSpectral:()=>jb,schemeTableau10:()=>xb,schemeYlGn:()=>a_,schemeYlGnBu:()=>r_,schemeYlOrBr:()=>s_,schemeYlOrRd:()=>u_,select:()=>Te,selectAll:()=>q_,selection:()=>ke,selector:()=>pt,selectorAll:()=>gt,set:()=>ha,shuffle:()=>U,stack:()=>Xw,stackOffsetDiverging:()=>Qw,stackOffsetExpand:()=>Zw,stackOffsetNone:()=>Ww,stackOffsetSilhouette:()=>Kw,stackOffsetWiggle:()=>Jw,stackOrderAppearance:()=>tk,stackOrderAscending:()=>nk,stackOrderDescending:()=>ik,stackOrderInsideOut:()=>ak,stackOrderNone:()=>Vw,stackOrderReverse:()=>ok,stratify:()=>hp,style:()=>Rt,sum:()=>q,svg:()=>Os,symbol:()=>rw,symbolCircle:()=>jx,symbolCross:()=>Yx,symbolDiamond:()=>qx,symbolSquare:()=>Gx,symbolStar:()=>Vx,symbolTriangle:()=>Zx,symbolWye:()=>ew,symbols:()=>nw,text:()=>xs,thresholdFreedmanDiaconis:()=>B,thresholdScott:()=>L,thresholdSturges:()=>N,tickFormat:()=>Cy,tickIncrement:()=>A,tickStep:()=>M,ticks:()=>S,timeDay:()=>Ag,timeDays:()=>Mg,timeFormat:()=>pm,timeFormatDefaultLocale:()=>Iv,timeFormatLocale:()=>fm,timeFriday:()=>vg,timeFridays:()=>Eg,timeHour:()=>Dg,timeHours:()=>Og,timeInterval:()=>tg,timeMillisecond:()=>Yg,timeMilliseconds:()=>zg,timeMinute:()=>Lg,timeMinutes:()=>Ig,timeMonday:()=>pg,timeMondays:()=>xg,timeMonth:()=>ag,timeMonths:()=>og,timeParse:()=>ym,timeSaturday:()=>bg,timeSaturdays:()=>Cg,timeSecond:()=>Fg,timeSeconds:()=>Pg,timeSunday:()=>dg,timeSundays:()=>_g,timeThursday:()=>mg,timeThursdays:()=>Tg,timeTuesday:()=>yg,timeTuesdays:()=>wg,timeWednesday:()=>gg,timeWednesdays:()=>kg,timeWeek:()=>dg,timeWeeks:()=>_g,timeYear:()=>ng,timeYears:()=>rg,timeout:()=>Kn,timer:()=>Vn,timerFlush:()=>Gn,touch:()=>On,touches:()=>H_,transition:()=>Hr,transpose:()=>H,tree:()=>vp,treemap:()=>kp,treemapBinary:()=>Tp,treemapDice:()=>ap,treemapResquarify:()=>Cp,treemapSlice:()=>bp,treemapSliceDice:()=>Ep,treemapSquarify:()=>wp,tsv:()=>Es,tsvFormat:()=>Oo,tsvFormatBody:()=>Bo,tsvFormatRow:()=>Io,tsvFormatRows:()=>Lo,tsvFormatValue:()=>Ro,tsvParse:()=>No,tsvParseRows:()=>Do,utcDay:()=>im,utcDays:()=>am,utcFormat:()=>gm,utcFriday:()=>Gg,utcFridays:()=>em,utcHour:()=>$v,utcHours:()=>Wv,utcMillisecond:()=>Yg,utcMilliseconds:()=>zg,utcMinute:()=>Gv,utcMinutes:()=>Xv,utcMonday:()=>Hg,utcMondays:()=>Qg,utcMonth:()=>Uv,utcMonths:()=>qv,utcParse:()=>mm,utcSaturday:()=>Xg,utcSaturdays:()=>nm,utcSecond:()=>Fg,utcSeconds:()=>Pg,utcSunday:()=>qg,utcSundays:()=>Zg,utcThursday:()=>Vg,utcThursdays:()=>tm,utcTuesday:()=>$g,utcTuesdays:()=>Kg,utcWednesday:()=>Wg,utcWednesdays:()=>Jg,utcWeek:()=>qg,utcWeeks:()=>Zg,utcYear:()=>sm,utcYears:()=>cm,values:()=>da,variance:()=>y,version:()=>r,voronoi:()=>Kk,window:()=>Ot,xml:()=>Ns,zip:()=>W,zoom:()=>fT,zoomIdentity:()=>nT,zoomTransform:()=>rT});var r="5.16.0";function i(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}function a(t){var e;return 1===t.length&&(e=t,t=function(t,n){return i(e(t),n)}),{left:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)<0?r=a+1:i=a}return r},right:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)>0?i=a:r=a+1}return r}}}var o=a(i),s=o.right,c=o.left;const u=s;function l(t,e){null==e&&(e=h);for(var n=0,r=t.length-1,i=t[0],a=new Array(r<0?0:r);n<r;)a[n]=e(i,i=t[++n]);return a}function h(t,e){return[t,e]}function f(t,e,n){var r,i,a,o,s=t.length,c=e.length,u=new Array(s*c);for(null==n&&(n=h),r=a=0;r<s;++r)for(o=t[r],i=0;i<c;++i,++a)u[a]=n(o,e[i]);return u}function d(t,e){return e<t?-1:e>t?1:e>=t?0:NaN}function p(t){return null===t?NaN:+t}function y(t,e){var n,r,i=t.length,a=0,o=-1,s=0,c=0;if(null==e)for(;++o<i;)isNaN(n=p(t[o]))||(c+=(r=n-s)*(n-(s+=r/++a)));else for(;++o<i;)isNaN(n=p(e(t[o],o,t)))||(c+=(r=n-s)*(n-(s+=r/++a)));if(a>1)return c/(a-1)}function g(t,e){var n=y(t,e);return n?Math.sqrt(n):n}function m(t,e){var n,r,i,a=t.length,o=-1;if(null==e){for(;++o<a;)if(null!=(n=t[o])&&n>=n)for(r=i=n;++o<a;)null!=(n=t[o])&&(r>n&&(r=n),i<n&&(i=n))}else for(;++o<a;)if(null!=(n=e(t[o],o,t))&&n>=n)for(r=i=n;++o<a;)null!=(n=e(t[o],o,t))&&(r>n&&(r=n),i<n&&(i=n));return[r,i]}var v=Array.prototype,b=v.slice,_=v.map;function x(t){return function(){return t}}function w(t){return t}function k(t,e,n){t=+t,e=+e,n=(i=arguments.length)<2?(e=t,t=0,1):i<3?1:+n;for(var r=-1,i=0|Math.max(0,Math.ceil((e-t)/n)),a=new Array(i);++r<i;)a[r]=t+r*n;return a}var T=Math.sqrt(50),E=Math.sqrt(10),C=Math.sqrt(2);function S(t,e,n){var r,i,a,o,s=-1;if(n=+n,(t=+t)==(e=+e)&&n>0)return[t];if((r=e<t)&&(i=t,t=e,e=i),0===(o=A(t,e,n))||!isFinite(o))return[];if(o>0)for(t=Math.ceil(t/o),e=Math.floor(e/o),a=new Array(i=Math.ceil(e-t+1));++s<i;)a[s]=(t+s)*o;else for(t=Math.floor(t*o),e=Math.ceil(e*o),a=new Array(i=Math.ceil(t-e+1));++s<i;)a[s]=(t-s)/o;return r&&a.reverse(),a}function A(t,e,n){var r=(e-t)/Math.max(0,n),i=Math.floor(Math.log(r)/Math.LN10),a=r/Math.pow(10,i);return i>=0?(a>=T?10:a>=E?5:a>=C?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=T?10:a>=E?5:a>=C?2:1)}function M(t,e,n){var r=Math.abs(e-t)/Math.max(0,n),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),a=r/i;return a>=T?i*=10:a>=E?i*=5:a>=C&&(i*=2),e<t?-i:i}function N(t){return Math.ceil(Math.log(t.length)/Math.LN2)+1}function D(){var t=w,e=m,n=N;function r(r){var i,a,o=r.length,s=new Array(o);for(i=0;i<o;++i)s[i]=t(r[i],i,r);var c=e(s),l=c[0],h=c[1],f=n(s,l,h);Array.isArray(f)||(f=M(l,h,f),f=k(Math.ceil(l/f)*f,h,f));for(var d=f.length;f[0]<=l;)f.shift(),--d;for(;f[d-1]>h;)f.pop(),--d;var p,y=new Array(d+1);for(i=0;i<=d;++i)(p=y[i]=[]).x0=i>0?f[i-1]:l,p.x1=i<d?f[i]:h;for(i=0;i<o;++i)l<=(a=s[i])&&a<=h&&y[u(f,a,0,d)].push(r[i]);return y}return r.value=function(e){return arguments.length?(t="function"==typeof e?e:x(e),r):t},r.domain=function(t){return arguments.length?(e="function"==typeof t?t:x([t[0],t[1]]),r):e},r.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?x(b.call(t)):x(t),r):n},r}function O(t,e,n){if(null==n&&(n=p),r=t.length){if((e=+e)<=0||r<2)return+n(t[0],0,t);if(e>=1)return+n(t[r-1],r-1,t);var r,i=(r-1)*e,a=Math.floor(i),o=+n(t[a],a,t);return o+(+n(t[a+1],a+1,t)-o)*(i-a)}}function B(t,e,n){return t=_.call(t,p).sort(i),Math.ceil((n-e)/(2*(O(t,.75)-O(t,.25))*Math.pow(t.length,-1/3)))}function L(t,e,n){return Math.ceil((n-e)/(3.5*g(t)*Math.pow(t.length,-1/3)))}function I(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&n>r&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&n>r&&(r=n);return r}function R(t,e){var n,r=t.length,i=r,a=-1,o=0;if(null==e)for(;++a<r;)isNaN(n=p(t[a]))?--i:o+=n;else for(;++a<r;)isNaN(n=p(e(t[a],a,t)))?--i:o+=n;if(i)return o/i}function F(t,e){var n,r=t.length,a=-1,o=[];if(null==e)for(;++a<r;)isNaN(n=p(t[a]))||o.push(n);else for(;++a<r;)isNaN(n=p(e(t[a],a,t)))||o.push(n);return O(o.sort(i),.5)}function P(t){for(var e,n,r,i=t.length,a=-1,o=0;++a<i;)o+=t[a].length;for(n=new Array(o);--i>=0;)for(e=(r=t[i]).length;--e>=0;)n[--o]=r[e];return n}function j(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&r>n&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&r>n&&(r=n);return r}function Y(t,e){for(var n=e.length,r=new Array(n);n--;)r[n]=t[e[n]];return r}function z(t,e){if(n=t.length){var n,r,a=0,o=0,s=t[o];for(null==e&&(e=i);++a<n;)(e(r=t[a],s)<0||0!==e(s,s))&&(s=r,o=a);return 0===e(s,s)?o:void 0}}function U(t,e,n){for(var r,i,a=(null==n?t.length:n)-(e=null==e?0:+e);a;)i=Math.random()*a--|0,r=t[a+e],t[a+e]=t[i+e],t[i+e]=r;return t}function q(t,e){var n,r=t.length,i=-1,a=0;if(null==e)for(;++i<r;)(n=+t[i])&&(a+=n);else for(;++i<r;)(n=+e(t[i],i,t))&&(a+=n);return a}function H(t){if(!(i=t.length))return[];for(var e=-1,n=j(t,$),r=new Array(n);++e<n;)for(var i,a=-1,o=r[e]=new Array(i);++a<i;)o[a]=t[a][e];return r}function $(t){return t.length}function W(){return H(arguments)}var V=Array.prototype.slice;function G(t){return t}var X=1e-6;function Z(t){return"translate("+(t+.5)+",0)"}function Q(t){return"translate(0,"+(t+.5)+")"}function K(t){return function(e){return+t(e)}}function J(t){var e=Math.max(0,t.bandwidth()-1)/2;return t.round()&&(e=Math.round(e)),function(n){return+t(n)+e}}function tt(){return!this.__axis}function et(t,e){var n=[],r=null,i=null,a=6,o=6,s=3,c=1===t||4===t?-1:1,u=4===t||2===t?"x":"y",l=1===t||3===t?Z:Q;function h(h){var f=null==r?e.ticks?e.ticks.apply(e,n):e.domain():r,d=null==i?e.tickFormat?e.tickFormat.apply(e,n):G:i,p=Math.max(a,0)+s,y=e.range(),g=+y[0]+.5,m=+y[y.length-1]+.5,v=(e.bandwidth?J:K)(e.copy()),b=h.selection?h.selection():h,_=b.selectAll(".domain").data([null]),x=b.selectAll(".tick").data(f,e).order(),w=x.exit(),k=x.enter().append("g").attr("class","tick"),T=x.select("line"),E=x.select("text");_=_.merge(_.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),x=x.merge(k),T=T.merge(k.append("line").attr("stroke","currentColor").attr(u+"2",c*a)),E=E.merge(k.append("text").attr("fill","currentColor").attr(u,c*p).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==b&&(_=_.transition(h),x=x.transition(h),T=T.transition(h),E=E.transition(h),w=w.transition(h).attr("opacity",X).attr("transform",(function(t){return isFinite(t=v(t))?l(t):this.getAttribute("transform")})),k.attr("opacity",X).attr("transform",(function(t){var e=this.parentNode.__axis;return l(e&&isFinite(e=e(t))?e:v(t))}))),w.remove(),_.attr("d",4===t||2==t?o?"M"+c*o+","+g+"H0.5V"+m+"H"+c*o:"M0.5,"+g+"V"+m:o?"M"+g+","+c*o+"V0.5H"+m+"V"+c*o:"M"+g+",0.5H"+m),x.attr("opacity",1).attr("transform",(function(t){return l(v(t))})),T.attr(u+"2",c*a),E.attr(u,c*p).text(d),b.filter(tt).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),b.each((function(){this.__axis=v}))}return h.scale=function(t){return arguments.length?(e=t,h):e},h.ticks=function(){return n=V.call(arguments),h},h.tickArguments=function(t){return arguments.length?(n=null==t?[]:V.call(t),h):n.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:V.call(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(a=o=+t,h):a},h.tickSizeInner=function(t){return arguments.length?(a=+t,h):a},h.tickSizeOuter=function(t){return arguments.length?(o=+t,h):o},h.tickPadding=function(t){return arguments.length?(s=+t,h):s},h}function nt(t){return et(1,t)}function rt(t){return et(2,t)}function it(t){return et(3,t)}function at(t){return et(4,t)}var ot={value:function(){}};function st(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new ct(r)}function ct(t){this._=t}function ut(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function lt(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function ht(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=ot,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}ct.prototype=st.prototype={constructor:ct,on:function(t,e){var n,r=this._,i=ut(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=ht(r[n],t.name,e);else if(null==e)for(n in r)r[n]=ht(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=lt(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new ct(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};const ft=st;function dt(){}function pt(t){return null==t?dt:function(){return this.querySelector(t)}}function yt(){return[]}function gt(t){return null==t?yt:function(){return this.querySelectorAll(t)}}function mt(t){return function(){return this.matches(t)}}function vt(t){return new Array(t.length)}function bt(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}function _t(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new bt(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function xt(t,e,n,r,i,a,o){var s,c,u,l={},h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u="$"+o.call(c,c.__data__,s,e),u in l?i[s]=c:l[u]=c);for(s=0;s<f;++s)(c=l[u="$"+o.call(t,a[s],s,a)])?(r[s]=c,c.__data__=a[s],l[u]=null):n[s]=new bt(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l[d[s]]===c&&(i[s]=c)}function wt(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}bt.prototype={constructor:bt,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var kt="http://www.w3.org/1999/xhtml";const Tt={svg:"http://www.w3.org/2000/svg",xhtml:kt,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Et(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),Tt.hasOwnProperty(e)?{space:Tt[e],local:t}:t}function Ct(t){return function(){this.removeAttribute(t)}}function St(t){return function(){this.removeAttributeNS(t.space,t.local)}}function At(t,e){return function(){this.setAttribute(t,e)}}function Mt(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function Nt(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function Dt(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function Ot(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function Bt(t){return function(){this.style.removeProperty(t)}}function Lt(t,e,n){return function(){this.style.setProperty(t,e,n)}}function It(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function Rt(t,e){return t.style.getPropertyValue(e)||Ot(t).getComputedStyle(t,null).getPropertyValue(e)}function Ft(t){return function(){delete this[t]}}function Pt(t,e){return function(){this[t]=e}}function jt(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function Yt(t){return t.trim().split(/^|\s+/)}function zt(t){return t.classList||new Ut(t)}function Ut(t){this._node=t,this._names=Yt(t.getAttribute("class")||"")}function qt(t,e){for(var n=zt(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function Ht(t,e){for(var n=zt(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function $t(t){return function(){qt(this,t)}}function Wt(t){return function(){Ht(this,t)}}function Vt(t,e){return function(){(e.apply(this,arguments)?qt:Ht)(this,t)}}function Gt(){this.textContent=""}function Xt(t){return function(){this.textContent=t}}function Zt(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function Qt(){this.innerHTML=""}function Kt(t){return function(){this.innerHTML=t}}function Jt(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function te(){this.nextSibling&&this.parentNode.appendChild(this)}function ee(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function ne(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===kt&&e.documentElement.namespaceURI===kt?e.createElement(t):e.createElementNS(n,t)}}function re(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function ie(t){var e=Et(t);return(e.local?re:ne)(e)}function ae(){return null}function oe(){var t=this.parentNode;t&&t.removeChild(this)}function se(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function ce(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}Ut.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var ue={},le=null;function he(t,e,n){return t=fe(t,e,n),function(e){var n=e.relatedTarget;n&&(n===this||8&n.compareDocumentPosition(this))||t.call(this,e)}}function fe(t,e,n){return function(r){var i=le;le=r;try{t.call(this,this.__data__,e,n)}finally{le=i}}}function de(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function pe(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.capture);++i?e.length=i:delete this.__on}}}function ye(t,e,n){var r=ue.hasOwnProperty(t.type)?he:fe;return function(i,a,o){var s,c=this.__on,u=r(e,a,o);if(c)for(var l=0,h=c.length;l<h;++l)if((s=c[l]).type===t.type&&s.name===t.name)return this.removeEventListener(s.type,s.listener,s.capture),this.addEventListener(s.type,s.listener=u,s.capture=n),void(s.value=e);this.addEventListener(t.type,u,n),s={type:t.type,name:t.name,value:e,listener:u,capture:n},c?c.push(s):this.__on=[s]}}function ge(t,e,n,r){var i=le;t.sourceEvent=le,le=t;try{return e.apply(n,r)}finally{le=i}}function me(t,e,n){var r=Ot(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function ve(t,e){return function(){return me(this,t,e)}}function be(t,e){return function(){return me(this,t,e.apply(this,arguments))}}"undefined"!=typeof document&&("onmouseenter"in document.documentElement||(ue={mouseenter:"mouseover",mouseleave:"mouseout"}));var _e=[null];function xe(t,e){this._groups=t,this._parents=e}function we(){return new xe([[document.documentElement]],_e)}xe.prototype=we.prototype={constructor:xe,select:function(t){"function"!=typeof t&&(t=pt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new xe(r,this._parents)},selectAll:function(t){"function"!=typeof t&&(t=gt(t));for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new xe(r,i)},filter:function(t){"function"!=typeof t&&(t=mt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new xe(r,this._parents)},data:function(t,e){if(!t)return p=new Array(this.size()),l=-1,this.each((function(t){p[++l]=t})),p;var n,r=e?xt:_t,i=this._parents,a=this._groups;"function"!=typeof t&&(n=t,t=function(){return n});for(var o=a.length,s=new Array(o),c=new Array(o),u=new Array(o),l=0;l<o;++l){var h=i[l],f=a[l],d=f.length,p=t.call(h,h&&h.__data__,l,i),y=p.length,g=c[l]=new Array(y),m=s[l]=new Array(y);r(h,f,g,m,u[l]=new Array(d),p,e);for(var v,b,_=0,x=0;_<y;++_)if(v=g[_]){for(_>=x&&(x=_+1);!(b=m[x])&&++x<y;);v._next=b||null}}return(s=new xe(s,i))._enter=c,s._exit=u,s},enter:function(){return new xe(this._enter||this._groups.map(vt),this._parents)},exit:function(){return new xe(this._exit||this._groups.map(vt),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return r="function"==typeof t?t(r):r.append(t+""),null!=e&&(i=e(i)),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new xe(o,this._parents)},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=wt);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new xe(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){var t=new Array(this.size()),e=-1;return this.each((function(){t[++e]=this})),t},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){var t=0;return this.each((function(){++t})),t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=Et(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?St:Ct:"function"==typeof e?n.local?Dt:Nt:n.local?Mt:At)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?Bt:"function"==typeof e?It:Lt)(t,e,null==n?"":n)):Rt(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?Ft:"function"==typeof e?jt:Pt)(t,e)):this.node()[t]},classed:function(t,e){var n=Yt(t+"");if(arguments.length<2){for(var r=zt(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?Vt:e?$t:Wt)(n,e))},text:function(t){return arguments.length?this.each(null==t?Gt:("function"==typeof t?Zt:Xt)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?Qt:("function"==typeof t?Jt:Kt)(t)):this.node().innerHTML},raise:function(){return this.each(te)},lower:function(){return this.each(ee)},append:function(t){var e="function"==typeof t?t:ie(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:ie(t),r=null==e?ae:"function"==typeof e?e:pt(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(oe)},clone:function(t){return this.select(t?ce:se)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=de(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?ye:pe,null==n&&(n=!1),r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?be:ve)(t,e))}};const ke=we;function Te(t){return"string"==typeof t?new xe([[document.querySelector(t)]],[document.documentElement]):new xe([[t]],_e)}function Ee(){le.stopImmediatePropagation()}function Ce(){le.preventDefault(),le.stopImmediatePropagation()}function Se(t){var e=t.document.documentElement,n=Te(t).on("dragstart.drag",Ce,!0);"onselectstart"in e?n.on("selectstart.drag",Ce,!0):(e.__noselect=e.style.MozUserSelect,e.style.MozUserSelect="none")}function Ae(t,e){var n=t.document.documentElement,r=Te(t).on("dragstart.drag",null);e&&(r.on("click.drag",Ce,!0),setTimeout((function(){r.on("click.drag",null)}),0)),"onselectstart"in n?r.on("selectstart.drag",null):(n.style.MozUserSelect=n.__noselect,delete n.__noselect)}function Me(t,e,n){t.prototype=e.prototype=n,n.constructor=t}function Ne(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function De(){}var Oe=.7,Be=1/Oe,Le="\\s*([+-]?\\d+)\\s*",Ie="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Re="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Fe=/^#([0-9a-f]{3,8})$/,Pe=new RegExp("^rgb\\("+[Le,Le,Le]+"\\)$"),je=new RegExp("^rgb\\("+[Re,Re,Re]+"\\)$"),Ye=new RegExp("^rgba\\("+[Le,Le,Le,Ie]+"\\)$"),ze=new RegExp("^rgba\\("+[Re,Re,Re,Ie]+"\\)$"),Ue=new RegExp("^hsl\\("+[Ie,Re,Re]+"\\)$"),qe=new RegExp("^hsla\\("+[Ie,Re,Re,Ie]+"\\)$"),He={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function $e(){return this.rgb().formatHex()}function We(){return this.rgb().formatRgb()}function Ve(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Fe.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?Ge(e):3===n?new Ke(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?Xe(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?Xe(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Pe.exec(t))?new Ke(e[1],e[2],e[3],1):(e=je.exec(t))?new Ke(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Ye.exec(t))?Xe(e[1],e[2],e[3],e[4]):(e=ze.exec(t))?Xe(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=Ue.exec(t))?nn(e[1],e[2]/100,e[3]/100,1):(e=qe.exec(t))?nn(e[1],e[2]/100,e[3]/100,e[4]):He.hasOwnProperty(t)?Ge(He[t]):"transparent"===t?new Ke(NaN,NaN,NaN,0):null}function Ge(t){return new Ke(t>>16&255,t>>8&255,255&t,1)}function Xe(t,e,n,r){return r<=0&&(t=e=n=NaN),new Ke(t,e,n,r)}function Ze(t){return t instanceof De||(t=Ve(t)),t?new Ke((t=t.rgb()).r,t.g,t.b,t.opacity):new Ke}function Qe(t,e,n,r){return 1===arguments.length?Ze(t):new Ke(t,e,n,null==r?1:r)}function Ke(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function Je(){return"#"+en(this.r)+en(this.g)+en(this.b)}function tn(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function en(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function nn(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new on(t,e,n,r)}function rn(t){if(t instanceof on)return new on(t.h,t.s,t.l,t.opacity);if(t instanceof De||(t=Ve(t)),!t)return new on;if(t instanceof on)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new on(o,s,c,t.opacity)}function an(t,e,n,r){return 1===arguments.length?rn(t):new on(t,e,n,null==r?1:r)}function on(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function sn(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function cn(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}function un(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return cn((n-r/e)*e,o,i,a,s)}}function ln(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return cn((n-r/e)*e,i,a,o,s)}}function hn(t){return function(){return t}}function fn(t,e){return function(n){return t+n*e}}function dn(t,e){var n=e-t;return n?fn(t,n>180||n<-180?n-360*Math.round(n/360):n):hn(isNaN(t)?e:t)}function pn(t,e){var n=e-t;return n?fn(t,n):hn(isNaN(t)?e:t)}Me(De,Ve,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:$e,formatHex:$e,formatHsl:function(){return rn(this).formatHsl()},formatRgb:We,toString:We}),Me(Ke,Qe,Ne(De,{brighter:function(t){return t=null==t?Be:Math.pow(Be,t),new Ke(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Oe:Math.pow(Oe,t),new Ke(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Je,formatHex:Je,formatRgb:tn,toString:tn})),Me(on,an,Ne(De,{brighter:function(t){return t=null==t?Be:Math.pow(Be,t),new on(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Oe:Math.pow(Oe,t),new on(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new Ke(sn(t>=240?t-240:t+120,i,r),sn(t,i,r),sn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const yn=function t(e){var n=function(t){return 1==(t=+t)?pn:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):hn(isNaN(e)?n:e)}}(e);function r(t,e){var r=n((t=Qe(t)).r,(e=Qe(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=pn(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function gn(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=Qe(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}var mn=gn(un),vn=gn(ln);function bn(t,e){e||(e=[]);var n,r=t?Math.min(e.length,t.length):0,i=e.slice();return function(a){for(n=0;n<r;++n)i[n]=t[n]*(1-a)+e[n]*a;return i}}function _n(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}function xn(t,e){return(_n(e)?bn:wn)(t,e)}function wn(t,e){var n,r=e?e.length:0,i=t?Math.min(r,t.length):0,a=new Array(i),o=new Array(r);for(n=0;n<i;++n)a[n]=Mn(t[n],e[n]);for(;n<r;++n)o[n]=e[n];return function(t){for(n=0;n<i;++n)o[n]=a[n](t);return o}}function kn(t,e){var n=new Date;return t=+t,e=+e,function(r){return n.setTime(t*(1-r)+e*r),n}}function Tn(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}function En(t,e){var n,r={},i={};for(n in null!==t&&"object"==typeof t||(t={}),null!==e&&"object"==typeof e||(e={}),e)n in t?r[n]=Mn(t[n],e[n]):i[n]=e[n];return function(t){for(n in r)i[n]=r[n](t);return i}}var Cn=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Sn=new RegExp(Cn.source,"g");function An(t,e){var n,r,i,a=Cn.lastIndex=Sn.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=Cn.exec(t))&&(r=Sn.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:Tn(n,r)})),a=Sn.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})}function Mn(t,e){var n,r=typeof e;return null==e||"boolean"===r?hn(e):("number"===r?Tn:"string"===r?(n=Ve(e))?(e=n,yn):An:e instanceof Ve?yn:e instanceof Date?kn:_n(e)?bn:Array.isArray(e)?wn:"function"!=typeof e.valueOf&&"function"!=typeof e.toString||isNaN(e)?En:Tn)(t,e)}function Nn(){for(var t,e=le;t=e.sourceEvent;)e=t;return e}function Dn(t,e){var n=t.ownerSVGElement||t;if(n.createSVGPoint){var r=n.createSVGPoint();return r.x=e.clientX,r.y=e.clientY,[(r=r.matrixTransform(t.getScreenCTM().inverse())).x,r.y]}var i=t.getBoundingClientRect();return[e.clientX-i.left-t.clientLeft,e.clientY-i.top-t.clientTop]}function On(t,e,n){arguments.length<3&&(n=e,e=Nn().changedTouches);for(var r,i=0,a=e?e.length:0;i<a;++i)if((r=e[i]).identifier===n)return Dn(t,r);return null}function Bn(t){var e=Nn();return e.changedTouches&&(e=e.changedTouches[0]),Dn(t,e)}var Ln,In,Rn=0,Fn=0,Pn=0,jn=0,Yn=0,zn=0,Un="object"==typeof performance&&performance.now?performance:Date,qn="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function Hn(){return Yn||(qn($n),Yn=Un.now()+zn)}function $n(){Yn=0}function Wn(){this._call=this._time=this._next=null}function Vn(t,e,n){var r=new Wn;return r.restart(t,e,n),r}function Gn(){Hn(),++Rn;for(var t,e=Ln;e;)(t=Yn-e._time)>=0&&e._call.call(null,t),e=e._next;--Rn}function Xn(){Yn=(jn=Un.now())+zn,Rn=Fn=0;try{Gn()}finally{Rn=0,function(){for(var t,e,n=Ln,r=1/0;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:Ln=e);In=t,Qn(r)}(),Yn=0}}function Zn(){var t=Un.now(),e=t-jn;e>1e3&&(zn-=e,jn=t)}function Qn(t){Rn||(Fn&&(Fn=clearTimeout(Fn)),t-Yn>24?(t<1/0&&(Fn=setTimeout(Xn,t-Un.now()-zn)),Pn&&(Pn=clearInterval(Pn))):(Pn||(jn=Un.now(),Pn=setInterval(Zn,1e3)),Rn=1,qn(Xn)))}function Kn(t,e,n){var r=new Wn;return e=null==e?0:+e,r.restart((function(n){r.stop(),t(n+e)}),e,n),r}Wn.prototype=Vn.prototype={constructor:Wn,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?Hn():+n)+(null==e?0:+e),this._next||In===this||(In?In._next=this:Ln=this,In=this),this._call=t,this._time=n,Qn()},stop:function(){this._call&&(this._call=null,this._time=1/0,Qn())}};var Jn=ft("start","end","cancel","interrupt"),tr=[];function er(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return Kn(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(Kn((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=Vn((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:Jn,tween:tr,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})}function nr(t,e){var n=ir(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function rr(t,e){var n=ir(t,e);if(n.state>3)throw new Error("too late; already running");return n}function ir(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function ar(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}}var or,sr,cr,ur,lr=180/Math.PI,hr={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function fr(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*lr,skewX:Math.atan(c)*lr,scaleX:o,scaleY:s}}function dr(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:Tn(t,i)},{i:c-2,x:Tn(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:Tn(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:Tn(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:Tn(t,n)},{i:s-2,x:Tn(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var pr=dr((function(t){return"none"===t?hr:(or||(or=document.createElement("DIV"),sr=document.documentElement,cr=document.defaultView),or.style.transform=t,t=cr.getComputedStyle(sr.appendChild(or),null).getPropertyValue("transform"),sr.removeChild(or),fr(+(t=t.slice(7,-1).split(","))[0],+t[1],+t[2],+t[3],+t[4],+t[5]))}),"px, ","px)","deg)"),yr=dr((function(t){return null==t?hr:(ur||(ur=document.createElementNS("http://www.w3.org/2000/svg","g")),ur.setAttribute("transform",t),(t=ur.transform.baseVal.consolidate())?fr((t=t.matrix).a,t.b,t.c,t.d,t.e,t.f):hr)}),", ",")",")");function gr(t,e){var n,r;return function(){var i=rr(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function mr(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=rr(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function vr(t,e,n){var r=t._id;return t.each((function(){var t=rr(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return ir(t,r).value[e]}}function br(t,e){var n;return("number"==typeof e?Tn:e instanceof Ve?yn:(n=Ve(e))?(e=n,yn):An)(t,e)}function _r(t){return function(){this.removeAttribute(t)}}function xr(t){return function(){this.removeAttributeNS(t.space,t.local)}}function wr(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function kr(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function Tr(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function Er(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function Cr(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function Sr(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function Ar(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Sr(t,i)),n}return i._value=e,i}function Mr(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Cr(t,i)),n}return i._value=e,i}function Nr(t,e){return function(){nr(this,t).delay=+e.apply(this,arguments)}}function Dr(t,e){return e=+e,function(){nr(this,t).delay=e}}function Or(t,e){return function(){rr(this,t).duration=+e.apply(this,arguments)}}function Br(t,e){return e=+e,function(){rr(this,t).duration=e}}function Lr(t,e){if("function"!=typeof e)throw new Error;return function(){rr(this,t).ease=e}}function Ir(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?nr:rr;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var Rr=ke.prototype.constructor;function Fr(t){return function(){this.style.removeProperty(t)}}function Pr(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function jr(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Pr(t,a,n)),r}return a._value=e,a}function Yr(t){return function(e){this.textContent=t.call(this,e)}}function zr(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Yr(r)),e}return r._value=t,r}var Ur=0;function qr(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function Hr(t){return ke().transition(t)}function $r(){return++Ur}var Wr=ke.prototype;function Vr(t){return t*t*t}function Gr(t){return--t*t*t+1}function Xr(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}qr.prototype=Hr.prototype={constructor:qr,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=pt(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,er(h[f],e,n,f,h,ir(s,n)));return new qr(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=gt(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=ir(c,n),y=0,g=d.length;y<g;++y)(f=d[y])&&er(f,e,n,y,d,p);a.push(d),o.push(c)}return new qr(a,o,e,n)},filter:function(t){"function"!=typeof t&&(t=mt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new qr(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new qr(o,this._parents,this._name,this._id)},selection:function(){return new Rr(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=$r(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=ir(o,e);er(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new qr(r,this._parents,t,n)},call:Wr.call,nodes:Wr.nodes,node:Wr.node,size:Wr.size,empty:Wr.empty,each:Wr.each,on:function(t,e){var n=this._id;return arguments.length<2?ir(this.node(),n).on.on(t):this.each(Ir(n,t,e))},attr:function(t,e){var n=Et(t),r="transform"===n?yr:br;return this.attrTween(t,"function"==typeof e?(n.local?Er:Tr)(n,r,vr(this,"attr."+t,e)):null==e?(n.local?xr:_r)(n):(n.local?kr:wr)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=Et(t);return this.tween(n,(r.local?Ar:Mr)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?pr:br;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=Rt(this,t),o=(this.style.removeProperty(t),Rt(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Fr(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=Rt(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=Rt(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,vr(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=rr(this,t),u=c.on,l=null==c.value[o]?a||(a=Fr(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=Rt(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,jr(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(vr(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,zr(t))},remove:function(){return this.on("end.remove",function(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}(this._id))},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=ir(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?gr:mr)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Nr:Dr)(e,t)):ir(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Or:Br)(e,t)):ir(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(Lr(e,t)):ir(this.node(),e).ease},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=rr(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e}))}))}};var Zr={time:null,delay:0,duration:250,ease:Xr};function Qr(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))return Zr.time=Hn(),Zr;return n}ke.prototype.interrupt=function(t){return this.each((function(){ar(this,t)}))},ke.prototype.transition=function(t){var e,n;t instanceof qr?(e=t._id,t=t._name):(e=$r(),(n=Zr).time=Hn(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&er(o,t,e,u,s,n||Qr(o,e));return new qr(r,this._parents,t,e)};var Kr=[null];function Jr(t,e){var n,r,i=t.__transition;if(i)for(r in e=null==e?null:e+"",i)if((n=i[r]).state>1&&n.name===e)return new qr([[t]],Kr,e,+r);return null}function ti(t){return function(){return t}}function ei(t,e,n){this.target=t,this.type=e,this.selection=n}function ni(){le.stopImmediatePropagation()}function ri(){le.preventDefault(),le.stopImmediatePropagation()}var ii={name:"drag"},ai={name:"space"},oi={name:"handle"},si={name:"center"};function ci(t){return[+t[0],+t[1]]}function ui(t){return[ci(t[0]),ci(t[1])]}function li(t){return function(e){return On(e,le.touches,t)}}var hi={name:"x",handles:["w","e"].map(bi),input:function(t,e){return null==t?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},fi={name:"y",handles:["n","s"].map(bi),input:function(t,e){return null==t?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},di={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(bi),input:function(t){return null==t?null:ui(t)},output:function(t){return t}},pi={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},yi={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},gi={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},mi={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},vi={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function bi(t){return{type:t}}function _i(){return!le.ctrlKey&&!le.button}function xi(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function wi(){return navigator.maxTouchPoints||"ontouchstart"in this}function ki(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function Ti(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function Ei(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function Ci(){return Mi(hi)}function Si(){return Mi(fi)}function Ai(){return Mi(di)}function Mi(t){var e,n=xi,r=_i,i=wi,a=!0,o=ft("start","brush","end"),s=6;function c(e){var n=e.property("__brush",y).selectAll(".overlay").data([bi("overlay")]);n.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",pi.overlay).merge(n).each((function(){var t=ki(this).extent;Te(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),e.selectAll(".selection").data([bi("selection")]).enter().append("rect").attr("class","selection").attr("cursor",pi.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=e.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return pi[t.type]})),e.each(u).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",f).filter(i).on("touchstart.brush",f).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function u(){var t=Te(this),e=ki(this).selection;e?(t.selectAll(".selection").style("display",null).attr("x",e[0][0]).attr("y",e[0][1]).attr("width",e[1][0]-e[0][0]).attr("height",e[1][1]-e[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?e[1][0]-s/2:e[0][0]-s/2})).attr("y",(function(t){return"s"===t.type[0]?e[1][1]-s/2:e[0][1]-s/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?e[1][0]-e[0][0]+s:s})).attr("height",(function(t){return"e"===t.type||"w"===t.type?e[1][1]-e[0][1]+s:s}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function l(t,e,n){var r=t.__brush.emitter;return!r||n&&r.clean?new h(t,e,n):r}function h(t,e,n){this.that=t,this.args=e,this.state=t.__brush,this.active=0,this.clean=n}function f(){if((!e||le.touches)&&r.apply(this,arguments)){var n,i,o,s,c,h,f,d,p,y,g,m=this,v=le.target.__data__.type,b="selection"===(a&&le.metaKey?v="overlay":v)?ii:a&&le.altKey?si:oi,_=t===fi?null:mi[v],x=t===hi?null:vi[v],w=ki(m),k=w.extent,T=w.selection,E=k[0][0],C=k[0][1],S=k[1][0],A=k[1][1],M=0,N=0,D=_&&x&&a&&le.shiftKey,O=le.touches?li(le.changedTouches[0].identifier):Bn,B=O(m),L=B,I=l(m,arguments,!0).beforestart();"overlay"===v?(T&&(p=!0),w.selection=T=[[n=t===fi?E:B[0],o=t===hi?C:B[1]],[c=t===fi?S:n,f=t===hi?A:o]]):(n=T[0][0],o=T[0][1],c=T[1][0],f=T[1][1]),i=n,s=o,h=c,d=f;var R=Te(m).attr("pointer-events","none"),F=R.selectAll(".overlay").attr("cursor",pi[v]);if(le.touches)I.moved=j,I.ended=z;else{var P=Te(le.view).on("mousemove.brush",j,!0).on("mouseup.brush",z,!0);a&&P.on("keydown.brush",U,!0).on("keyup.brush",q,!0),Se(le.view)}ni(),ar(m),u.call(m),I.start()}function j(){var t=O(m);!D||y||g||(Math.abs(t[0]-L[0])>Math.abs(t[1]-L[1])?g=!0:y=!0),L=t,p=!0,ri(),Y()}function Y(){var t;switch(M=L[0]-B[0],N=L[1]-B[1],b){case ai:case ii:_&&(M=Math.max(E-n,Math.min(S-c,M)),i=n+M,h=c+M),x&&(N=Math.max(C-o,Math.min(A-f,N)),s=o+N,d=f+N);break;case oi:_<0?(M=Math.max(E-n,Math.min(S-n,M)),i=n+M,h=c):_>0&&(M=Math.max(E-c,Math.min(S-c,M)),i=n,h=c+M),x<0?(N=Math.max(C-o,Math.min(A-o,N)),s=o+N,d=f):x>0&&(N=Math.max(C-f,Math.min(A-f,N)),s=o,d=f+N);break;case si:_&&(i=Math.max(E,Math.min(S,n-M*_)),h=Math.max(E,Math.min(S,c+M*_))),x&&(s=Math.max(C,Math.min(A,o-N*x)),d=Math.max(C,Math.min(A,f+N*x)))}h<i&&(_*=-1,t=n,n=c,c=t,t=i,i=h,h=t,v in yi&&F.attr("cursor",pi[v=yi[v]])),d<s&&(x*=-1,t=o,o=f,f=t,t=s,s=d,d=t,v in gi&&F.attr("cursor",pi[v=gi[v]])),w.selection&&(T=w.selection),y&&(i=T[0][0],h=T[1][0]),g&&(s=T[0][1],d=T[1][1]),T[0][0]===i&&T[0][1]===s&&T[1][0]===h&&T[1][1]===d||(w.selection=[[i,s],[h,d]],u.call(m),I.brush())}function z(){if(ni(),le.touches){if(le.touches.length)return;e&&clearTimeout(e),e=setTimeout((function(){e=null}),500)}else Ae(le.view,p),P.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null);R.attr("pointer-events","all"),F.attr("cursor",pi.overlay),w.selection&&(T=w.selection),Ti(T)&&(w.selection=null,u.call(m)),I.end()}function U(){switch(le.keyCode){case 16:D=_&&x;break;case 18:b===oi&&(_&&(c=h-M*_,n=i+M*_),x&&(f=d-N*x,o=s+N*x),b=si,Y());break;case 32:b!==oi&&b!==si||(_<0?c=h-M:_>0&&(n=i-M),x<0?f=d-N:x>0&&(o=s-N),b=ai,F.attr("cursor",pi.selection),Y());break;default:return}ri()}function q(){switch(le.keyCode){case 16:D&&(y=g=D=!1,Y());break;case 18:b===si&&(_<0?c=h:_>0&&(n=i),x<0?f=d:x>0&&(o=s),b=oi,Y());break;case 32:b===ai&&(le.altKey?(_&&(c=h-M*_,n=i+M*_),x&&(f=d-N*x,o=s+N*x),b=si):(_<0?c=h:_>0&&(n=i),x<0?f=d:x>0&&(o=s),b=oi),F.attr("cursor",pi[v]),Y());break;default:return}ri()}}function d(){l(this,arguments).moved()}function p(){l(this,arguments).ended()}function y(){var e=this.__brush||{selection:null};return e.extent=ui(n.apply(this,arguments)),e.dim=t,e}return c.move=function(e,n){e.selection?e.on("start.brush",(function(){l(this,arguments).beforestart().start()})).on("interrupt.brush end.brush",(function(){l(this,arguments).end()})).tween("brush",(function(){var e=this,r=e.__brush,i=l(e,arguments),a=r.selection,o=t.input("function"==typeof n?n.apply(this,arguments):n,r.extent),s=Mn(a,o);function c(t){r.selection=1===t&&null===o?null:s(t),u.call(e),i.brush()}return null!==a&&null!==o?c:c(1)})):e.each((function(){var e=this,r=arguments,i=e.__brush,a=t.input("function"==typeof n?n.apply(e,r):n,i.extent),o=l(e,r).beforestart();ar(e),i.selection=null===a?null:a,u.call(e),o.start().brush().end()}))},c.clear=function(t){c.move(t,null)},h.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting?(this.starting=!1,this.emit("start")):this.emit("brush"),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(e){ge(new ei(c,e,t.output(this.state.selection)),o.apply,o,[e,this.that,this.args])}},c.extent=function(t){return arguments.length?(n="function"==typeof t?t:ti(ui(t)),c):n},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:ti(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:ti(!!t),c):i},c.handleSize=function(t){return arguments.length?(s=+t,c):s},c.keyModifiers=function(t){return arguments.length?(a=!!t,c):a},c.on=function(){var t=o.on.apply(o,arguments);return t===o?c:t},c}var Ni=Math.cos,Di=Math.sin,Oi=Math.PI,Bi=Oi/2,Li=2*Oi,Ii=Math.max;function Ri(t){return function(e,n){return t(e.source.value+e.target.value,n.source.value+n.target.value)}}function Fi(){var t=0,e=null,n=null,r=null;function i(i){var a,o,s,c,u,l,h=i.length,f=[],d=k(h),p=[],y=[],g=y.groups=new Array(h),m=new Array(h*h);for(a=0,u=-1;++u<h;){for(o=0,l=-1;++l<h;)o+=i[u][l];f.push(o),p.push(k(h)),a+=o}for(e&&d.sort((function(t,n){return e(f[t],f[n])})),n&&p.forEach((function(t,e){t.sort((function(t,r){return n(i[e][t],i[e][r])}))})),c=(a=Ii(0,Li-t*h)/a)?t:Li/h,o=0,u=-1;++u<h;){for(s=o,l=-1;++l<h;){var v=d[u],b=p[v][l],_=i[v][b],x=o,w=o+=_*a;m[b*h+v]={index:v,subindex:b,startAngle:x,endAngle:w,value:_}}g[v]={index:v,startAngle:s,endAngle:o,value:f[v]},o+=c}for(u=-1;++u<h;)for(l=u-1;++l<h;){var T=m[l*h+u],E=m[u*h+l];(T.value||E.value)&&y.push(T.value<E.value?{source:E,target:T}:{source:T,target:E})}return r?y.sort(r):y}return i.padAngle=function(e){return arguments.length?(t=Ii(0,e),i):t},i.sortGroups=function(t){return arguments.length?(e=t,i):e},i.sortSubgroups=function(t){return arguments.length?(n=t,i):n},i.sortChords=function(t){return arguments.length?(null==t?r=null:(r=Ri(t))._=t,i):r&&r._},i}var Pi=Array.prototype.slice;function ji(t){return function(){return t}}var Yi=Math.PI,zi=2*Yi,Ui=1e-6,qi=zi-Ui;function Hi(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function $i(){return new Hi}Hi.prototype=$i.prototype={constructor:Hi,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,r){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+r)},bezierCurveTo:function(t,e,n,r,i,a){this._+="C"+ +t+","+ +e+","+ +n+","+ +r+","+(this._x1=+i)+","+(this._y1=+a)},arcTo:function(t,e,n,r,i){t=+t,e=+e,n=+n,r=+r,i=+i;var a=this._x1,o=this._y1,s=n-t,c=r-e,u=a-t,l=o-e,h=u*u+l*l;if(i<0)throw new Error("negative radius: "+i);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>Ui)if(Math.abs(l*s-c*u)>Ui&&i){var f=n-a,d=r-o,p=s*s+c*c,y=f*f+d*d,g=Math.sqrt(p),m=Math.sqrt(h),v=i*Math.tan((Yi-Math.acos((p+h-y)/(2*g*m)))/2),b=v/m,_=v/g;Math.abs(b-1)>Ui&&(this._+="L"+(t+b*u)+","+(e+b*l)),this._+="A"+i+","+i+",0,0,"+ +(l*f>u*d)+","+(this._x1=t+_*s)+","+(this._y1=e+_*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e)},arc:function(t,e,n,r,i,a){t=+t,e=+e,a=!!a;var o=(n=+n)*Math.cos(r),s=n*Math.sin(r),c=t+o,u=e+s,l=1^a,h=a?r-i:i-r;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+u:(Math.abs(this._x1-c)>Ui||Math.abs(this._y1-u)>Ui)&&(this._+="L"+c+","+u),n&&(h<0&&(h=h%zi+zi),h>qi?this._+="A"+n+","+n+",0,1,"+l+","+(t-o)+","+(e-s)+"A"+n+","+n+",0,1,"+l+","+(this._x1=c)+","+(this._y1=u):h>Ui&&(this._+="A"+n+","+n+",0,"+ +(h>=Yi)+","+l+","+(this._x1=t+n*Math.cos(i))+","+(this._y1=e+n*Math.sin(i))))},rect:function(t,e,n,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};const Wi=$i;function Vi(t){return t.source}function Gi(t){return t.target}function Xi(t){return t.radius}function Zi(t){return t.startAngle}function Qi(t){return t.endAngle}function Ki(){var t=Vi,e=Gi,n=Xi,r=Zi,i=Qi,a=null;function o(){var o,s=Pi.call(arguments),c=t.apply(this,s),u=e.apply(this,s),l=+n.apply(this,(s[0]=c,s)),h=r.apply(this,s)-Bi,f=i.apply(this,s)-Bi,d=l*Ni(h),p=l*Di(h),y=+n.apply(this,(s[0]=u,s)),g=r.apply(this,s)-Bi,m=i.apply(this,s)-Bi;if(a||(a=o=Wi()),a.moveTo(d,p),a.arc(0,0,l,h,f),h===g&&f===m||(a.quadraticCurveTo(0,0,y*Ni(g),y*Di(g)),a.arc(0,0,y,g,m)),a.quadraticCurveTo(0,0,d,p),a.closePath(),o)return a=null,o+""||null}return o.radius=function(t){return arguments.length?(n="function"==typeof t?t:ji(+t),o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:ji(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:ji(+t),o):i},o.source=function(e){return arguments.length?(t=e,o):t},o.target=function(t){return arguments.length?(e=t,o):e},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o}var Ji="$";function ta(){}function ea(t,e){var n=new ta;if(t instanceof ta)t.each((function(t,e){n.set(e,t)}));else if(Array.isArray(t)){var r,i=-1,a=t.length;if(null==e)for(;++i<a;)n.set(i,t[i]);else for(;++i<a;)n.set(e(r=t[i],i,t),r)}else if(t)for(var o in t)n.set(o,t[o]);return n}ta.prototype=ea.prototype={constructor:ta,has:function(t){return Ji+t in this},get:function(t){return this[Ji+t]},set:function(t,e){return this[Ji+t]=e,this},remove:function(t){var e=Ji+t;return e in this&&delete this[e]},clear:function(){for(var t in this)t[0]===Ji&&delete this[t]},keys:function(){var t=[];for(var e in this)e[0]===Ji&&t.push(e.slice(1));return t},values:function(){var t=[];for(var e in this)e[0]===Ji&&t.push(this[e]);return t},entries:function(){var t=[];for(var e in this)e[0]===Ji&&t.push({key:e.slice(1),value:this[e]});return t},size:function(){var t=0;for(var e in this)e[0]===Ji&&++t;return t},empty:function(){for(var t in this)if(t[0]===Ji)return!1;return!0},each:function(t){for(var e in this)e[0]===Ji&&t(this[e],e.slice(1),this)}};const na=ea;function ra(){var t,e,n,r=[],i=[];function a(n,i,o,s){if(i>=r.length)return null!=t&&n.sort(t),null!=e?e(n):n;for(var c,u,l,h=-1,f=n.length,d=r[i++],p=na(),y=o();++h<f;)(l=p.get(c=d(u=n[h])+""))?l.push(u):p.set(c,[u]);return p.each((function(t,e){s(y,e,a(t,i,o,s))})),y}function o(t,n){if(++n>r.length)return t;var a,s=i[n-1];return null!=e&&n>=r.length?a=t.entries():(a=[],t.each((function(t,e){a.push({key:e,values:o(t,n)})}))),null!=s?a.sort((function(t,e){return s(t.key,e.key)})):a}return n={object:function(t){return a(t,0,ia,aa)},map:function(t){return a(t,0,oa,sa)},entries:function(t){return o(a(t,0,oa,sa),0)},key:function(t){return r.push(t),n},sortKeys:function(t){return i[r.length-1]=t,n},sortValues:function(e){return t=e,n},rollup:function(t){return e=t,n}}}function ia(){return{}}function aa(t,e,n){t[e]=n}function oa(){return na()}function sa(t,e,n){t.set(e,n)}function ca(){}var ua=na.prototype;function la(t,e){var n=new ca;if(t instanceof ca)t.each((function(t){n.add(t)}));else if(t){var r=-1,i=t.length;if(null==e)for(;++r<i;)n.add(t[r]);else for(;++r<i;)n.add(e(t[r],r,t))}return n}ca.prototype=la.prototype={constructor:ca,has:ua.has,add:function(t){return this[Ji+(t+="")]=t,this},remove:ua.remove,clear:ua.clear,values:ua.keys,size:ua.size,empty:ua.empty,each:ua.each};const ha=la;function fa(t){var e=[];for(var n in t)e.push(n);return e}function da(t){var e=[];for(var n in t)e.push(t[n]);return e}function pa(t){var e=[];for(var n in t)e.push({key:n,value:t[n]});return e}var ya=Math.PI/180,ga=180/Math.PI,ma=.96422,va=.82521,ba=4/29,_a=6/29,xa=3*_a*_a;function wa(t){if(t instanceof Ea)return new Ea(t.l,t.a,t.b,t.opacity);if(t instanceof Ba)return La(t);t instanceof Ke||(t=Ze(t));var e,n,r=Ma(t.r),i=Ma(t.g),a=Ma(t.b),o=Ca((.2225045*r+.7168786*i+.0606169*a)/1);return r===i&&i===a?e=n=o:(e=Ca((.4360747*r+.3850649*i+.1430804*a)/ma),n=Ca((.0139322*r+.0971045*i+.7141733*a)/va)),new Ea(116*o-16,500*(e-o),200*(o-n),t.opacity)}function ka(t,e){return new Ea(t,0,0,null==e?1:e)}function Ta(t,e,n,r){return 1===arguments.length?wa(t):new Ea(t,e,n,null==r?1:r)}function Ea(t,e,n,r){this.l=+t,this.a=+e,this.b=+n,this.opacity=+r}function Ca(t){return t>.008856451679035631?Math.pow(t,1/3):t/xa+ba}function Sa(t){return t>_a?t*t*t:xa*(t-ba)}function Aa(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Ma(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Na(t){if(t instanceof Ba)return new Ba(t.h,t.c,t.l,t.opacity);if(t instanceof Ea||(t=wa(t)),0===t.a&&0===t.b)return new Ba(NaN,0<t.l&&t.l<100?0:NaN,t.l,t.opacity);var e=Math.atan2(t.b,t.a)*ga;return new Ba(e<0?e+360:e,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function Da(t,e,n,r){return 1===arguments.length?Na(t):new Ba(n,e,t,null==r?1:r)}function Oa(t,e,n,r){return 1===arguments.length?Na(t):new Ba(t,e,n,null==r?1:r)}function Ba(t,e,n,r){this.h=+t,this.c=+e,this.l=+n,this.opacity=+r}function La(t){if(isNaN(t.h))return new Ea(t.l,0,0,t.opacity);var e=t.h*ya;return new Ea(t.l,Math.cos(e)*t.c,Math.sin(e)*t.c,t.opacity)}Me(Ea,Ta,Ne(De,{brighter:function(t){return new Ea(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Ea(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this.b)?t:t-this.b/200;return new Ke(Aa(3.1338561*(e=ma*Sa(e))-1.6168667*(t=1*Sa(t))-.4906146*(n=va*Sa(n))),Aa(-.9787684*e+1.9161415*t+.033454*n),Aa(.0719453*e-.2289914*t+1.4052427*n),this.opacity)}})),Me(Ba,Oa,Ne(De,{brighter:function(t){return new Ba(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new Ba(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return La(this).rgb()}}));var Ia=-.14861,Ra=1.78277,Fa=-.29227,Pa=-.90649,ja=1.97294,Ya=ja*Pa,za=ja*Ra,Ua=Ra*Fa-Pa*Ia;function qa(t){if(t instanceof $a)return new $a(t.h,t.s,t.l,t.opacity);t instanceof Ke||(t=Ze(t));var e=t.r/255,n=t.g/255,r=t.b/255,i=(Ua*r+Ya*e-za*n)/(Ua+Ya-za),a=r-i,o=(ja*(n-i)-Fa*a)/Pa,s=Math.sqrt(o*o+a*a)/(ja*i*(1-i)),c=s?Math.atan2(o,a)*ga-120:NaN;return new $a(c<0?c+360:c,s,i,t.opacity)}function Ha(t,e,n,r){return 1===arguments.length?qa(t):new $a(t,e,n,null==r?1:r)}function $a(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}Me($a,Ha,Ne(De,{brighter:function(t){return t=null==t?Be:Math.pow(Be,t),new $a(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Oe:Math.pow(Oe,t),new $a(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*ya,e=+this.l,n=isNaN(this.s)?0:this.s*e*(1-e),r=Math.cos(t),i=Math.sin(t);return new Ke(255*(e+n*(Ia*r+Ra*i)),255*(e+n*(Fa*r+Pa*i)),255*(e+n*(ja*r)),this.opacity)}}));var Wa=Array.prototype.slice;function Va(t,e){return t-e}function Ga(t){return function(){return t}}function Xa(t,e){for(var n,r=-1,i=e.length;++r<i;)if(n=Za(t,e[r]))return n;return 0}function Za(t,e){for(var n=e[0],r=e[1],i=-1,a=0,o=t.length,s=o-1;a<o;s=a++){var c=t[a],u=c[0],l=c[1],h=t[s],f=h[0],d=h[1];if(Qa(c,h,e))return 0;l>r!=d>r&&n<(f-u)*(r-l)/(d-l)+u&&(i=-i)}return i}function Qa(t,e,n){var r,i,a,o;return function(t,e,n){return(e[0]-t[0])*(n[1]-t[1])==(n[0]-t[0])*(e[1]-t[1])}(t,e,n)&&(i=t[r=+(t[0]===e[0])],a=n[r],o=e[r],i<=a&&a<=o||o<=a&&a<=i)}function Ka(){}var Ja=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function to(){var t=1,e=1,n=N,r=s;function i(t){var e=n(t);if(Array.isArray(e))e=e.slice().sort(Va);else{var r=m(t),i=r[0],o=r[1];e=M(i,o,e),e=k(Math.floor(i/e)*e,Math.floor(o/e)*e,e)}return e.map((function(e){return a(t,e)}))}function a(n,i){var a=[],s=[];return function(n,r,i){var a,s,c,u,l,h,f=new Array,d=new Array;for(a=s=-1,u=n[0]>=r,Ja[u<<1].forEach(p);++a<t-1;)c=u,u=n[a+1]>=r,Ja[c|u<<1].forEach(p);for(Ja[u<<0].forEach(p);++s<e-1;){for(a=-1,u=n[s*t+t]>=r,l=n[s*t]>=r,Ja[u<<1|l<<2].forEach(p);++a<t-1;)c=u,u=n[s*t+t+a+1]>=r,h=l,l=n[s*t+a+1]>=r,Ja[c|u<<1|l<<2|h<<3].forEach(p);Ja[u|l<<3].forEach(p)}for(a=-1,l=n[s*t]>=r,Ja[l<<2].forEach(p);++a<t-1;)h=l,l=n[s*t+a+1]>=r,Ja[l<<2|h<<3].forEach(p);function p(t){var e,n,r=[t[0][0]+a,t[0][1]+s],c=[t[1][0]+a,t[1][1]+s],u=o(r),l=o(c);(e=d[u])?(n=f[l])?(delete d[e.end],delete f[n.start],e===n?(e.ring.push(c),i(e.ring)):f[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete d[e.end],e.ring.push(c),d[e.end=l]=e):(e=f[l])?(n=d[u])?(delete f[e.start],delete d[n.end],e===n?(e.ring.push(c),i(e.ring)):f[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete f[e.start],e.ring.unshift(r),f[e.start=u]=e):f[u]=d[l]={start:u,end:l,ring:[r,c]}}Ja[l<<3].forEach(p)}(n,i,(function(t){r(t,n,i),function(t){for(var e=0,n=t.length,r=t[n-1][1]*t[0][0]-t[n-1][0]*t[0][1];++e<n;)r+=t[e-1][1]*t[e][0]-t[e-1][0]*t[e][1];return r}(t)>0?a.push([t]):s.push(t)})),s.forEach((function(t){for(var e,n=0,r=a.length;n<r;++n)if(-1!==Xa((e=a[n])[0],t))return void e.push(t)})),{type:"MultiPolygon",value:i,coordinates:a}}function o(e){return 2*e[0]+e[1]*(t+1)*4}function s(n,r,i){n.forEach((function(n){var a,o=n[0],s=n[1],c=0|o,u=0|s,l=r[u*t+c];o>0&&o<t&&c===o&&(a=r[u*t+c-1],n[0]=o+(i-a)/(l-a)-.5),s>0&&s<e&&u===s&&(a=r[(u-1)*t+c],n[1]=s+(i-a)/(l-a)-.5)}))}return i.contour=a,i.size=function(n){if(!arguments.length)return[t,e];var r=Math.ceil(n[0]),a=Math.ceil(n[1]);if(!(r>0&&a>0))throw new Error("invalid size");return t=r,e=a,i},i.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?Ga(Wa.call(t)):Ga(t),i):n},i.smooth=function(t){return arguments.length?(r=t?s:Ka,i):r===s},i}function eo(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<i;++o)for(var s=0,c=0;s<r+n;++s)s<r&&(c+=t.data[s+o*r]),s>=n&&(s>=a&&(c-=t.data[s-a+o*r]),e.data[s-n+o*r]=c/Math.min(s+1,r-1+a-s,a))}function no(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<r;++o)for(var s=0,c=0;s<i+n;++s)s<i&&(c+=t.data[o+s*r]),s>=n&&(s>=a&&(c-=t.data[o+(s-a)*r]),e.data[o+(s-n)*r]=c/Math.min(s+1,i-1+a-s,a))}function ro(t){return t[0]}function io(t){return t[1]}function ao(){return 1}function oo(){var t=ro,e=io,n=ao,r=960,i=500,a=20,o=2,s=3*a,c=r+2*s>>o,u=i+2*s>>o,l=Ga(20);function h(r){var i=new Float32Array(c*u),h=new Float32Array(c*u);r.forEach((function(r,a,l){var h=+t(r,a,l)+s>>o,f=+e(r,a,l)+s>>o,d=+n(r,a,l);h>=0&&h<c&&f>=0&&f<u&&(i[h+f*c]+=d)})),eo({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),no({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),eo({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),no({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),eo({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),no({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o);var d=l(i);if(!Array.isArray(d)){var p=I(i);d=M(0,p,d),(d=k(0,Math.floor(p/d)*d,d)).shift()}return to().thresholds(d).size([c,u])(i).map(f)}function f(t){return t.value*=Math.pow(2,-2*o),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(y)}function y(t){t[0]=t[0]*Math.pow(2,o)-s,t[1]=t[1]*Math.pow(2,o)-s}function g(){return c=r+2*(s=3*a)>>o,u=i+2*s>>o,h}return h.x=function(e){return arguments.length?(t="function"==typeof e?e:Ga(+e),h):t},h.y=function(t){return arguments.length?(e="function"==typeof t?t:Ga(+t),h):e},h.weight=function(t){return arguments.length?(n="function"==typeof t?t:Ga(+t),h):n},h.size=function(t){if(!arguments.length)return[r,i];var e=Math.ceil(t[0]),n=Math.ceil(t[1]);if(!(e>=0||e>=0))throw new Error("invalid size");return r=e,i=n,g()},h.cellSize=function(t){if(!arguments.length)return 1<<o;if(!((t=+t)>=1))throw new Error("invalid cell size");return o=Math.floor(Math.log(t)/Math.LN2),g()},h.thresholds=function(t){return arguments.length?(l="function"==typeof t?t:Array.isArray(t)?Ga(Wa.call(t)):Ga(t),h):l},h.bandwidth=function(t){if(!arguments.length)return Math.sqrt(a*(a+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return a=Math.round((Math.sqrt(4*t*t+1)-1)/2),g()},h}function so(t){return function(){return t}}function co(t,e,n,r,i,a,o,s,c,u){this.target=t,this.type=e,this.subject=n,this.identifier=r,this.active=i,this.x=a,this.y=o,this.dx=s,this.dy=c,this._=u}function uo(){return!le.ctrlKey&&!le.button}function lo(){return this.parentNode}function ho(t){return null==t?{x:le.x,y:le.y}:t}function fo(){return navigator.maxTouchPoints||"ontouchstart"in this}function po(){var t,e,n,r,i=uo,a=lo,o=ho,s=fo,c={},u=ft("start","drag","end"),l=0,h=0;function f(t){t.on("mousedown.drag",d).filter(s).on("touchstart.drag",g).on("touchmove.drag",m).on("touchend.drag touchcancel.drag",v).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(){if(!r&&i.apply(this,arguments)){var o=b("mouse",a.apply(this,arguments),Bn,this,arguments);o&&(Te(le.view).on("mousemove.drag",p,!0).on("mouseup.drag",y,!0),Se(le.view),Ee(),n=!1,t=le.clientX,e=le.clientY,o("start"))}}function p(){if(Ce(),!n){var r=le.clientX-t,i=le.clientY-e;n=r*r+i*i>h}c.mouse("drag")}function y(){Te(le.view).on("mousemove.drag mouseup.drag",null),Ae(le.view,n),Ce(),c.mouse("end")}function g(){if(i.apply(this,arguments)){var t,e,n=le.changedTouches,r=a.apply(this,arguments),o=n.length;for(t=0;t<o;++t)(e=b(n[t].identifier,r,On,this,arguments))&&(Ee(),e("start"))}}function m(){var t,e,n=le.changedTouches,r=n.length;for(t=0;t<r;++t)(e=c[n[t].identifier])&&(Ce(),e("drag"))}function v(){var t,e,n=le.changedTouches,i=n.length;for(r&&clearTimeout(r),r=setTimeout((function(){r=null}),500),t=0;t<i;++t)(e=c[n[t].identifier])&&(Ee(),e("end"))}function b(t,e,n,r,i){var a,s,h,d=n(e,t),p=u.copy();if(ge(new co(f,"beforestart",a,t,l,d[0],d[1],0,0,p),(function(){return null!=(le.subject=a=o.apply(r,i))&&(s=a.x-d[0]||0,h=a.y-d[1]||0,!0)})))return function o(u){var y,g=d;switch(u){case"start":c[t]=o,y=l++;break;case"end":delete c[t],--l;case"drag":d=n(e,t),y=l}ge(new co(f,u,a,t,y,d[0]+s,d[1]+h,d[0]-g[0],d[1]-g[1],p),p.apply,p,[u,r,i])}}return f.filter=function(t){return arguments.length?(i="function"==typeof t?t:so(!!t),f):i},f.container=function(t){return arguments.length?(a="function"==typeof t?t:so(t),f):a},f.subject=function(t){return arguments.length?(o="function"==typeof t?t:so(t),f):o},f.touchable=function(t){return arguments.length?(s="function"==typeof t?t:so(!!t),f):s},f.on=function(){var t=u.on.apply(u,arguments);return t===u?f:t},f.clickDistance=function(t){return arguments.length?(h=(t=+t)*t,f):Math.sqrt(h)},f}co.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var yo={},go={};function mo(t){return new Function("d","return {"+t.map((function(t,e){return JSON.stringify(t)+": d["+e+'] || ""'})).join(",")+"}")}function vo(t){var e=Object.create(null),n=[];return t.forEach((function(t){for(var r in t)r in e||n.push(e[r]=r)})),n}function bo(t,e){var n=t+"",r=n.length;return r<e?new Array(e-r+1).join(0)+n:n}function _o(t){var e=new RegExp('["'+t+"\n\r]"),n=t.charCodeAt(0);function r(t,e){var r,i=[],a=t.length,o=0,s=0,c=a<=0,u=!1;function l(){if(c)return go;if(u)return u=!1,yo;var e,r,i=o;if(34===t.charCodeAt(i)){for(;o++<a&&34!==t.charCodeAt(o)||34===t.charCodeAt(++o););return(e=o)>=a?c=!0:10===(r=t.charCodeAt(o++))?u=!0:13===r&&(u=!0,10===t.charCodeAt(o)&&++o),t.slice(i+1,e-1).replace(/""/g,'"')}for(;o<a;){if(10===(r=t.charCodeAt(e=o++)))u=!0;else if(13===r)u=!0,10===t.charCodeAt(o)&&++o;else if(r!==n)continue;return t.slice(i,e)}return c=!0,t.slice(i,a)}for(10===t.charCodeAt(a-1)&&--a,13===t.charCodeAt(a-1)&&--a;(r=l())!==go;){for(var h=[];r!==yo&&r!==go;)h.push(r),r=l();e&&null==(h=e(h,s++))||i.push(h)}return i}function i(e,n){return e.map((function(e){return n.map((function(t){return o(e[t])})).join(t)}))}function a(e){return e.map(o).join(t)}function o(t){return null==t?"":t instanceof Date?function(t){var e=t.getUTCHours(),n=t.getUTCMinutes(),r=t.getUTCSeconds(),i=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":function(t){return t<0?"-"+bo(-t,6):t>9999?"+"+bo(t,6):bo(t,4)}(t.getUTCFullYear())+"-"+bo(t.getUTCMonth()+1,2)+"-"+bo(t.getUTCDate(),2)+(i?"T"+bo(e,2)+":"+bo(n,2)+":"+bo(r,2)+"."+bo(i,3)+"Z":r?"T"+bo(e,2)+":"+bo(n,2)+":"+bo(r,2)+"Z":n||e?"T"+bo(e,2)+":"+bo(n,2)+"Z":"")}(t):e.test(t+="")?'"'+t.replace(/"/g,'""')+'"':t}return{parse:function(t,e){var n,i,a=r(t,(function(t,r){if(n)return n(t,r-1);i=t,n=e?function(t,e){var n=mo(t);return function(r,i){return e(n(r),i,t)}}(t,e):mo(t)}));return a.columns=i||[],a},parseRows:r,format:function(e,n){return null==n&&(n=vo(e)),[n.map(o).join(t)].concat(i(e,n)).join("\n")},formatBody:function(t,e){return null==e&&(e=vo(t)),i(t,e).join("\n")},formatRows:function(t){return t.map(a).join("\n")},formatRow:a,formatValue:o}}var xo=_o(","),wo=xo.parse,ko=xo.parseRows,To=xo.format,Eo=xo.formatBody,Co=xo.formatRows,So=xo.formatRow,Ao=xo.formatValue,Mo=_o("\t"),No=Mo.parse,Do=Mo.parseRows,Oo=Mo.format,Bo=Mo.formatBody,Lo=Mo.formatRows,Io=Mo.formatRow,Ro=Mo.formatValue;function Fo(t){for(var e in t){var n,r,i=t[e].trim();if(i)if("true"===i)i=!0;else if("false"===i)i=!1;else if("NaN"===i)i=NaN;else if(isNaN(n=+i)){if(!(r=i.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/)))continue;Po&&r[4]&&!r[7]&&(i=i.replace(/-/g,"/").replace(/T/," ")),i=new Date(i)}else i=n;else i=null;t[e]=i}return t}var Po=new Date("2019-01-01T00:00").getHours()||new Date("2019-07-01T00:00").getHours();function jo(t){return+t}function Yo(t){return t*t}function zo(t){return t*(2-t)}function Uo(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}var qo=function t(e){function n(t){return Math.pow(t,e)}return e=+e,n.exponent=t,n}(3),Ho=function t(e){function n(t){return 1-Math.pow(1-t,e)}return e=+e,n.exponent=t,n}(3),$o=function t(e){function n(t){return((t*=2)<=1?Math.pow(t,e):2-Math.pow(2-t,e))/2}return e=+e,n.exponent=t,n}(3),Wo=Math.PI,Vo=Wo/2;function Go(t){return 1==+t?1:1-Math.cos(t*Vo)}function Xo(t){return Math.sin(t*Vo)}function Zo(t){return(1-Math.cos(Wo*t))/2}function Qo(t){return 1.0009775171065494*(Math.pow(2,-10*t)-.0009765625)}function Ko(t){return Qo(1-+t)}function Jo(t){return 1-Qo(t)}function ts(t){return((t*=2)<=1?Qo(1-t):2-Qo(t-1))/2}function es(t){return 1-Math.sqrt(1-t*t)}function ns(t){return Math.sqrt(1- --t*t)}function rs(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}var is=7.5625;function as(t){return 1-os(1-t)}function os(t){return(t=+t)<.36363636363636365?is*t*t:t<.7272727272727273?is*(t-=.5454545454545454)*t+.75:t<.9090909090909091?is*(t-=.8181818181818182)*t+.9375:is*(t-=.9545454545454546)*t+.984375}function ss(t){return((t*=2)<=1?1-os(1-t):os(t-1)+1)/2}var cs=1.70158,us=function t(e){function n(t){return(t=+t)*t*(e*(t-1)+t)}return e=+e,n.overshoot=t,n}(cs),ls=function t(e){function n(t){return--t*t*((t+1)*e+t)+1}return e=+e,n.overshoot=t,n}(cs),hs=function t(e){function n(t){return((t*=2)<1?t*t*((e+1)*t-e):(t-=2)*t*((e+1)*t+e)+2)/2}return e=+e,n.overshoot=t,n}(cs),fs=2*Math.PI,ds=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=fs);function i(t){return e*Qo(- --t)*Math.sin((r-t)/n)}return i.amplitude=function(e){return t(e,n*fs)},i.period=function(n){return t(e,n)},i}(1,.3),ps=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=fs);function i(t){return 1-e*Qo(t=+t)*Math.sin((t+r)/n)}return i.amplitude=function(e){return t(e,n*fs)},i.period=function(n){return t(e,n)},i}(1,.3),ys=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=fs);function i(t){return((t=2*t-1)<0?e*Qo(-t)*Math.sin((r-t)/n):2-e*Qo(t)*Math.sin((r+t)/n))/2}return i.amplitude=function(e){return t(e,n*fs)},i.period=function(n){return t(e,n)},i}(1,.3);function gs(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.blob()}function ms(t,e){return fetch(t,e).then(gs)}function vs(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.arrayBuffer()}function bs(t,e){return fetch(t,e).then(vs)}function _s(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.text()}function xs(t,e){return fetch(t,e).then(_s)}function ws(t){return function(e,n,r){return 2===arguments.length&&"function"==typeof n&&(r=n,n=void 0),xs(e,n).then((function(e){return t(e,r)}))}}function ks(t,e,n,r){3===arguments.length&&"function"==typeof n&&(r=n,n=void 0);var i=_o(t);return xs(e,n).then((function(t){return i.parse(t,r)}))}var Ts=ws(wo),Es=ws(No);function Cs(t,e){return new Promise((function(n,r){var i=new Image;for(var a in e)i[a]=e[a];i.onerror=r,i.onload=function(){n(i)},i.src=t}))}function Ss(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);if(204!==t.status&&205!==t.status)return t.json()}function As(t,e){return fetch(t,e).then(Ss)}function Ms(t){return function(e,n){return xs(e,n).then((function(e){return(new DOMParser).parseFromString(e,t)}))}}const Ns=Ms("application/xml");var Ds=Ms("text/html"),Os=Ms("image/svg+xml");function Bs(t,e){var n;function r(){var r,i,a=n.length,o=0,s=0;for(r=0;r<a;++r)o+=(i=n[r]).x,s+=i.y;for(o=o/a-t,s=s/a-e,r=0;r<a;++r)(i=n[r]).x-=o,i.y-=s}return null==t&&(t=0),null==e&&(e=0),r.initialize=function(t){n=t},r.x=function(e){return arguments.length?(t=+e,r):t},r.y=function(t){return arguments.length?(e=+t,r):e},r}function Ls(t){return function(){return t}}function Is(){return 1e-6*(Math.random()-.5)}function Rs(t,e,n,r){if(isNaN(e)||isNaN(n))return t;var i,a,o,s,c,u,l,h,f,d=t._root,p={data:r},y=t._x0,g=t._y0,m=t._x1,v=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((u=e>=(a=(y+m)/2))?y=a:m=a,(l=n>=(o=(g+v)/2))?g=o:v=o,i=d,!(d=d[h=l<<1|u]))return i[h]=p,t;if(s=+t._x.call(null,d.data),c=+t._y.call(null,d.data),e===s&&n===c)return p.next=d,i?i[h]=p:t._root=p,t;do{i=i?i[h]=new Array(4):t._root=new Array(4),(u=e>=(a=(y+m)/2))?y=a:m=a,(l=n>=(o=(g+v)/2))?g=o:v=o}while((h=l<<1|u)==(f=(c>=o)<<1|s>=a));return i[f]=d,i[h]=p,t}function Fs(t,e,n,r,i){this.node=t,this.x0=e,this.y0=n,this.x1=r,this.y1=i}function Ps(t){return t[0]}function js(t){return t[1]}function Ys(t,e,n){var r=new zs(null==e?Ps:e,null==n?js:n,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function zs(t,e,n,r,i,a){this._x=t,this._y=e,this._x0=n,this._y0=r,this._x1=i,this._y1=a,this._root=void 0}function Us(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var qs=Ys.prototype=zs.prototype;function Hs(t){return t.x+t.vx}function $s(t){return t.y+t.vy}function Ws(t){var e,n,r=1,i=1;function a(){for(var t,a,s,c,u,l,h,f=e.length,d=0;d<i;++d)for(a=Ys(e,Hs,$s).visitAfter(o),t=0;t<f;++t)s=e[t],l=n[s.index],h=l*l,c=s.x+s.vx,u=s.y+s.vy,a.visit(p);function p(t,e,n,i,a){var o=t.data,f=t.r,d=l+f;if(!o)return e>c+d||i<c-d||n>u+d||a<u-d;if(o.index>s.index){var p=c-o.x-o.vx,y=u-o.y-o.vy,g=p*p+y*y;g<d*d&&(0===p&&(g+=(p=Is())*p),0===y&&(g+=(y=Is())*y),g=(d-(g=Math.sqrt(g)))/g*r,s.vx+=(p*=g)*(d=(f*=f)/(h+f)),s.vy+=(y*=g)*d,o.vx-=p*(d=1-d),o.vy-=y*d)}}}function o(t){if(t.data)return t.r=n[t.data.index];for(var e=t.r=0;e<4;++e)t[e]&&t[e].r>t.r&&(t.r=t[e].r)}function s(){if(e){var r,i,a=e.length;for(n=new Array(a),r=0;r<a;++r)i=e[r],n[i.index]=+t(i,r,e)}}return"function"!=typeof t&&(t=Ls(null==t?1:+t)),a.initialize=function(t){e=t,s()},a.iterations=function(t){return arguments.length?(i=+t,a):i},a.strength=function(t){return arguments.length?(r=+t,a):r},a.radius=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),s(),a):t},a}function Vs(t){return t.index}function Gs(t,e){var n=t.get(e);if(!n)throw new Error("missing: "+e);return n}function Xs(t){var e,n,r,i,a,o=Vs,s=function(t){return 1/Math.min(i[t.source.index],i[t.target.index])},c=Ls(30),u=1;function l(r){for(var i=0,o=t.length;i<u;++i)for(var s,c,l,h,f,d,p,y=0;y<o;++y)c=(s=t[y]).source,h=(l=s.target).x+l.vx-c.x-c.vx||Is(),f=l.y+l.vy-c.y-c.vy||Is(),h*=d=((d=Math.sqrt(h*h+f*f))-n[y])/d*r*e[y],f*=d,l.vx-=h*(p=a[y]),l.vy-=f*p,c.vx+=h*(p=1-p),c.vy+=f*p}function h(){if(r){var s,c,u=r.length,l=t.length,h=na(r,o);for(s=0,i=new Array(u);s<l;++s)(c=t[s]).index=s,"object"!=typeof c.source&&(c.source=Gs(h,c.source)),"object"!=typeof c.target&&(c.target=Gs(h,c.target)),i[c.source.index]=(i[c.source.index]||0)+1,i[c.target.index]=(i[c.target.index]||0)+1;for(s=0,a=new Array(l);s<l;++s)c=t[s],a[s]=i[c.source.index]/(i[c.source.index]+i[c.target.index]);e=new Array(l),f(),n=new Array(l),d()}}function f(){if(r)for(var n=0,i=t.length;n<i;++n)e[n]=+s(t[n],n,t)}function d(){if(r)for(var e=0,i=t.length;e<i;++e)n[e]=+c(t[e],e,t)}return null==t&&(t=[]),l.initialize=function(t){r=t,h()},l.links=function(e){return arguments.length?(t=e,h(),l):t},l.id=function(t){return arguments.length?(o=t,l):o},l.iterations=function(t){return arguments.length?(u=+t,l):u},l.strength=function(t){return arguments.length?(s="function"==typeof t?t:Ls(+t),f(),l):s},l.distance=function(t){return arguments.length?(c="function"==typeof t?t:Ls(+t),d(),l):c},l}function Zs(t){return t.x}function Qs(t){return t.y}qs.copy=function(){var t,e,n=new zs(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return n;if(!r.length)return n._root=Us(r),n;for(t=[{source:r,target:n._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(e=r.source[i])&&(e.length?t.push({source:e,target:r.target[i]=new Array(4)}):r.target[i]=Us(e));return n},qs.add=function(t){var e=+this._x.call(null,t),n=+this._y.call(null,t);return Rs(this.cover(e,n),e,n,t)},qs.addAll=function(t){var e,n,r,i,a=t.length,o=new Array(a),s=new Array(a),c=1/0,u=1/0,l=-1/0,h=-1/0;for(n=0;n<a;++n)isNaN(r=+this._x.call(null,e=t[n]))||isNaN(i=+this._y.call(null,e))||(o[n]=r,s[n]=i,r<c&&(c=r),r>l&&(l=r),i<u&&(u=i),i>h&&(h=i));if(c>l||u>h)return this;for(this.cover(c,u).cover(l,h),n=0;n<a;++n)Rs(this,o[n],s[n],t[n]);return this},qs.cover=function(t,e){if(isNaN(t=+t)||isNaN(e=+e))return this;var n=this._x0,r=this._y0,i=this._x1,a=this._y1;if(isNaN(n))i=(n=Math.floor(t))+1,a=(r=Math.floor(e))+1;else{for(var o,s,c=i-n,u=this._root;n>t||t>=i||r>e||e>=a;)switch(s=(e<r)<<1|t<n,(o=new Array(4))[s]=u,u=o,c*=2,s){case 0:i=n+c,a=r+c;break;case 1:n=i-c,a=r+c;break;case 2:i=n+c,r=a-c;break;case 3:n=i-c,r=a-c}this._root&&this._root.length&&(this._root=u)}return this._x0=n,this._y0=r,this._x1=i,this._y1=a,this},qs.data=function(){var t=[];return this.visit((function(e){if(!e.length)do{t.push(e.data)}while(e=e.next)})),t},qs.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},qs.find=function(t,e,n){var r,i,a,o,s,c,u,l=this._x0,h=this._y0,f=this._x1,d=this._y1,p=[],y=this._root;for(y&&p.push(new Fs(y,l,h,f,d)),null==n?n=1/0:(l=t-n,h=e-n,f=t+n,d=e+n,n*=n);c=p.pop();)if(!(!(y=c.node)||(i=c.x0)>f||(a=c.y0)>d||(o=c.x1)<l||(s=c.y1)<h))if(y.length){var g=(i+o)/2,m=(a+s)/2;p.push(new Fs(y[3],g,m,o,s),new Fs(y[2],i,m,g,s),new Fs(y[1],g,a,o,m),new Fs(y[0],i,a,g,m)),(u=(e>=m)<<1|t>=g)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-u],p[p.length-1-u]=c)}else{var v=t-+this._x.call(null,y.data),b=e-+this._y.call(null,y.data),_=v*v+b*b;if(_<n){var x=Math.sqrt(n=_);l=t-x,h=e-x,f=t+x,d=e+x,r=y.data}}return r},qs.remove=function(t){if(isNaN(a=+this._x.call(null,t))||isNaN(o=+this._y.call(null,t)))return this;var e,n,r,i,a,o,s,c,u,l,h,f,d=this._root,p=this._x0,y=this._y0,g=this._x1,m=this._y1;if(!d)return this;if(d.length)for(;;){if((u=a>=(s=(p+g)/2))?p=s:g=s,(l=o>=(c=(y+m)/2))?y=c:m=c,e=d,!(d=d[h=l<<1|u]))return this;if(!d.length)break;(e[h+1&3]||e[h+2&3]||e[h+3&3])&&(n=e,f=h)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):e?(i?e[h]=i:delete e[h],(d=e[0]||e[1]||e[2]||e[3])&&d===(e[3]||e[2]||e[1]||e[0])&&!d.length&&(n?n[f]=d:this._root=d),this):(this._root=i,this)},qs.removeAll=function(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this},qs.root=function(){return this._root},qs.size=function(){var t=0;return this.visit((function(e){if(!e.length)do{++t}while(e=e.next)})),t},qs.visit=function(t){var e,n,r,i,a,o,s=[],c=this._root;for(c&&s.push(new Fs(c,this._x0,this._y0,this._x1,this._y1));e=s.pop();)if(!t(c=e.node,r=e.x0,i=e.y0,a=e.x1,o=e.y1)&&c.length){var u=(r+a)/2,l=(i+o)/2;(n=c[3])&&s.push(new Fs(n,u,l,a,o)),(n=c[2])&&s.push(new Fs(n,r,l,u,o)),(n=c[1])&&s.push(new Fs(n,u,i,a,l)),(n=c[0])&&s.push(new Fs(n,r,i,u,l))}return this},qs.visitAfter=function(t){var e,n=[],r=[];for(this._root&&n.push(new Fs(this._root,this._x0,this._y0,this._x1,this._y1));e=n.pop();){var i=e.node;if(i.length){var a,o=e.x0,s=e.y0,c=e.x1,u=e.y1,l=(o+c)/2,h=(s+u)/2;(a=i[0])&&n.push(new Fs(a,o,s,l,h)),(a=i[1])&&n.push(new Fs(a,l,s,c,h)),(a=i[2])&&n.push(new Fs(a,o,h,l,u)),(a=i[3])&&n.push(new Fs(a,l,h,c,u))}r.push(e)}for(;e=r.pop();)t(e.node,e.x0,e.y0,e.x1,e.y1);return this},qs.x=function(t){return arguments.length?(this._x=t,this):this._x},qs.y=function(t){return arguments.length?(this._y=t,this):this._y};var Ks=Math.PI*(3-Math.sqrt(5));function Js(t){var e,n=1,r=.001,i=1-Math.pow(r,1/300),a=0,o=.6,s=na(),c=Vn(l),u=ft("tick","end");function l(){h(),u.call("tick",e),n<r&&(c.stop(),u.call("end",e))}function h(r){var c,u,l=t.length;void 0===r&&(r=1);for(var h=0;h<r;++h)for(n+=(a-n)*i,s.each((function(t){t(n)})),c=0;c<l;++c)null==(u=t[c]).fx?u.x+=u.vx*=o:(u.x=u.fx,u.vx=0),null==u.fy?u.y+=u.vy*=o:(u.y=u.fy,u.vy=0);return e}function f(){for(var e,n=0,r=t.length;n<r;++n){if((e=t[n]).index=n,null!=e.fx&&(e.x=e.fx),null!=e.fy&&(e.y=e.fy),isNaN(e.x)||isNaN(e.y)){var i=10*Math.sqrt(n),a=n*Ks;e.x=i*Math.cos(a),e.y=i*Math.sin(a)}(isNaN(e.vx)||isNaN(e.vy))&&(e.vx=e.vy=0)}}function d(e){return e.initialize&&e.initialize(t),e}return null==t&&(t=[]),f(),e={tick:h,restart:function(){return c.restart(l),e},stop:function(){return c.stop(),e},nodes:function(n){return arguments.length?(t=n,f(),s.each(d),e):t},alpha:function(t){return arguments.length?(n=+t,e):n},alphaMin:function(t){return arguments.length?(r=+t,e):r},alphaDecay:function(t){return arguments.length?(i=+t,e):+i},alphaTarget:function(t){return arguments.length?(a=+t,e):a},velocityDecay:function(t){return arguments.length?(o=1-t,e):1-o},force:function(t,n){return arguments.length>1?(null==n?s.remove(t):s.set(t,d(n)),e):s.get(t)},find:function(e,n,r){var i,a,o,s,c,u=0,l=t.length;for(null==r?r=1/0:r*=r,u=0;u<l;++u)(o=(i=e-(s=t[u]).x)*i+(a=n-s.y)*a)<r&&(c=s,r=o);return c},on:function(t,n){return arguments.length>1?(u.on(t,n),e):u.on(t)}}}function tc(){var t,e,n,r,i=Ls(-30),a=1,o=1/0,s=.81;function c(r){var i,a=t.length,o=Ys(t,Zs,Qs).visitAfter(l);for(n=r,i=0;i<a;++i)e=t[i],o.visit(h)}function u(){if(t){var e,n,a=t.length;for(r=new Array(a),e=0;e<a;++e)n=t[e],r[n.index]=+i(n,e,t)}}function l(t){var e,n,i,a,o,s=0,c=0;if(t.length){for(i=a=o=0;o<4;++o)(e=t[o])&&(n=Math.abs(e.value))&&(s+=e.value,c+=n,i+=n*e.x,a+=n*e.y);t.x=i/c,t.y=a/c}else{(e=t).x=e.data.x,e.y=e.data.y;do{s+=r[e.data.index]}while(e=e.next)}t.value=s}function h(t,i,c,u){if(!t.value)return!0;var l=t.x-e.x,h=t.y-e.y,f=u-i,d=l*l+h*h;if(f*f/s<d)return d<o&&(0===l&&(d+=(l=Is())*l),0===h&&(d+=(h=Is())*h),d<a&&(d=Math.sqrt(a*d)),e.vx+=l*t.value*n/d,e.vy+=h*t.value*n/d),!0;if(!(t.length||d>=o)){(t.data!==e||t.next)&&(0===l&&(d+=(l=Is())*l),0===h&&(d+=(h=Is())*h),d<a&&(d=Math.sqrt(a*d)));do{t.data!==e&&(f=r[t.data.index]*n/d,e.vx+=l*f,e.vy+=h*f)}while(t=t.next)}}return c.initialize=function(e){t=e,u()},c.strength=function(t){return arguments.length?(i="function"==typeof t?t:Ls(+t),u(),c):i},c.distanceMin=function(t){return arguments.length?(a=t*t,c):Math.sqrt(a)},c.distanceMax=function(t){return arguments.length?(o=t*t,c):Math.sqrt(o)},c.theta=function(t){return arguments.length?(s=t*t,c):Math.sqrt(s)},c}function ec(t,e,n){var r,i,a,o=Ls(.1);function s(t){for(var o=0,s=r.length;o<s;++o){var c=r[o],u=c.x-e||1e-6,l=c.y-n||1e-6,h=Math.sqrt(u*u+l*l),f=(a[o]-h)*i[o]*t/h;c.vx+=u*f,c.vy+=l*f}}function c(){if(r){var e,n=r.length;for(i=new Array(n),a=new Array(n),e=0;e<n;++e)a[e]=+t(r[e],e,r),i[e]=isNaN(a[e])?0:+o(r[e],e,r)}}return"function"!=typeof t&&(t=Ls(+t)),null==e&&(e=0),null==n&&(n=0),s.initialize=function(t){r=t,c()},s.strength=function(t){return arguments.length?(o="function"==typeof t?t:Ls(+t),c(),s):o},s.radius=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),c(),s):t},s.x=function(t){return arguments.length?(e=+t,s):e},s.y=function(t){return arguments.length?(n=+t,s):n},s}function nc(t){var e,n,r,i=Ls(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vx+=(r[a]-i.x)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=Ls(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:Ls(+t),o(),a):i},a.x=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),o(),a):t},a}function rc(t){var e,n,r,i=Ls(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vy+=(r[a]-i.y)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=Ls(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:Ls(+t),o(),a):i},a.y=function(e){return arguments.length?(t="function"==typeof e?e:Ls(+e),o(),a):t},a}function ic(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,r=t.slice(0,n);return[r.length>1?r[0]+r.slice(2):r,+t.slice(n+1)]}function ac(t){return(t=ic(Math.abs(t)))?t[1]:NaN}var oc,sc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function cc(t){if(!(e=sc.exec(t)))throw new Error("invalid format: "+t);var e;return new uc({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}function uc(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function lc(t,e){var n=ic(t,e);if(!n)return t+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}cc.prototype=uc.prototype,uc.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};const hc={"%":function(t,e){return(100*t).toFixed(e)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:function(t,e){return t.toExponential(e)},f:function(t,e){return t.toFixed(e)},g:function(t,e){return t.toPrecision(e)},o:function(t){return Math.round(t).toString(8)},p:function(t,e){return lc(100*t,e)},r:lc,s:function(t,e){var n=ic(t,e);if(!n)return t+"";var r=n[0],i=n[1],a=i-(oc=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,o=r.length;return a===o?r:a>o?r+new Array(a-o+1).join("0"):a>0?r.slice(0,a)+"."+r.slice(a):"0."+new Array(1-a).join("0")+ic(t,Math.max(0,e+a-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}};function fc(t){return t}var dc,pc,yc,gc=Array.prototype.map,mc=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function vc(t){var e,n,r=void 0===t.grouping||void 0===t.thousands?fc:(e=gc.call(t.grouping,Number),n=t.thousands+"",function(t,r){for(var i=t.length,a=[],o=0,s=e[0],c=0;i>0&&s>0&&(c+s+1>r&&(s=Math.max(1,r-c)),a.push(t.substring(i-=s,i+s)),!((c+=s+1)>r));)s=e[o=(o+1)%e.length];return a.reverse().join(n)}),i=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",o=void 0===t.decimal?".":t.decimal+"",s=void 0===t.numerals?fc:function(t){return function(e){return e.replace(/[0-9]/g,(function(e){return t[+e]}))}}(gc.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",u=void 0===t.minus?"-":t.minus+"",l=void 0===t.nan?"NaN":t.nan+"";function h(t){var e=(t=cc(t)).fill,n=t.align,h=t.sign,f=t.symbol,d=t.zero,p=t.width,y=t.comma,g=t.precision,m=t.trim,v=t.type;"n"===v?(y=!0,v="g"):hc[v]||(void 0===g&&(g=12),m=!0,v="g"),(d||"0"===e&&"="===n)&&(d=!0,e="0",n="=");var b="$"===f?i:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?a:/[%p]/.test(v)?c:"",x=hc[v],w=/[defgprs%]/.test(v);function k(t){var i,a,c,f=b,k=_;if("c"===v)k=x(t)+k,t="";else{var T=(t=+t)<0||1/t<0;if(t=isNaN(t)?l:x(Math.abs(t),g),m&&(t=function(t){t:for(var e,n=t.length,r=1,i=-1;r<n;++r)switch(t[r]){case".":i=e=r;break;case"0":0===i&&(i=r),e=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(e+1):t}(t)),T&&0==+t&&"+"!==h&&(T=!1),f=(T?"("===h?h:u:"-"===h||"("===h?"":h)+f,k=("s"===v?mc[8+oc/3]:"")+k+(T&&"("===h?")":""),w)for(i=-1,a=t.length;++i<a;)if(48>(c=t.charCodeAt(i))||c>57){k=(46===c?o+t.slice(i+1):t.slice(i))+k,t=t.slice(0,i);break}}y&&!d&&(t=r(t,1/0));var E=f.length+t.length+k.length,C=E<p?new Array(p-E+1).join(e):"";switch(y&&d&&(t=r(C+t,C.length?p-k.length:1/0),C=""),n){case"<":t=f+t+k+C;break;case"=":t=f+C+t+k;break;case"^":t=C.slice(0,E=C.length>>1)+f+t+k+C.slice(E);break;default:t=C+f+t+k}return s(t)}return g=void 0===g?6:/[gprs]/.test(v)?Math.max(1,Math.min(21,g)):Math.max(0,Math.min(20,g)),k.toString=function(){return t+""},k}return{format:h,formatPrefix:function(t,e){var n=h(((t=cc(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(ac(e)/3))),i=Math.pow(10,-r),a=mc[8+r/3];return function(t){return n(i*t)+a}}}}function bc(t){return dc=vc(t),pc=dc.format,yc=dc.formatPrefix,dc}function _c(t){return Math.max(0,-ac(Math.abs(t)))}function xc(t,e){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(ac(e)/3)))-ac(Math.abs(t)))}function wc(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,ac(e)-ac(t))+1}function kc(){return new Tc}function Tc(){this.reset()}bc({decimal:".",thousands:",",grouping:[3],currency:["$",""],minus:"-"}),Tc.prototype={constructor:Tc,reset:function(){this.s=this.t=0},add:function(t){Cc(Ec,t,this.t),Cc(this,Ec.s,this.s),this.s?this.t+=Ec.t:this.s=Ec.t},valueOf:function(){return this.s}};var Ec=new Tc;function Cc(t,e,n){var r=t.s=e+n,i=r-e,a=r-i;t.t=e-a+(n-i)}var Sc=1e-6,Ac=1e-12,Mc=Math.PI,Nc=Mc/2,Dc=Mc/4,Oc=2*Mc,Bc=180/Mc,Lc=Mc/180,Ic=Math.abs,Rc=Math.atan,Fc=Math.atan2,Pc=Math.cos,jc=Math.ceil,Yc=Math.exp,zc=(Math.floor,Math.log),Uc=Math.pow,qc=Math.sin,Hc=Math.sign||function(t){return t>0?1:t<0?-1:0},$c=Math.sqrt,Wc=Math.tan;function Vc(t){return t>1?0:t<-1?Mc:Math.acos(t)}function Gc(t){return t>1?Nc:t<-1?-Nc:Math.asin(t)}function Xc(t){return(t=qc(t/2))*t}function Zc(){}function Qc(t,e){t&&Jc.hasOwnProperty(t.type)&&Jc[t.type](t,e)}var Kc={Feature:function(t,e){Qc(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)Qc(n[r].geometry,e)}},Jc={Sphere:function(t,e){e.sphere()},Point:function(t,e){t=t.coordinates,e.point(t[0],t[1],t[2])},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)t=n[r],e.point(t[0],t[1],t[2])},LineString:function(t,e){tu(t.coordinates,e,0)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)tu(n[r],e,0)},Polygon:function(t,e){eu(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)eu(n[r],e)},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)Qc(n[r],e)}};function tu(t,e,n){var r,i=-1,a=t.length-n;for(e.lineStart();++i<a;)r=t[i],e.point(r[0],r[1],r[2]);e.lineEnd()}function eu(t,e){var n=-1,r=t.length;for(e.polygonStart();++n<r;)tu(t[n],e,1);e.polygonEnd()}function nu(t,e){t&&Kc.hasOwnProperty(t.type)?Kc[t.type](t,e):Qc(t,e)}var ru,iu,au,ou,su,cu=kc(),uu=kc(),lu={point:Zc,lineStart:Zc,lineEnd:Zc,polygonStart:function(){cu.reset(),lu.lineStart=hu,lu.lineEnd=fu},polygonEnd:function(){var t=+cu;uu.add(t<0?Oc+t:t),this.lineStart=this.lineEnd=this.point=Zc},sphere:function(){uu.add(Oc)}};function hu(){lu.point=du}function fu(){pu(ru,iu)}function du(t,e){lu.point=pu,ru=t,iu=e,au=t*=Lc,ou=Pc(e=(e*=Lc)/2+Dc),su=qc(e)}function pu(t,e){var n=(t*=Lc)-au,r=n>=0?1:-1,i=r*n,a=Pc(e=(e*=Lc)/2+Dc),o=qc(e),s=su*o,c=ou*a+s*Pc(i),u=s*r*qc(i);cu.add(Fc(u,c)),au=t,ou=a,su=o}function yu(t){return uu.reset(),nu(t,lu),2*uu}function gu(t){return[Fc(t[1],t[0]),Gc(t[2])]}function mu(t){var e=t[0],n=t[1],r=Pc(n);return[r*Pc(e),r*qc(e),qc(n)]}function vu(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function bu(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function _u(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}function xu(t,e){return[t[0]*e,t[1]*e,t[2]*e]}function wu(t){var e=$c(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t[2]/=e}var ku,Tu,Eu,Cu,Su,Au,Mu,Nu,Du,Ou,Bu,Lu,Iu,Ru,Fu,Pu,ju,Yu,zu,Uu,qu,Hu,$u,Wu,Vu,Gu,Xu=kc(),Zu={point:Qu,lineStart:Ju,lineEnd:tl,polygonStart:function(){Zu.point=el,Zu.lineStart=nl,Zu.lineEnd=rl,Xu.reset(),lu.polygonStart()},polygonEnd:function(){lu.polygonEnd(),Zu.point=Qu,Zu.lineStart=Ju,Zu.lineEnd=tl,cu<0?(ku=-(Eu=180),Tu=-(Cu=90)):Xu>Sc?Cu=90:Xu<-1e-6&&(Tu=-90),Ou[0]=ku,Ou[1]=Eu},sphere:function(){ku=-(Eu=180),Tu=-(Cu=90)}};function Qu(t,e){Du.push(Ou=[ku=t,Eu=t]),e<Tu&&(Tu=e),e>Cu&&(Cu=e)}function Ku(t,e){var n=mu([t*Lc,e*Lc]);if(Nu){var r=bu(Nu,n),i=bu([r[1],-r[0],0],r);wu(i),i=gu(i);var a,o=t-Su,s=o>0?1:-1,c=i[0]*Bc*s,u=Ic(o)>180;u^(s*Su<c&&c<s*t)?(a=i[1]*Bc)>Cu&&(Cu=a):u^(s*Su<(c=(c+360)%360-180)&&c<s*t)?(a=-i[1]*Bc)<Tu&&(Tu=a):(e<Tu&&(Tu=e),e>Cu&&(Cu=e)),u?t<Su?il(ku,t)>il(ku,Eu)&&(Eu=t):il(t,Eu)>il(ku,Eu)&&(ku=t):Eu>=ku?(t<ku&&(ku=t),t>Eu&&(Eu=t)):t>Su?il(ku,t)>il(ku,Eu)&&(Eu=t):il(t,Eu)>il(ku,Eu)&&(ku=t)}else Du.push(Ou=[ku=t,Eu=t]);e<Tu&&(Tu=e),e>Cu&&(Cu=e),Nu=n,Su=t}function Ju(){Zu.point=Ku}function tl(){Ou[0]=ku,Ou[1]=Eu,Zu.point=Qu,Nu=null}function el(t,e){if(Nu){var n=t-Su;Xu.add(Ic(n)>180?n+(n>0?360:-360):n)}else Au=t,Mu=e;lu.point(t,e),Ku(t,e)}function nl(){lu.lineStart()}function rl(){el(Au,Mu),lu.lineEnd(),Ic(Xu)>Sc&&(ku=-(Eu=180)),Ou[0]=ku,Ou[1]=Eu,Nu=null}function il(t,e){return(e-=t)<0?e+360:e}function al(t,e){return t[0]-e[0]}function ol(t,e){return t[0]<=t[1]?t[0]<=e&&e<=t[1]:e<t[0]||t[1]<e}function sl(t){var e,n,r,i,a,o,s;if(Cu=Eu=-(ku=Tu=1/0),Du=[],nu(t,Zu),n=Du.length){for(Du.sort(al),e=1,a=[r=Du[0]];e<n;++e)ol(r,(i=Du[e])[0])||ol(r,i[1])?(il(r[0],i[1])>il(r[0],r[1])&&(r[1]=i[1]),il(i[0],r[1])>il(r[0],r[1])&&(r[0]=i[0])):a.push(r=i);for(o=-1/0,e=0,r=a[n=a.length-1];e<=n;r=i,++e)i=a[e],(s=il(r[1],i[0]))>o&&(o=s,ku=i[0],Eu=r[1])}return Du=Ou=null,ku===1/0||Tu===1/0?[[NaN,NaN],[NaN,NaN]]:[[ku,Tu],[Eu,Cu]]}var cl={sphere:Zc,point:ul,lineStart:hl,lineEnd:pl,polygonStart:function(){cl.lineStart=yl,cl.lineEnd=gl},polygonEnd:function(){cl.lineStart=hl,cl.lineEnd=pl}};function ul(t,e){t*=Lc;var n=Pc(e*=Lc);ll(n*Pc(t),n*qc(t),qc(e))}function ll(t,e,n){++Bu,Iu+=(t-Iu)/Bu,Ru+=(e-Ru)/Bu,Fu+=(n-Fu)/Bu}function hl(){cl.point=fl}function fl(t,e){t*=Lc;var n=Pc(e*=Lc);Wu=n*Pc(t),Vu=n*qc(t),Gu=qc(e),cl.point=dl,ll(Wu,Vu,Gu)}function dl(t,e){t*=Lc;var n=Pc(e*=Lc),r=n*Pc(t),i=n*qc(t),a=qc(e),o=Fc($c((o=Vu*a-Gu*i)*o+(o=Gu*r-Wu*a)*o+(o=Wu*i-Vu*r)*o),Wu*r+Vu*i+Gu*a);Lu+=o,Pu+=o*(Wu+(Wu=r)),ju+=o*(Vu+(Vu=i)),Yu+=o*(Gu+(Gu=a)),ll(Wu,Vu,Gu)}function pl(){cl.point=ul}function yl(){cl.point=ml}function gl(){vl(Hu,$u),cl.point=ul}function ml(t,e){Hu=t,$u=e,t*=Lc,e*=Lc,cl.point=vl;var n=Pc(e);Wu=n*Pc(t),Vu=n*qc(t),Gu=qc(e),ll(Wu,Vu,Gu)}function vl(t,e){t*=Lc;var n=Pc(e*=Lc),r=n*Pc(t),i=n*qc(t),a=qc(e),o=Vu*a-Gu*i,s=Gu*r-Wu*a,c=Wu*i-Vu*r,u=$c(o*o+s*s+c*c),l=Gc(u),h=u&&-l/u;zu+=h*o,Uu+=h*s,qu+=h*c,Lu+=l,Pu+=l*(Wu+(Wu=r)),ju+=l*(Vu+(Vu=i)),Yu+=l*(Gu+(Gu=a)),ll(Wu,Vu,Gu)}function bl(t){Bu=Lu=Iu=Ru=Fu=Pu=ju=Yu=zu=Uu=qu=0,nu(t,cl);var e=zu,n=Uu,r=qu,i=e*e+n*n+r*r;return i<Ac&&(e=Pu,n=ju,r=Yu,Lu<Sc&&(e=Iu,n=Ru,r=Fu),(i=e*e+n*n+r*r)<Ac)?[NaN,NaN]:[Fc(n,e)*Bc,Gc(r/$c(i))*Bc]}function _l(t){return function(){return t}}function xl(t,e){function n(n,r){return n=t(n,r),e(n[0],n[1])}return t.invert&&e.invert&&(n.invert=function(n,r){return(n=e.invert(n,r))&&t.invert(n[0],n[1])}),n}function wl(t,e){return[Ic(t)>Mc?t+Math.round(-t/Oc)*Oc:t,e]}function kl(t,e,n){return(t%=Oc)?e||n?xl(El(t),Cl(e,n)):El(t):e||n?Cl(e,n):wl}function Tl(t){return function(e,n){return[(e+=t)>Mc?e-Oc:e<-Mc?e+Oc:e,n]}}function El(t){var e=Tl(t);return e.invert=Tl(-t),e}function Cl(t,e){var n=Pc(t),r=qc(t),i=Pc(e),a=qc(e);function o(t,e){var o=Pc(e),s=Pc(t)*o,c=qc(t)*o,u=qc(e),l=u*n+s*r;return[Fc(c*i-l*a,s*n-u*r),Gc(l*i+c*a)]}return o.invert=function(t,e){var o=Pc(e),s=Pc(t)*o,c=qc(t)*o,u=qc(e),l=u*i-c*a;return[Fc(c*i+u*a,s*n+l*r),Gc(l*n-s*r)]},o}function Sl(t){function e(e){return(e=t(e[0]*Lc,e[1]*Lc))[0]*=Bc,e[1]*=Bc,e}return t=kl(t[0]*Lc,t[1]*Lc,t.length>2?t[2]*Lc:0),e.invert=function(e){return(e=t.invert(e[0]*Lc,e[1]*Lc))[0]*=Bc,e[1]*=Bc,e},e}function Al(t,e,n,r,i,a){if(n){var o=Pc(e),s=qc(e),c=r*n;null==i?(i=e+r*Oc,a=e-c/2):(i=Ml(o,i),a=Ml(o,a),(r>0?i<a:i>a)&&(i+=r*Oc));for(var u,l=i;r>0?l>a:l<a;l-=c)u=gu([o,-s*Pc(l),-s*qc(l)]),t.point(u[0],u[1])}}function Ml(t,e){(e=mu(e))[0]-=t,wu(e);var n=Vc(-e[1]);return((-e[2]<0?-n:n)+Oc-Sc)%Oc}function Nl(){var t,e,n=_l([0,0]),r=_l(90),i=_l(6),a={point:function(n,r){t.push(n=e(n,r)),n[0]*=Bc,n[1]*=Bc}};function o(){var o=n.apply(this,arguments),s=r.apply(this,arguments)*Lc,c=i.apply(this,arguments)*Lc;return t=[],e=kl(-o[0]*Lc,-o[1]*Lc,0).invert,Al(a,s,c,1),o={type:"Polygon",coordinates:[t]},t=e=null,o}return o.center=function(t){return arguments.length?(n="function"==typeof t?t:_l([+t[0],+t[1]]),o):n},o.radius=function(t){return arguments.length?(r="function"==typeof t?t:_l(+t),o):r},o.precision=function(t){return arguments.length?(i="function"==typeof t?t:_l(+t),o):i},o}function Dl(){var t,e=[];return{point:function(e,n,r){t.push([e,n,r])},lineStart:function(){e.push(t=[])},lineEnd:Zc,rejoin:function(){e.length>1&&e.push(e.pop().concat(e.shift()))},result:function(){var n=e;return e=[],t=null,n}}}function Ol(t,e){return Ic(t[0]-e[0])<Sc&&Ic(t[1]-e[1])<Sc}function Bl(t,e,n,r){this.x=t,this.z=e,this.o=n,this.e=r,this.v=!1,this.n=this.p=null}function Ll(t,e,n,r,i){var a,o,s=[],c=[];if(t.forEach((function(t){if(!((e=t.length-1)<=0)){var e,n,r=t[0],o=t[e];if(Ol(r,o)){if(!r[2]&&!o[2]){for(i.lineStart(),a=0;a<e;++a)i.point((r=t[a])[0],r[1]);return void i.lineEnd()}o[0]+=2e-6}s.push(n=new Bl(r,t,null,!0)),c.push(n.o=new Bl(r,null,n,!1)),s.push(n=new Bl(o,t,null,!1)),c.push(n.o=new Bl(o,null,n,!0))}})),s.length){for(c.sort(e),Il(s),Il(c),a=0,o=c.length;a<o;++a)c[a].e=n=!n;for(var u,l,h=s[0];;){for(var f=h,d=!0;f.v;)if((f=f.n)===h)return;u=f.z,i.lineStart();do{if(f.v=f.o.v=!0,f.e){if(d)for(a=0,o=u.length;a<o;++a)i.point((l=u[a])[0],l[1]);else r(f.x,f.n.x,1,i);f=f.n}else{if(d)for(u=f.p.z,a=u.length-1;a>=0;--a)i.point((l=u[a])[0],l[1]);else r(f.x,f.p.x,-1,i);f=f.p}u=(f=f.o).z,d=!d}while(!f.v);i.lineEnd()}}}function Il(t){if(e=t.length){for(var e,n,r=0,i=t[0];++r<e;)i.n=n=t[r],n.p=i,i=n;i.n=n=t[0],n.p=i}}wl.invert=wl;var Rl=kc();function Fl(t){return Ic(t[0])<=Mc?t[0]:Hc(t[0])*((Ic(t[0])+Mc)%Oc-Mc)}function Pl(t,e){var n=Fl(e),r=e[1],i=qc(r),a=[qc(n),-Pc(n),0],o=0,s=0;Rl.reset(),1===i?r=Nc+Sc:-1===i&&(r=-Nc-Sc);for(var c=0,u=t.length;c<u;++c)if(h=(l=t[c]).length)for(var l,h,f=l[h-1],d=Fl(f),p=f[1]/2+Dc,y=qc(p),g=Pc(p),m=0;m<h;++m,d=b,y=x,g=w,f=v){var v=l[m],b=Fl(v),_=v[1]/2+Dc,x=qc(_),w=Pc(_),k=b-d,T=k>=0?1:-1,E=T*k,C=E>Mc,S=y*x;if(Rl.add(Fc(S*T*qc(E),g*w+S*Pc(E))),o+=C?k+T*Oc:k,C^d>=n^b>=n){var A=bu(mu(f),mu(v));wu(A);var M=bu(a,A);wu(M);var N=(C^k>=0?-1:1)*Gc(M[2]);(r>N||r===N&&(A[0]||A[1]))&&(s+=C^k>=0?1:-1)}}return(o<-1e-6||o<Sc&&Rl<-1e-6)^1&s}function jl(t,e,n,r){return function(i){var a,o,s,c=e(i),u=Dl(),l=e(u),h=!1,f={point:d,lineStart:y,lineEnd:g,polygonStart:function(){f.point=m,f.lineStart=v,f.lineEnd=b,o=[],a=[]},polygonEnd:function(){f.point=d,f.lineStart=y,f.lineEnd=g,o=P(o);var t=Pl(a,r);o.length?(h||(i.polygonStart(),h=!0),Ll(o,zl,t,n,i)):t&&(h||(i.polygonStart(),h=!0),i.lineStart(),n(null,null,1,i),i.lineEnd()),h&&(i.polygonEnd(),h=!1),o=a=null},sphere:function(){i.polygonStart(),i.lineStart(),n(null,null,1,i),i.lineEnd(),i.polygonEnd()}};function d(e,n){t(e,n)&&i.point(e,n)}function p(t,e){c.point(t,e)}function y(){f.point=p,c.lineStart()}function g(){f.point=d,c.lineEnd()}function m(t,e){s.push([t,e]),l.point(t,e)}function v(){l.lineStart(),s=[]}function b(){m(s[0][0],s[0][1]),l.lineEnd();var t,e,n,r,c=l.clean(),f=u.result(),d=f.length;if(s.pop(),a.push(s),s=null,d)if(1&c){if((e=(n=f[0]).length-1)>0){for(h||(i.polygonStart(),h=!0),i.lineStart(),t=0;t<e;++t)i.point((r=n[t])[0],r[1]);i.lineEnd()}}else d>1&&2&c&&f.push(f.pop().concat(f.shift())),o.push(f.filter(Yl))}return f}}function Yl(t){return t.length>1}function zl(t,e){return((t=t.x)[0]<0?t[1]-Nc-Sc:Nc-t[1])-((e=e.x)[0]<0?e[1]-Nc-Sc:Nc-e[1])}const Ul=jl((function(){return!0}),(function(t){var e,n=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),e=1},point:function(a,o){var s=a>0?Mc:-Mc,c=Ic(a-n);Ic(c-Mc)<Sc?(t.point(n,r=(r+o)/2>0?Nc:-Nc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),t.point(a,r),e=0):i!==s&&c>=Mc&&(Ic(n-i)<Sc&&(n-=i*Sc),Ic(a-s)<Sc&&(a-=s*Sc),r=function(t,e,n,r){var i,a,o=qc(t-n);return Ic(o)>Sc?Rc((qc(e)*(a=Pc(r))*qc(n)-qc(r)*(i=Pc(e))*qc(t))/(i*a*o)):(e+r)/2}(n,r,a,o),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),e=0),t.point(n=a,r=o),i=s},lineEnd:function(){t.lineEnd(),n=r=NaN},clean:function(){return 2-e}}}),(function(t,e,n,r){var i;if(null==t)i=n*Nc,r.point(-Mc,i),r.point(0,i),r.point(Mc,i),r.point(Mc,0),r.point(Mc,-i),r.point(0,-i),r.point(-Mc,-i),r.point(-Mc,0),r.point(-Mc,i);else if(Ic(t[0]-e[0])>Sc){var a=t[0]<e[0]?Mc:-Mc;i=n*a/2,r.point(-a,i),r.point(0,i),r.point(a,i)}else r.point(e[0],e[1])}),[-Mc,-Nc]);function ql(t){var e=Pc(t),n=6*Lc,r=e>0,i=Ic(e)>Sc;function a(t,n){return Pc(t)*Pc(n)>e}function o(t,n,r){var i=[1,0,0],a=bu(mu(t),mu(n)),o=vu(a,a),s=a[0],c=o-s*s;if(!c)return!r&&t;var u=e*o/c,l=-e*s/c,h=bu(i,a),f=xu(i,u);_u(f,xu(a,l));var d=h,p=vu(f,d),y=vu(d,d),g=p*p-y*(vu(f,f)-1);if(!(g<0)){var m=$c(g),v=xu(d,(-p-m)/y);if(_u(v,f),v=gu(v),!r)return v;var b,_=t[0],x=n[0],w=t[1],k=n[1];x<_&&(b=_,_=x,x=b);var T=x-_,E=Ic(T-Mc)<Sc;if(!E&&k<w&&(b=w,w=k,k=b),E||T<Sc?E?w+k>0^v[1]<(Ic(v[0]-_)<Sc?w:k):w<=v[1]&&v[1]<=k:T>Mc^(_<=v[0]&&v[0]<=x)){var C=xu(d,(-p+m)/y);return _u(C,f),[v,gu(C)]}}}function s(e,n){var i=r?t:Mc-t,a=0;return e<-i?a|=1:e>i&&(a|=2),n<-i?a|=4:n>i&&(a|=8),a}return jl(a,(function(t){var e,n,c,u,l;return{lineStart:function(){u=c=!1,l=1},point:function(h,f){var d,p=[h,f],y=a(h,f),g=r?y?0:s(h,f):y?s(h+(h<0?Mc:-Mc),f):0;if(!e&&(u=c=y)&&t.lineStart(),y!==c&&(!(d=o(e,p))||Ol(e,d)||Ol(p,d))&&(p[2]=1),y!==c)l=0,y?(t.lineStart(),d=o(p,e),t.point(d[0],d[1])):(d=o(e,p),t.point(d[0],d[1],2),t.lineEnd()),e=d;else if(i&&e&&r^y){var m;g&n||!(m=o(p,e,!0))||(l=0,r?(t.lineStart(),t.point(m[0][0],m[0][1]),t.point(m[1][0],m[1][1]),t.lineEnd()):(t.point(m[1][0],m[1][1]),t.lineEnd(),t.lineStart(),t.point(m[0][0],m[0][1],3)))}!y||e&&Ol(e,p)||t.point(p[0],p[1]),e=p,c=y,n=g},lineEnd:function(){c&&t.lineEnd(),e=null},clean:function(){return l|(u&&c)<<1}}}),(function(e,r,i,a){Al(a,t,n,i,e,r)}),r?[0,-t]:[-Mc,t-Mc])}var Hl=1e9,$l=-Hl;function Wl(t,e,n,r){function i(i,a){return t<=i&&i<=n&&e<=a&&a<=r}function a(i,a,s,u){var l=0,h=0;if(null==i||(l=o(i,s))!==(h=o(a,s))||c(i,a)<0^s>0)do{u.point(0===l||3===l?t:n,l>1?r:e)}while((l=(l+s+4)%4)!==h);else u.point(a[0],a[1])}function o(r,i){return Ic(r[0]-t)<Sc?i>0?0:3:Ic(r[0]-n)<Sc?i>0?2:1:Ic(r[1]-e)<Sc?i>0?1:0:i>0?3:2}function s(t,e){return c(t.x,e.x)}function c(t,e){var n=o(t,1),r=o(e,1);return n!==r?n-r:0===n?e[1]-t[1]:1===n?t[0]-e[0]:2===n?t[1]-e[1]:e[0]-t[0]}return function(o){var c,u,l,h,f,d,p,y,g,m,v,b=o,_=Dl(),x={point:w,lineStart:function(){x.point=k,u&&u.push(l=[]),m=!0,g=!1,p=y=NaN},lineEnd:function(){c&&(k(h,f),d&&g&&_.rejoin(),c.push(_.result())),x.point=w,g&&b.lineEnd()},polygonStart:function(){b=_,c=[],u=[],v=!0},polygonEnd:function(){var e=function(){for(var e=0,n=0,i=u.length;n<i;++n)for(var a,o,s=u[n],c=1,l=s.length,h=s[0],f=h[0],d=h[1];c<l;++c)a=f,o=d,f=(h=s[c])[0],d=h[1],o<=r?d>r&&(f-a)*(r-o)>(d-o)*(t-a)&&++e:d<=r&&(f-a)*(r-o)<(d-o)*(t-a)&&--e;return e}(),n=v&&e,i=(c=P(c)).length;(n||i)&&(o.polygonStart(),n&&(o.lineStart(),a(null,null,1,o),o.lineEnd()),i&&Ll(c,s,e,a,o),o.polygonEnd()),b=o,c=u=l=null}};function w(t,e){i(t,e)&&b.point(t,e)}function k(a,o){var s=i(a,o);if(u&&l.push([a,o]),m)h=a,f=o,d=s,m=!1,s&&(b.lineStart(),b.point(a,o));else if(s&&g)b.point(a,o);else{var c=[p=Math.max($l,Math.min(Hl,p)),y=Math.max($l,Math.min(Hl,y))],_=[a=Math.max($l,Math.min(Hl,a)),o=Math.max($l,Math.min(Hl,o))];!function(t,e,n,r,i,a){var o,s=t[0],c=t[1],u=0,l=1,h=e[0]-s,f=e[1]-c;if(o=n-s,h||!(o>0)){if(o/=h,h<0){if(o<u)return;o<l&&(l=o)}else if(h>0){if(o>l)return;o>u&&(u=o)}if(o=i-s,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>u&&(u=o)}else if(h>0){if(o<u)return;o<l&&(l=o)}if(o=r-c,f||!(o>0)){if(o/=f,f<0){if(o<u)return;o<l&&(l=o)}else if(f>0){if(o>l)return;o>u&&(u=o)}if(o=a-c,f||!(o<0)){if(o/=f,f<0){if(o>l)return;o>u&&(u=o)}else if(f>0){if(o<u)return;o<l&&(l=o)}return u>0&&(t[0]=s+u*h,t[1]=c+u*f),l<1&&(e[0]=s+l*h,e[1]=c+l*f),!0}}}}}(c,_,t,e,n,r)?s&&(b.lineStart(),b.point(a,o),v=!1):(g||(b.lineStart(),b.point(c[0],c[1])),b.point(_[0],_[1]),s||b.lineEnd(),v=!1)}p=a,y=o,g=s}return x}}function Vl(){var t,e,n,r=0,i=0,a=960,o=500;return n={stream:function(n){return t&&e===n?t:t=Wl(r,i,a,o)(e=n)},extent:function(s){return arguments.length?(r=+s[0][0],i=+s[0][1],a=+s[1][0],o=+s[1][1],t=e=null,n):[[r,i],[a,o]]}}}var Gl,Xl,Zl,Ql=kc(),Kl={sphere:Zc,point:Zc,lineStart:function(){Kl.point=th,Kl.lineEnd=Jl},lineEnd:Zc,polygonStart:Zc,polygonEnd:Zc};function Jl(){Kl.point=Kl.lineEnd=Zc}function th(t,e){Gl=t*=Lc,Xl=qc(e*=Lc),Zl=Pc(e),Kl.point=eh}function eh(t,e){t*=Lc;var n=qc(e*=Lc),r=Pc(e),i=Ic(t-Gl),a=Pc(i),o=r*qc(i),s=Zl*n-Xl*r*a,c=Xl*n+Zl*r*a;Ql.add(Fc($c(o*o+s*s),c)),Gl=t,Xl=n,Zl=r}function nh(t){return Ql.reset(),nu(t,Kl),+Ql}var rh=[null,null],ih={type:"LineString",coordinates:rh};function ah(t,e){return rh[0]=t,rh[1]=e,nh(ih)}var oh={Feature:function(t,e){return ch(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)if(ch(n[r].geometry,e))return!0;return!1}},sh={Sphere:function(){return!0},Point:function(t,e){return uh(t.coordinates,e)},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(uh(n[r],e))return!0;return!1},LineString:function(t,e){return lh(t.coordinates,e)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(lh(n[r],e))return!0;return!1},Polygon:function(t,e){return hh(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(hh(n[r],e))return!0;return!1},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)if(ch(n[r],e))return!0;return!1}};function ch(t,e){return!(!t||!sh.hasOwnProperty(t.type))&&sh[t.type](t,e)}function uh(t,e){return 0===ah(t,e)}function lh(t,e){for(var n,r,i,a=0,o=t.length;a<o;a++){if(0===(r=ah(t[a],e)))return!0;if(a>0&&(i=ah(t[a],t[a-1]))>0&&n<=i&&r<=i&&(n+r-i)*(1-Math.pow((n-r)/i,2))<Ac*i)return!0;n=r}return!1}function hh(t,e){return!!Pl(t.map(fh),dh(e))}function fh(t){return(t=t.map(dh)).pop(),t}function dh(t){return[t[0]*Lc,t[1]*Lc]}function ph(t,e){return(t&&oh.hasOwnProperty(t.type)?oh[t.type]:ch)(t,e)}function yh(t,e,n){var r=k(t,e-Sc,n).concat(e);return function(t){return r.map((function(e){return[t,e]}))}}function gh(t,e,n){var r=k(t,e-Sc,n).concat(e);return function(t){return r.map((function(e){return[e,t]}))}}function mh(){var t,e,n,r,i,a,o,s,c,u,l,h,f=10,d=f,p=90,y=360,g=2.5;function m(){return{type:"MultiLineString",coordinates:v()}}function v(){return k(jc(r/p)*p,n,p).map(l).concat(k(jc(s/y)*y,o,y).map(h)).concat(k(jc(e/f)*f,t,f).filter((function(t){return Ic(t%p)>Sc})).map(c)).concat(k(jc(a/d)*d,i,d).filter((function(t){return Ic(t%y)>Sc})).map(u))}return m.lines=function(){return v().map((function(t){return{type:"LineString",coordinates:t}}))},m.outline=function(){return{type:"Polygon",coordinates:[l(r).concat(h(o).slice(1),l(n).reverse().slice(1),h(s).reverse().slice(1))]}},m.extent=function(t){return arguments.length?m.extentMajor(t).extentMinor(t):m.extentMinor()},m.extentMajor=function(t){return arguments.length?(r=+t[0][0],n=+t[1][0],s=+t[0][1],o=+t[1][1],r>n&&(t=r,r=n,n=t),s>o&&(t=s,s=o,o=t),m.precision(g)):[[r,s],[n,o]]},m.extentMinor=function(n){return arguments.length?(e=+n[0][0],t=+n[1][0],a=+n[0][1],i=+n[1][1],e>t&&(n=e,e=t,t=n),a>i&&(n=a,a=i,i=n),m.precision(g)):[[e,a],[t,i]]},m.step=function(t){return arguments.length?m.stepMajor(t).stepMinor(t):m.stepMinor()},m.stepMajor=function(t){return arguments.length?(p=+t[0],y=+t[1],m):[p,y]},m.stepMinor=function(t){return arguments.length?(f=+t[0],d=+t[1],m):[f,d]},m.precision=function(f){return arguments.length?(g=+f,c=yh(a,i,90),u=gh(e,t,g),l=yh(s,o,90),h=gh(r,n,g),m):g},m.extentMajor([[-180,-89.999999],[180,89.999999]]).extentMinor([[-180,-80.000001],[180,80.000001]])}function vh(){return mh()()}function bh(t,e){var n=t[0]*Lc,r=t[1]*Lc,i=e[0]*Lc,a=e[1]*Lc,o=Pc(r),s=qc(r),c=Pc(a),u=qc(a),l=o*Pc(n),h=o*qc(n),f=c*Pc(i),d=c*qc(i),p=2*Gc($c(Xc(a-r)+o*c*Xc(i-n))),y=qc(p),g=p?function(t){var e=qc(t*=p)/y,n=qc(p-t)/y,r=n*l+e*f,i=n*h+e*d,a=n*s+e*u;return[Fc(i,r)*Bc,Fc(a,$c(r*r+i*i))*Bc]}:function(){return[n*Bc,r*Bc]};return g.distance=p,g}function _h(t){return t}var xh,wh,kh,Th,Eh=kc(),Ch=kc(),Sh={point:Zc,lineStart:Zc,lineEnd:Zc,polygonStart:function(){Sh.lineStart=Ah,Sh.lineEnd=Dh},polygonEnd:function(){Sh.lineStart=Sh.lineEnd=Sh.point=Zc,Eh.add(Ic(Ch)),Ch.reset()},result:function(){var t=Eh/2;return Eh.reset(),t}};function Ah(){Sh.point=Mh}function Mh(t,e){Sh.point=Nh,xh=kh=t,wh=Th=e}function Nh(t,e){Ch.add(Th*t-kh*e),kh=t,Th=e}function Dh(){Nh(xh,wh)}const Oh=Sh;var Bh=1/0,Lh=Bh,Ih=-Bh,Rh=Ih,Fh={point:function(t,e){t<Bh&&(Bh=t),t>Ih&&(Ih=t),e<Lh&&(Lh=e),e>Rh&&(Rh=e)},lineStart:Zc,lineEnd:Zc,polygonStart:Zc,polygonEnd:Zc,result:function(){var t=[[Bh,Lh],[Ih,Rh]];return Ih=Rh=-(Lh=Bh=1/0),t}};const Ph=Fh;var jh,Yh,zh,Uh,qh=0,Hh=0,$h=0,Wh=0,Vh=0,Gh=0,Xh=0,Zh=0,Qh=0,Kh={point:Jh,lineStart:tf,lineEnd:rf,polygonStart:function(){Kh.lineStart=af,Kh.lineEnd=of},polygonEnd:function(){Kh.point=Jh,Kh.lineStart=tf,Kh.lineEnd=rf},result:function(){var t=Qh?[Xh/Qh,Zh/Qh]:Gh?[Wh/Gh,Vh/Gh]:$h?[qh/$h,Hh/$h]:[NaN,NaN];return qh=Hh=$h=Wh=Vh=Gh=Xh=Zh=Qh=0,t}};function Jh(t,e){qh+=t,Hh+=e,++$h}function tf(){Kh.point=ef}function ef(t,e){Kh.point=nf,Jh(zh=t,Uh=e)}function nf(t,e){var n=t-zh,r=e-Uh,i=$c(n*n+r*r);Wh+=i*(zh+t)/2,Vh+=i*(Uh+e)/2,Gh+=i,Jh(zh=t,Uh=e)}function rf(){Kh.point=Jh}function af(){Kh.point=sf}function of(){cf(jh,Yh)}function sf(t,e){Kh.point=cf,Jh(jh=zh=t,Yh=Uh=e)}function cf(t,e){var n=t-zh,r=e-Uh,i=$c(n*n+r*r);Wh+=i*(zh+t)/2,Vh+=i*(Uh+e)/2,Gh+=i,Xh+=(i=Uh*t-zh*e)*(zh+t),Zh+=i*(Uh+e),Qh+=3*i,Jh(zh=t,Uh=e)}const uf=Kh;function lf(t){this._context=t}lf.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._context.moveTo(t,e),this._point=1;break;case 1:this._context.lineTo(t,e);break;default:this._context.moveTo(t+this._radius,e),this._context.arc(t,e,this._radius,0,Oc)}},result:Zc};var hf,ff,df,pf,yf,gf=kc(),mf={point:Zc,lineStart:function(){mf.point=vf},lineEnd:function(){hf&&bf(ff,df),mf.point=Zc},polygonStart:function(){hf=!0},polygonEnd:function(){hf=null},result:function(){var t=+gf;return gf.reset(),t}};function vf(t,e){mf.point=bf,ff=pf=t,df=yf=e}function bf(t,e){pf-=t,yf-=e,gf.add($c(pf*pf+yf*yf)),pf=t,yf=e}const _f=mf;function xf(){this._string=[]}function wf(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function kf(t,e){var n,r,i=4.5;function a(t){return t&&("function"==typeof i&&r.pointRadius(+i.apply(this,arguments)),nu(t,n(r))),r.result()}return a.area=function(t){return nu(t,n(Oh)),Oh.result()},a.measure=function(t){return nu(t,n(_f)),_f.result()},a.bounds=function(t){return nu(t,n(Ph)),Ph.result()},a.centroid=function(t){return nu(t,n(uf)),uf.result()},a.projection=function(e){return arguments.length?(n=null==e?(t=null,_h):(t=e).stream,a):t},a.context=function(t){return arguments.length?(r=null==t?(e=null,new xf):new lf(e=t),"function"!=typeof i&&r.pointRadius(i),a):e},a.pointRadius=function(t){return arguments.length?(i="function"==typeof t?t:(r.pointRadius(+t),+t),a):i},a.projection(t).context(e)}function Tf(t){return{stream:Ef(t)}}function Ef(t){return function(e){var n=new Cf;for(var r in t)n[r]=t[r];return n.stream=e,n}}function Cf(){}function Sf(t,e,n){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),nu(n,t.stream(Ph)),e(Ph.result()),null!=r&&t.clipExtent(r),t}function Af(t,e,n){return Sf(t,(function(n){var r=e[1][0]-e[0][0],i=e[1][1]-e[0][1],a=Math.min(r/(n[1][0]-n[0][0]),i/(n[1][1]-n[0][1])),o=+e[0][0]+(r-a*(n[1][0]+n[0][0]))/2,s=+e[0][1]+(i-a*(n[1][1]+n[0][1]))/2;t.scale(150*a).translate([o,s])}),n)}function Mf(t,e,n){return Af(t,[[0,0],e],n)}function Nf(t,e,n){return Sf(t,(function(n){var r=+e,i=r/(n[1][0]-n[0][0]),a=(r-i*(n[1][0]+n[0][0]))/2,o=-i*n[0][1];t.scale(150*i).translate([a,o])}),n)}function Df(t,e,n){return Sf(t,(function(n){var r=+e,i=r/(n[1][1]-n[0][1]),a=-i*n[0][0],o=(r-i*(n[1][1]+n[0][1]))/2;t.scale(150*i).translate([a,o])}),n)}xf.prototype={_radius:4.5,_circle:wf(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._string.push("M",t,",",e),this._point=1;break;case 1:this._string.push("L",t,",",e);break;default:null==this._circle&&(this._circle=wf(this._radius)),this._string.push("M",t,",",e,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},Cf.prototype={constructor:Cf,point:function(t,e){this.stream.point(t,e)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var Of=Pc(30*Lc);function Bf(t,e){return+e?function(t,e){function n(r,i,a,o,s,c,u,l,h,f,d,p,y,g){var m=u-r,v=l-i,b=m*m+v*v;if(b>4*e&&y--){var _=o+f,x=s+d,w=c+p,k=$c(_*_+x*x+w*w),T=Gc(w/=k),E=Ic(Ic(w)-1)<Sc||Ic(a-h)<Sc?(a+h)/2:Fc(x,_),C=t(E,T),S=C[0],A=C[1],M=S-r,N=A-i,D=v*M-m*N;(D*D/b>e||Ic((m*M+v*N)/b-.5)>.3||o*f+s*d+c*p<Of)&&(n(r,i,a,o,s,c,S,A,E,_/=k,x/=k,w,y,g),g.point(S,A),n(S,A,E,_,x,w,u,l,h,f,d,p,y,g))}}return function(e){var r,i,a,o,s,c,u,l,h,f,d,p,y={point:g,lineStart:m,lineEnd:b,polygonStart:function(){e.polygonStart(),y.lineStart=_},polygonEnd:function(){e.polygonEnd(),y.lineStart=m}};function g(n,r){n=t(n,r),e.point(n[0],n[1])}function m(){l=NaN,y.point=v,e.lineStart()}function v(r,i){var a=mu([r,i]),o=t(r,i);n(l,h,u,f,d,p,l=o[0],h=o[1],u=r,f=a[0],d=a[1],p=a[2],16,e),e.point(l,h)}function b(){y.point=g,e.lineEnd()}function _(){m(),y.point=x,y.lineEnd=w}function x(t,e){v(r=t,e),i=l,a=h,o=f,s=d,c=p,y.point=v}function w(){n(l,h,u,f,d,p,i,a,r,o,s,c,16,e),y.lineEnd=b,b()}return y}}(t,e):function(t){return Ef({point:function(e,n){e=t(e,n),this.stream.point(e[0],e[1])}})}(t)}var Lf=Ef({point:function(t,e){this.stream.point(t*Lc,e*Lc)}});function If(t,e,n,r,i){function a(a,o){return[e+t*(a*=r),n-t*(o*=i)]}return a.invert=function(a,o){return[(a-e)/t*r,(n-o)/t*i]},a}function Rf(t,e,n,r,i,a){var o=Pc(a),s=qc(a),c=o*t,u=s*t,l=o/t,h=s/t,f=(s*n-o*e)/t,d=(s*e+o*n)/t;function p(t,a){return[c*(t*=r)-u*(a*=i)+e,n-u*t-c*a]}return p.invert=function(t,e){return[r*(l*t-h*e+f),i*(d-h*t-l*e)]},p}function Ff(t){return Pf((function(){return t}))()}function Pf(t){var e,n,r,i,a,o,s,c,u,l,h=150,f=480,d=250,p=0,y=0,g=0,m=0,v=0,b=0,_=1,x=1,w=null,k=Ul,T=null,E=_h,C=.5;function S(t){return c(t[0]*Lc,t[1]*Lc)}function A(t){return(t=c.invert(t[0],t[1]))&&[t[0]*Bc,t[1]*Bc]}function M(){var t=Rf(h,0,0,_,x,b).apply(null,e(p,y)),r=(b?Rf:If)(h,f-t[0],d-t[1],_,x,b);return n=kl(g,m,v),s=xl(e,r),c=xl(n,s),o=Bf(s,C),N()}function N(){return u=l=null,S}return S.stream=function(t){return u&&l===t?u:u=Lf(function(t){return Ef({point:function(e,n){var r=t(e,n);return this.stream.point(r[0],r[1])}})}(n)(k(o(E(l=t)))))},S.preclip=function(t){return arguments.length?(k=t,w=void 0,N()):k},S.postclip=function(t){return arguments.length?(E=t,T=r=i=a=null,N()):E},S.clipAngle=function(t){return arguments.length?(k=+t?ql(w=t*Lc):(w=null,Ul),N()):w*Bc},S.clipExtent=function(t){return arguments.length?(E=null==t?(T=r=i=a=null,_h):Wl(T=+t[0][0],r=+t[0][1],i=+t[1][0],a=+t[1][1]),N()):null==T?null:[[T,r],[i,a]]},S.scale=function(t){return arguments.length?(h=+t,M()):h},S.translate=function(t){return arguments.length?(f=+t[0],d=+t[1],M()):[f,d]},S.center=function(t){return arguments.length?(p=t[0]%360*Lc,y=t[1]%360*Lc,M()):[p*Bc,y*Bc]},S.rotate=function(t){return arguments.length?(g=t[0]%360*Lc,m=t[1]%360*Lc,v=t.length>2?t[2]%360*Lc:0,M()):[g*Bc,m*Bc,v*Bc]},S.angle=function(t){return arguments.length?(b=t%360*Lc,M()):b*Bc},S.reflectX=function(t){return arguments.length?(_=t?-1:1,M()):_<0},S.reflectY=function(t){return arguments.length?(x=t?-1:1,M()):x<0},S.precision=function(t){return arguments.length?(o=Bf(s,C=t*t),N()):$c(C)},S.fitExtent=function(t,e){return Af(S,t,e)},S.fitSize=function(t,e){return Mf(S,t,e)},S.fitWidth=function(t,e){return Nf(S,t,e)},S.fitHeight=function(t,e){return Df(S,t,e)},function(){return e=t.apply(this,arguments),S.invert=e.invert&&A,M()}}function jf(t){var e=0,n=Mc/3,r=Pf(t),i=r(e,n);return i.parallels=function(t){return arguments.length?r(e=t[0]*Lc,n=t[1]*Lc):[e*Bc,n*Bc]},i}function Yf(t,e){var n=qc(t),r=(n+qc(e))/2;if(Ic(r)<Sc)return function(t){var e=Pc(t);function n(t,n){return[t*e,qc(n)/e]}return n.invert=function(t,n){return[t/e,Gc(n*e)]},n}(t);var i=1+n*(2*r-n),a=$c(i)/r;function o(t,e){var n=$c(i-2*r*qc(e))/r;return[n*qc(t*=r),a-n*Pc(t)]}return o.invert=function(t,e){var n=a-e,o=Fc(t,Ic(n))*Hc(n);return n*r<0&&(o-=Mc*Hc(t)*Hc(n)),[o/r,Gc((i-(t*t+n*n)*r*r)/(2*r))]},o}function zf(){return jf(Yf).scale(155.424).center([0,33.6442])}function Uf(){return zf().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])}function qf(){var t,e,n,r,i,a,o=Uf(),s=zf().rotate([154,0]).center([-2,58.5]).parallels([55,65]),c=zf().rotate([157,0]).center([-3,19.9]).parallels([8,18]),u={point:function(t,e){a=[t,e]}};function l(t){var e=t[0],o=t[1];return a=null,n.point(e,o),a||(r.point(e,o),a)||(i.point(e,o),a)}function h(){return t=e=null,l}return l.invert=function(t){var e=o.scale(),n=o.translate(),r=(t[0]-n[0])/e,i=(t[1]-n[1])/e;return(i>=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:o).invert(t)},l.stream=function(n){return t&&e===n?t:(r=[o.stream(e=n),s.stream(n),c.stream(n)],i=r.length,t={point:function(t,e){for(var n=-1;++n<i;)r[n].point(t,e)},sphere:function(){for(var t=-1;++t<i;)r[t].sphere()},lineStart:function(){for(var t=-1;++t<i;)r[t].lineStart()},lineEnd:function(){for(var t=-1;++t<i;)r[t].lineEnd()},polygonStart:function(){for(var t=-1;++t<i;)r[t].polygonStart()},polygonEnd:function(){for(var t=-1;++t<i;)r[t].polygonEnd()}});var r,i},l.precision=function(t){return arguments.length?(o.precision(t),s.precision(t),c.precision(t),h()):o.precision()},l.scale=function(t){return arguments.length?(o.scale(t),s.scale(.35*t),c.scale(t),l.translate(o.translate())):o.scale()},l.translate=function(t){if(!arguments.length)return o.translate();var e=o.scale(),a=+t[0],l=+t[1];return n=o.translate(t).clipExtent([[a-.455*e,l-.238*e],[a+.455*e,l+.238*e]]).stream(u),r=s.translate([a-.307*e,l+.201*e]).clipExtent([[a-.425*e+Sc,l+.12*e+Sc],[a-.214*e-Sc,l+.234*e-Sc]]).stream(u),i=c.translate([a-.205*e,l+.212*e]).clipExtent([[a-.214*e+Sc,l+.166*e+Sc],[a-.115*e-Sc,l+.234*e-Sc]]).stream(u),h()},l.fitExtent=function(t,e){return Af(l,t,e)},l.fitSize=function(t,e){return Mf(l,t,e)},l.fitWidth=function(t,e){return Nf(l,t,e)},l.fitHeight=function(t,e){return Df(l,t,e)},l.scale(1070)}function Hf(t){return function(e,n){var r=Pc(e),i=Pc(n),a=t(r*i);return[a*i*qc(e),a*qc(n)]}}function $f(t){return function(e,n){var r=$c(e*e+n*n),i=t(r),a=qc(i),o=Pc(i);return[Fc(e*a,r*o),Gc(r&&n*a/r)]}}var Wf=Hf((function(t){return $c(2/(1+t))}));function Vf(){return Ff(Wf).scale(124.75).clipAngle(179.999)}Wf.invert=$f((function(t){return 2*Gc(t/2)}));var Gf=Hf((function(t){return(t=Vc(t))&&t/qc(t)}));function Xf(){return Ff(Gf).scale(79.4188).clipAngle(179.999)}function Zf(t,e){return[t,zc(Wc((Nc+e)/2))]}function Qf(){return Kf(Zf).scale(961/Oc)}function Kf(t){var e,n,r,i=Ff(t),a=i.center,o=i.scale,s=i.translate,c=i.clipExtent,u=null;function l(){var a=Mc*o(),s=i(Sl(i.rotate()).invert([0,0]));return c(null==u?[[s[0]-a,s[1]-a],[s[0]+a,s[1]+a]]:t===Zf?[[Math.max(s[0]-a,u),e],[Math.min(s[0]+a,n),r]]:[[u,Math.max(s[1]-a,e)],[n,Math.min(s[1]+a,r)]])}return i.scale=function(t){return arguments.length?(o(t),l()):o()},i.translate=function(t){return arguments.length?(s(t),l()):s()},i.center=function(t){return arguments.length?(a(t),l()):a()},i.clipExtent=function(t){return arguments.length?(null==t?u=e=n=r=null:(u=+t[0][0],e=+t[0][1],n=+t[1][0],r=+t[1][1]),l()):null==u?null:[[u,e],[n,r]]},l()}function Jf(t){return Wc((Nc+t)/2)}function td(t,e){var n=Pc(t),r=t===e?qc(t):zc(n/Pc(e))/zc(Jf(e)/Jf(t)),i=n*Uc(Jf(t),r)/r;if(!r)return Zf;function a(t,e){i>0?e<-Nc+Sc&&(e=-Nc+Sc):e>Nc-Sc&&(e=Nc-Sc);var n=i/Uc(Jf(e),r);return[n*qc(r*t),i-n*Pc(r*t)]}return a.invert=function(t,e){var n=i-e,a=Hc(r)*$c(t*t+n*n),o=Fc(t,Ic(n))*Hc(n);return n*r<0&&(o-=Mc*Hc(t)*Hc(n)),[o/r,2*Rc(Uc(i/a,1/r))-Nc]},a}function ed(){return jf(td).scale(109.5).parallels([30,30])}function nd(t,e){return[t,e]}function rd(){return Ff(nd).scale(152.63)}function id(t,e){var n=Pc(t),r=t===e?qc(t):(n-Pc(e))/(e-t),i=n/r+t;if(Ic(r)<Sc)return nd;function a(t,e){var n=i-e,a=r*t;return[n*qc(a),i-n*Pc(a)]}return a.invert=function(t,e){var n=i-e,a=Fc(t,Ic(n))*Hc(n);return n*r<0&&(a-=Mc*Hc(t)*Hc(n)),[a/r,i-Hc(r)*$c(t*t+n*n)]},a}function ad(){return jf(id).scale(131.154).center([0,13.9389])}Gf.invert=$f((function(t){return t})),Zf.invert=function(t,e){return[t,2*Rc(Yc(e))-Nc]},nd.invert=nd;var od=1.340264,sd=-.081106,cd=893e-6,ud=.003796,ld=$c(3)/2;function hd(t,e){var n=Gc(ld*qc(e)),r=n*n,i=r*r*r;return[t*Pc(n)/(ld*(od+3*sd*r+i*(7*cd+9*ud*r))),n*(od+sd*r+i*(cd+ud*r))]}function fd(){return Ff(hd).scale(177.158)}function dd(t,e){var n=Pc(e),r=Pc(t)*n;return[n*qc(t)/r,qc(e)/r]}function pd(){return Ff(dd).scale(144.049).clipAngle(60)}function yd(){var t,e,n,r,i,a,o,s=1,c=0,u=0,l=1,h=1,f=0,d=null,p=1,y=1,g=Ef({point:function(t,e){var n=b([t,e]);this.stream.point(n[0],n[1])}}),m=_h;function v(){return p=s*l,y=s*h,a=o=null,b}function b(n){var r=n[0]*p,i=n[1]*y;if(f){var a=i*t-r*e;r=r*t+i*e,i=a}return[r+c,i+u]}return b.invert=function(n){var r=n[0]-c,i=n[1]-u;if(f){var a=i*t+r*e;r=r*t-i*e,i=a}return[r/p,i/y]},b.stream=function(t){return a&&o===t?a:a=g(m(o=t))},b.postclip=function(t){return arguments.length?(m=t,d=n=r=i=null,v()):m},b.clipExtent=function(t){return arguments.length?(m=null==t?(d=n=r=i=null,_h):Wl(d=+t[0][0],n=+t[0][1],r=+t[1][0],i=+t[1][1]),v()):null==d?null:[[d,n],[r,i]]},b.scale=function(t){return arguments.length?(s=+t,v()):s},b.translate=function(t){return arguments.length?(c=+t[0],u=+t[1],v()):[c,u]},b.angle=function(n){return arguments.length?(e=qc(f=n%360*Lc),t=Pc(f),v()):f*Bc},b.reflectX=function(t){return arguments.length?(l=t?-1:1,v()):l<0},b.reflectY=function(t){return arguments.length?(h=t?-1:1,v()):h<0},b.fitExtent=function(t,e){return Af(b,t,e)},b.fitSize=function(t,e){return Mf(b,t,e)},b.fitWidth=function(t,e){return Nf(b,t,e)},b.fitHeight=function(t,e){return Df(b,t,e)},b}function gd(t,e){var n=e*e,r=n*n;return[t*(.8707-.131979*n+r*(r*(.003971*n-.001529*r)-.013791)),e*(1.007226+n*(.015085+r*(.028874*n-.044475-.005916*r)))]}function md(){return Ff(gd).scale(175.295)}function vd(t,e){return[Pc(e)*qc(t),qc(e)]}function bd(){return Ff(vd).scale(249.5).clipAngle(90.000001)}function _d(t,e){var n=Pc(e),r=1+Pc(t)*n;return[n*qc(t)/r,qc(e)/r]}function xd(){return Ff(_d).scale(250).clipAngle(142)}function wd(t,e){return[zc(Wc((Nc+e)/2)),-t]}function kd(){var t=Kf(wd),e=t.center,n=t.rotate;return t.center=function(t){return arguments.length?e([-t[1],t[0]]):[(t=e())[1],-t[0]]},t.rotate=function(t){return arguments.length?n([t[0],t[1],t.length>2?t[2]+90:90]):[(t=n())[0],t[1],t[2]-90]},n([0,0,90]).scale(159.155)}function Td(t,e){return t.parent===e.parent?1:2}function Ed(t,e){return t+e.x}function Cd(t,e){return Math.max(t,e.y)}function Sd(){var t=Td,e=1,n=1,r=!1;function i(i){var a,o=0;i.eachAfter((function(e){var n=e.children;n?(e.x=function(t){return t.reduce(Ed,0)/t.length}(n),e.y=function(t){return 1+t.reduce(Cd,0)}(n)):(e.x=a?o+=t(e,a):0,e.y=0,a=e)}));var s=function(t){for(var e;e=t.children;)t=e[0];return t}(i),c=function(t){for(var e;e=t.children;)t=e[e.length-1];return t}(i),u=s.x-t(s,c)/2,l=c.x+t(c,s)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*e,t.y=(i.y-t.y)*n}:function(t){t.x=(t.x-u)/(l-u)*e,t.y=(1-(i.y?t.y/i.y:1))*n})}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i}function Ad(t){var e=0,n=t.children,r=n&&n.length;if(r)for(;--r>=0;)e+=n[r].value;else e=1;t.value=e}function Md(t,e){var n,r,i,a,o,s=new Bd(t),c=+t.value&&(s.value=t.value),u=[s];for(null==e&&(e=Nd);n=u.pop();)if(c&&(n.value=+n.data.value),(i=e(n.data))&&(o=i.length))for(n.children=new Array(o),a=o-1;a>=0;--a)u.push(r=n.children[a]=new Bd(i[a])),r.parent=n,r.depth=n.depth+1;return s.eachBefore(Od)}function Nd(t){return t.children}function Dd(t){t.data=t.data.data}function Od(t){var e=0;do{t.height=e}while((t=t.parent)&&t.height<++e)}function Bd(t){this.data=t,this.depth=this.height=0,this.parent=null}hd.invert=function(t,e){for(var n,r=e,i=r*r,a=i*i*i,o=0;o<12&&(a=(i=(r-=n=(r*(od+sd*i+a*(cd+ud*i))-e)/(od+3*sd*i+a*(7*cd+9*ud*i)))*r)*i*i,!(Ic(n)<Ac));++o);return[ld*t*(od+3*sd*i+a*(7*cd+9*ud*i))/Pc(r),Gc(qc(r)/ld)]},dd.invert=$f(Rc),gd.invert=function(t,e){var n,r=e,i=25;do{var a=r*r,o=a*a;r-=n=(r*(1.007226+a*(.015085+o*(.028874*a-.044475-.005916*o)))-e)/(1.007226+a*(.045255+o*(.259866*a-.311325-.005916*11*o)))}while(Ic(n)>Sc&&--i>0);return[t/(.8707+(a=r*r)*(a*(a*a*a*(.003971-.001529*a)-.013791)-.131979)),r]},vd.invert=$f(Gc),_d.invert=$f((function(t){return 2*Rc(t)})),wd.invert=function(t,e){return[-e,2*Rc(Yc(t))-Nc]},Bd.prototype=Md.prototype={constructor:Bd,count:function(){return this.eachAfter(Ad)},each:function(t){var e,n,r,i,a=this,o=[a];do{for(e=o.reverse(),o=[];a=e.pop();)if(t(a),n=a.children)for(r=0,i=n.length;r<i;++r)o.push(n[r])}while(o.length);return this},eachAfter:function(t){for(var e,n,r,i=this,a=[i],o=[];i=a.pop();)if(o.push(i),e=i.children)for(n=0,r=e.length;n<r;++n)a.push(e[n]);for(;i=o.pop();)t(i);return this},eachBefore:function(t){for(var e,n,r=this,i=[r];r=i.pop();)if(t(r),e=r.children)for(n=e.length-1;n>=0;--n)i.push(e[n]);return this},sum:function(t){return this.eachAfter((function(e){for(var n=+t(e.data)||0,r=e.children,i=r&&r.length;--i>=0;)n+=r[i].value;e.value=n}))},sort:function(t){return this.eachBefore((function(e){e.children&&e.children.sort(t)}))},path:function(t){for(var e=this,n=function(t,e){if(t===e)return t;var n=t.ancestors(),r=e.ancestors(),i=null;for(t=n.pop(),e=r.pop();t===e;)i=t,t=n.pop(),e=r.pop();return i}(e,t),r=[e];e!==n;)e=e.parent,r.push(e);for(var i=r.length;t!==n;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e},descendants:function(){var t=[];return this.each((function(e){t.push(e)})),t},leaves:function(){var t=[];return this.eachBefore((function(e){e.children||t.push(e)})),t},links:function(){var t=this,e=[];return t.each((function(n){n!==t&&e.push({source:n.parent,target:n})})),e},copy:function(){return Md(this).eachBefore(Dd)}};var Ld=Array.prototype.slice;function Id(t){for(var e,n,r=0,i=(t=function(t){for(var e,n,r=t.length;r;)n=Math.random()*r--|0,e=t[r],t[r]=t[n],t[n]=e;return t}(Ld.call(t))).length,a=[];r<i;)e=t[r],n&&Pd(n,e)?++r:(n=Yd(a=Rd(a,e)),r=0);return n}function Rd(t,e){var n,r;if(jd(e,t))return[e];for(n=0;n<t.length;++n)if(Fd(e,t[n])&&jd(zd(t[n],e),t))return[t[n],e];for(n=0;n<t.length-1;++n)for(r=n+1;r<t.length;++r)if(Fd(zd(t[n],t[r]),e)&&Fd(zd(t[n],e),t[r])&&Fd(zd(t[r],e),t[n])&&jd(Ud(t[n],t[r],e),t))return[t[n],t[r],e];throw new Error}function Fd(t,e){var n=t.r-e.r,r=e.x-t.x,i=e.y-t.y;return n<0||n*n<r*r+i*i}function Pd(t,e){var n=t.r-e.r+1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function jd(t,e){for(var n=0;n<e.length;++n)if(!Pd(t,e[n]))return!1;return!0}function Yd(t){switch(t.length){case 1:return function(t){return{x:t.x,y:t.y,r:t.r}}(t[0]);case 2:return zd(t[0],t[1]);case 3:return Ud(t[0],t[1],t[2])}}function zd(t,e){var n=t.x,r=t.y,i=t.r,a=e.x,o=e.y,s=e.r,c=a-n,u=o-r,l=s-i,h=Math.sqrt(c*c+u*u);return{x:(n+a+c/h*l)/2,y:(r+o+u/h*l)/2,r:(h+i+s)/2}}function Ud(t,e,n){var r=t.x,i=t.y,a=t.r,o=e.x,s=e.y,c=e.r,u=n.x,l=n.y,h=n.r,f=r-o,d=r-u,p=i-s,y=i-l,g=c-a,m=h-a,v=r*r+i*i-a*a,b=v-o*o-s*s+c*c,_=v-u*u-l*l+h*h,x=d*p-f*y,w=(p*_-y*b)/(2*x)-r,k=(y*g-p*m)/x,T=(d*b-f*_)/(2*x)-i,E=(f*m-d*g)/x,C=k*k+E*E-1,S=2*(a+w*k+T*E),A=w*w+T*T-a*a,M=-(C?(S+Math.sqrt(S*S-4*C*A))/(2*C):A/S);return{x:r+w+k*M,y:i+T+E*M,r:M}}function qd(t,e,n){var r,i,a,o,s=t.x-e.x,c=t.y-e.y,u=s*s+c*c;u?(i=e.r+n.r,i*=i,o=t.r+n.r,i>(o*=o)?(r=(u+o-i)/(2*u),a=Math.sqrt(Math.max(0,o/u-r*r)),n.x=t.x-r*s-a*c,n.y=t.y-r*c+a*s):(r=(u+i-o)/(2*u),a=Math.sqrt(Math.max(0,i/u-r*r)),n.x=e.x+r*s-a*c,n.y=e.y+r*c+a*s)):(n.x=e.x+n.r,n.y=e.y)}function Hd(t,e){var n=t.r+e.r-1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function $d(t){var e=t._,n=t.next._,r=e.r+n.r,i=(e.x*n.r+n.x*e.r)/r,a=(e.y*n.r+n.y*e.r)/r;return i*i+a*a}function Wd(t){this._=t,this.next=null,this.previous=null}function Vd(t){if(!(i=t.length))return 0;var e,n,r,i,a,o,s,c,u,l,h;if((e=t[0]).x=0,e.y=0,!(i>1))return e.r;if(n=t[1],e.x=-n.r,n.x=e.r,n.y=0,!(i>2))return e.r+n.r;qd(n,e,r=t[2]),e=new Wd(e),n=new Wd(n),r=new Wd(r),e.next=r.previous=n,n.next=e.previous=r,r.next=n.previous=e;t:for(s=3;s<i;++s){qd(e._,n._,r=t[s]),r=new Wd(r),c=n.next,u=e.previous,l=n._.r,h=e._.r;do{if(l<=h){if(Hd(c._,r._)){n=c,e.next=n,n.previous=e,--s;continue t}l+=c._.r,c=c.next}else{if(Hd(u._,r._)){(e=u).next=n,n.previous=e,--s;continue t}h+=u._.r,u=u.previous}}while(c!==u.next);for(r.previous=e,r.next=n,e.next=n.previous=n=r,a=$d(e);(r=r.next)!==n;)(o=$d(r))<a&&(e=r,a=o);n=e.next}for(e=[n._],r=n;(r=r.next)!==n;)e.push(r._);for(r=Id(e),s=0;s<i;++s)(e=t[s]).x-=r.x,e.y-=r.y;return r.r}function Gd(t){return Vd(t),t}function Xd(t){return null==t?null:Zd(t)}function Zd(t){if("function"!=typeof t)throw new Error;return t}function Qd(){return 0}function Kd(t){return function(){return t}}function Jd(t){return Math.sqrt(t.value)}function tp(){var t=null,e=1,n=1,r=Qd;function i(i){return i.x=e/2,i.y=n/2,t?i.eachBefore(ep(t)).eachAfter(np(r,.5)).eachBefore(rp(1)):i.eachBefore(ep(Jd)).eachAfter(np(Qd,1)).eachAfter(np(r,i.r/Math.min(e,n))).eachBefore(rp(Math.min(e,n)/(2*i.r))),i}return i.radius=function(e){return arguments.length?(t=Xd(e),i):t},i.size=function(t){return arguments.length?(e=+t[0],n=+t[1],i):[e,n]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:Kd(+t),i):r},i}function ep(t){return function(e){e.children||(e.r=Math.max(0,+t(e)||0))}}function np(t,e){return function(n){if(r=n.children){var r,i,a,o=r.length,s=t(n)*e||0;if(s)for(i=0;i<o;++i)r[i].r+=s;if(a=Vd(r),s)for(i=0;i<o;++i)r[i].r-=s;n.r=a+s}}}function rp(t){return function(e){var n=e.parent;e.r*=t,n&&(e.x=n.x+t*e.x,e.y=n.y+t*e.y)}}function ip(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function ap(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(r-e)/t.value;++s<c;)(a=o[s]).y0=n,a.y1=i,a.x0=e,a.x1=e+=a.value*u}function op(){var t=1,e=1,n=0,r=!1;function i(i){var a=i.height+1;return i.x0=i.y0=n,i.x1=t,i.y1=e/a,i.eachBefore(function(t,e){return function(r){r.children&&ap(r,r.x0,t*(r.depth+1)/e,r.x1,t*(r.depth+2)/e);var i=r.x0,a=r.y0,o=r.x1-n,s=r.y1-n;o<i&&(i=o=(i+o)/2),s<a&&(a=s=(a+s)/2),r.x0=i,r.y0=a,r.x1=o,r.y1=s}}(e,a)),r&&i.eachBefore(ip),i}return i.round=function(t){return arguments.length?(r=!!t,i):r},i.size=function(n){return arguments.length?(t=+n[0],e=+n[1],i):[t,e]},i.padding=function(t){return arguments.length?(n=+t,i):n},i}var sp={depth:-1},cp={};function up(t){return t.id}function lp(t){return t.parentId}function hp(){var t=up,e=lp;function n(n){var r,i,a,o,s,c,u,l=n.length,h=new Array(l),f={};for(i=0;i<l;++i)r=n[i],s=h[i]=new Bd(r),null!=(c=t(r,i,n))&&(c+="")&&(f[u="$"+(s.id=c)]=u in f?cp:s);for(i=0;i<l;++i)if(s=h[i],null!=(c=e(n[i],i,n))&&(c+="")){if(!(o=f["$"+c]))throw new Error("missing: "+c);if(o===cp)throw new Error("ambiguous: "+c);o.children?o.children.push(s):o.children=[s],s.parent=o}else{if(a)throw new Error("multiple roots");a=s}if(!a)throw new Error("no root");if(a.parent=sp,a.eachBefore((function(t){t.depth=t.parent.depth+1,--l})).eachBefore(Od),a.parent=null,l>0)throw new Error("cycle");return a}return n.id=function(e){return arguments.length?(t=Zd(e),n):t},n.parentId=function(t){return arguments.length?(e=Zd(t),n):e},n}function fp(t,e){return t.parent===e.parent?1:2}function dp(t){var e=t.children;return e?e[0]:t.t}function pp(t){var e=t.children;return e?e[e.length-1]:t.t}function yp(t,e,n){var r=n/(e.i-t.i);e.c-=r,e.s+=n,t.c+=r,e.z+=n,e.m+=n}function gp(t,e,n){return t.a.parent===e.parent?t.a:n}function mp(t,e){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=e}function vp(){var t=fp,e=1,n=1,r=null;function i(i){var c=function(t){for(var e,n,r,i,a,o=new mp(t,0),s=[o];e=s.pop();)if(r=e._.children)for(e.children=new Array(a=r.length),i=a-1;i>=0;--i)s.push(n=e.children[i]=new mp(r[i],i)),n.parent=e;return(o.parent=new mp(null,0)).children=[o],o}(i);if(c.eachAfter(a),c.parent.m=-c.z,c.eachBefore(o),r)i.eachBefore(s);else{var u=i,l=i,h=i;i.eachBefore((function(t){t.x<u.x&&(u=t),t.x>l.x&&(l=t),t.depth>h.depth&&(h=t)}));var f=u===l?1:t(u,l)/2,d=f-u.x,p=e/(l.x+f+d),y=n/(h.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*y}))}return i}function a(e){var n=e.children,r=e.parent.children,i=e.i?r[e.i-1]:null;if(n){!function(t){for(var e,n=0,r=0,i=t.children,a=i.length;--a>=0;)(e=i[a]).z+=n,e.m+=n,n+=e.s+(r+=e.c)}(e);var a=(n[0].z+n[n.length-1].z)/2;i?(e.z=i.z+t(e._,i._),e.m=e.z-a):e.z=a}else i&&(e.z=i.z+t(e._,i._));e.parent.A=function(e,n,r){if(n){for(var i,a=e,o=e,s=n,c=a.parent.children[0],u=a.m,l=o.m,h=s.m,f=c.m;s=pp(s),a=dp(a),s&&a;)c=dp(c),(o=pp(o)).a=e,(i=s.z+h-a.z-u+t(s._,a._))>0&&(yp(gp(s,e,r),e,i),u+=i,l+=i),h+=s.m,u+=a.m,f+=c.m,l+=o.m;s&&!pp(o)&&(o.t=s,o.m+=h-l),a&&!dp(c)&&(c.t=a,c.m+=u-f,r=e)}return r}(e,i,e.parent.A||r[0])}function o(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function s(t){t.x*=e,t.y=t.depth*n}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i}function bp(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(i-n)/t.value;++s<c;)(a=o[s]).x0=e,a.x1=r,a.y0=n,a.y1=n+=a.value*u}mp.prototype=Object.create(Bd.prototype);var _p=(1+Math.sqrt(5))/2;function xp(t,e,n,r,i,a){for(var o,s,c,u,l,h,f,d,p,y,g,m=[],v=e.children,b=0,_=0,x=v.length,w=e.value;b<x;){c=i-n,u=a-r;do{l=v[_++].value}while(!l&&_<x);for(h=f=l,g=l*l*(y=Math.max(u/c,c/u)/(w*t)),p=Math.max(f/g,g/h);_<x;++_){if(l+=s=v[_].value,s<h&&(h=s),s>f&&(f=s),g=l*l*y,(d=Math.max(f/g,g/h))>p){l-=s;break}p=d}m.push(o={value:l,dice:c<u,children:v.slice(b,_)}),o.dice?ap(o,n,r,i,w?r+=u*l/w:a):bp(o,n,r,w?n+=c*l/w:i,a),w-=l,b=_}return m}const wp=function t(e){function n(t,n,r,i,a){xp(e,t,n,r,i,a)}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(_p);function kp(){var t=wp,e=!1,n=1,r=1,i=[0],a=Qd,o=Qd,s=Qd,c=Qd,u=Qd;function l(t){return t.x0=t.y0=0,t.x1=n,t.y1=r,t.eachBefore(h),i=[0],e&&t.eachBefore(ip),t}function h(e){var n=i[e.depth],r=e.x0+n,l=e.y0+n,h=e.x1-n,f=e.y1-n;h<r&&(r=h=(r+h)/2),f<l&&(l=f=(l+f)/2),e.x0=r,e.y0=l,e.x1=h,e.y1=f,e.children&&(n=i[e.depth+1]=a(e)/2,r+=u(e)-n,l+=o(e)-n,(h-=s(e)-n)<r&&(r=h=(r+h)/2),(f-=c(e)-n)<l&&(l=f=(l+f)/2),t(e,r,l,h,f))}return l.round=function(t){return arguments.length?(e=!!t,l):e},l.size=function(t){return arguments.length?(n=+t[0],r=+t[1],l):[n,r]},l.tile=function(e){return arguments.length?(t=Zd(e),l):t},l.padding=function(t){return arguments.length?l.paddingInner(t).paddingOuter(t):l.paddingInner()},l.paddingInner=function(t){return arguments.length?(a="function"==typeof t?t:Kd(+t),l):a},l.paddingOuter=function(t){return arguments.length?l.paddingTop(t).paddingRight(t).paddingBottom(t).paddingLeft(t):l.paddingTop()},l.paddingTop=function(t){return arguments.length?(o="function"==typeof t?t:Kd(+t),l):o},l.paddingRight=function(t){return arguments.length?(s="function"==typeof t?t:Kd(+t),l):s},l.paddingBottom=function(t){return arguments.length?(c="function"==typeof t?t:Kd(+t),l):c},l.paddingLeft=function(t){return arguments.length?(u="function"==typeof t?t:Kd(+t),l):u},l}function Tp(t,e,n,r,i){var a,o,s=t.children,c=s.length,u=new Array(c+1);for(u[0]=o=a=0;a<c;++a)u[a+1]=o+=s[a].value;!function t(e,n,r,i,a,o,c){if(e>=n-1){var l=s[e];return l.x0=i,l.y0=a,l.x1=o,void(l.y1=c)}for(var h=u[e],f=r/2+h,d=e+1,p=n-1;d<p;){var y=d+p>>>1;u[y]<f?d=y+1:p=y}f-u[d-1]<u[d]-f&&e+1<d&&--d;var g=u[d]-h,m=r-g;if(o-i>c-a){var v=(i*m+o*g)/r;t(e,d,g,i,a,v,c),t(d,n,m,v,a,o,c)}else{var b=(a*m+c*g)/r;t(e,d,g,i,a,o,b),t(d,n,m,i,b,o,c)}}(0,c,t.value,e,n,r,i)}function Ep(t,e,n,r,i){(1&t.depth?bp:ap)(t,e,n,r,i)}const Cp=function t(e){function n(t,n,r,i,a){if((o=t._squarify)&&o.ratio===e)for(var o,s,c,u,l,h=-1,f=o.length,d=t.value;++h<f;){for(c=(s=o[h]).children,u=s.value=0,l=c.length;u<l;++u)s.value+=c[u].value;s.dice?ap(s,n,r,i,r+=(a-r)*s.value/d):bp(s,n,r,n+=(i-n)*s.value/d,a),d-=s.value}else t._squarify=o=xp(e,t,n,r,i,a),o.ratio=e}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(_p);function Sp(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}}function Ap(t,e){var n=dn(+t,+e);return function(t){var e=n(t);return e-360*Math.floor(e/360)}}function Mp(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}}var Np=Math.SQRT2;function Dp(t){return((t=Math.exp(t))+1/t)/2}function Op(t,e){var n,r,i=t[0],a=t[1],o=t[2],s=e[0],c=e[1],u=e[2],l=s-i,h=c-a,f=l*l+h*h;if(f<1e-12)r=Math.log(u/o)/Np,n=function(t){return[i+t*l,a+t*h,o*Math.exp(Np*t*r)]};else{var d=Math.sqrt(f),p=(u*u-o*o+4*f)/(2*o*2*d),y=(u*u-o*o-4*f)/(2*u*2*d),g=Math.log(Math.sqrt(p*p+1)-p),m=Math.log(Math.sqrt(y*y+1)-y);r=(m-g)/Np,n=function(t){var e,n=t*r,s=Dp(g),c=o/(2*d)*(s*(e=Np*n+g,((e=Math.exp(2*e))-1)/(e+1))-function(t){return((t=Math.exp(t))-1/t)/2}(g));return[i+c*l,a+c*h,o*s/Dp(Np*n+g)]}}return n.duration=1e3*r,n}function Bp(t){return function(e,n){var r=t((e=an(e)).h,(n=an(n)).h),i=pn(e.s,n.s),a=pn(e.l,n.l),o=pn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.s=i(t),e.l=a(t),e.opacity=o(t),e+""}}}const Lp=Bp(dn);var Ip=Bp(pn);function Rp(t,e){var n=pn((t=Ta(t)).l,(e=Ta(e)).l),r=pn(t.a,e.a),i=pn(t.b,e.b),a=pn(t.opacity,e.opacity);return function(e){return t.l=n(e),t.a=r(e),t.b=i(e),t.opacity=a(e),t+""}}function Fp(t){return function(e,n){var r=t((e=Oa(e)).h,(n=Oa(n)).h),i=pn(e.c,n.c),a=pn(e.l,n.l),o=pn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.c=i(t),e.l=a(t),e.opacity=o(t),e+""}}}const Pp=Fp(dn);var jp=Fp(pn);function Yp(t){return function e(n){function r(e,r){var i=t((e=Ha(e)).h,(r=Ha(r)).h),a=pn(e.s,r.s),o=pn(e.l,r.l),s=pn(e.opacity,r.opacity);return function(t){return e.h=i(t),e.s=a(t),e.l=o(Math.pow(t,n)),e.opacity=s(t),e+""}}return n=+n,r.gamma=e,r}(1)}const zp=Yp(dn);var Up=Yp(pn);function qp(t,e){for(var n=0,r=e.length-1,i=e[0],a=new Array(r<0?0:r);n<r;)a[n]=t(i,i=e[++n]);return function(t){var e=Math.max(0,Math.min(r-1,Math.floor(t*=r)));return a[e](t-e)}}function Hp(t,e){for(var n=new Array(e),r=0;r<e;++r)n[r]=t(r/(e-1));return n}function $p(t){for(var e,n=-1,r=t.length,i=t[r-1],a=0;++n<r;)e=i,i=t[n],a+=e[1]*i[0]-e[0]*i[1];return a/2}function Wp(t){for(var e,n,r=-1,i=t.length,a=0,o=0,s=t[i-1],c=0;++r<i;)e=s,s=t[r],c+=n=e[0]*s[1]-s[0]*e[1],a+=(e[0]+s[0])*n,o+=(e[1]+s[1])*n;return[a/(c*=3),o/c]}function Vp(t,e,n){return(e[0]-t[0])*(n[1]-t[1])-(e[1]-t[1])*(n[0]-t[0])}function Gp(t,e){return t[0]-e[0]||t[1]-e[1]}function Xp(t){for(var e=t.length,n=[0,1],r=2,i=2;i<e;++i){for(;r>1&&Vp(t[n[r-2]],t[n[r-1]],t[i])<=0;)--r;n[r++]=i}return n.slice(0,r)}function Zp(t){if((n=t.length)<3)return null;var e,n,r=new Array(n),i=new Array(n);for(e=0;e<n;++e)r[e]=[+t[e][0],+t[e][1],e];for(r.sort(Gp),e=0;e<n;++e)i[e]=[r[e][0],-r[e][1]];var a=Xp(r),o=Xp(i),s=o[0]===a[0],c=o[o.length-1]===a[a.length-1],u=[];for(e=a.length-1;e>=0;--e)u.push(t[r[a[e]][2]]);for(e=+s;e<o.length-c;++e)u.push(t[r[o[e]][2]]);return u}function Qp(t,e){for(var n,r,i=t.length,a=t[i-1],o=e[0],s=e[1],c=a[0],u=a[1],l=!1,h=0;h<i;++h)n=(a=t[h])[0],(r=a[1])>s!=u>s&&o<(c-n)*(s-r)/(u-r)+n&&(l=!l),c=n,u=r;return l}function Kp(t){for(var e,n,r=-1,i=t.length,a=t[i-1],o=a[0],s=a[1],c=0;++r<i;)e=o,n=s,e-=o=(a=t[r])[0],n-=s=a[1],c+=Math.sqrt(e*e+n*n);return c}function Jp(){return Math.random()}const ty=function t(e){function n(t,n){return t=null==t?0:+t,n=null==n?1:+n,1===arguments.length?(n=t,t=0):n-=t,function(){return e()*n+t}}return n.source=t,n}(Jp),ey=function t(e){function n(t,n){var r,i;return t=null==t?0:+t,n=null==n?1:+n,function(){var a;if(null!=r)a=r,r=null;else do{r=2*e()-1,a=2*e()-1,i=r*r+a*a}while(!i||i>1);return t+n*a*Math.sqrt(-2*Math.log(i)/i)}}return n.source=t,n}(Jp),ny=function t(e){function n(){var t=ey.source(e).apply(this,arguments);return function(){return Math.exp(t())}}return n.source=t,n}(Jp),ry=function t(e){function n(t){return function(){for(var n=0,r=0;r<t;++r)n+=e();return n}}return n.source=t,n}(Jp),iy=function t(e){function n(t){var n=ry.source(e)(t);return function(){return n()/t}}return n.source=t,n}(Jp),ay=function t(e){function n(t){return function(){return-Math.log(1-e())/t}}return n.source=t,n}(Jp);function oy(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t)}return this}function sy(t,e){switch(arguments.length){case 0:break;case 1:this.interpolator(t);break;default:this.interpolator(e).domain(t)}return this}var cy=Array.prototype,uy=cy.map,ly=cy.slice,hy={name:"implicit"};function fy(){var t=na(),e=[],n=[],r=hy;function i(i){var a=i+"",o=t.get(a);if(!o){if(r!==hy)return r;t.set(a,o=e.push(i))}return n[(o-1)%n.length]}return i.domain=function(n){if(!arguments.length)return e.slice();e=[],t=na();for(var r,a,o=-1,s=n.length;++o<s;)t.has(a=(r=n[o])+"")||t.set(a,e.push(r));return i},i.range=function(t){return arguments.length?(n=ly.call(t),i):n.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return fy(e,n).unknown(r)},oy.apply(i,arguments),i}function dy(){var t,e,n=fy().unknown(void 0),r=n.domain,i=n.range,a=[0,1],o=!1,s=0,c=0,u=.5;function l(){var n=r().length,l=a[1]<a[0],h=a[l-0],f=a[1-l];t=(f-h)/Math.max(1,n-s+2*c),o&&(t=Math.floor(t)),h+=(f-h-t*(n-s))*u,e=t*(1-s),o&&(h=Math.round(h),e=Math.round(e));var d=k(n).map((function(e){return h+t*e}));return i(l?d.reverse():d)}return delete n.unknown,n.domain=function(t){return arguments.length?(r(t),l()):r()},n.range=function(t){return arguments.length?(a=[+t[0],+t[1]],l()):a.slice()},n.rangeRound=function(t){return a=[+t[0],+t[1]],o=!0,l()},n.bandwidth=function(){return e},n.step=function(){return t},n.round=function(t){return arguments.length?(o=!!t,l()):o},n.padding=function(t){return arguments.length?(s=Math.min(1,c=+t),l()):s},n.paddingInner=function(t){return arguments.length?(s=Math.min(1,t),l()):s},n.paddingOuter=function(t){return arguments.length?(c=+t,l()):c},n.align=function(t){return arguments.length?(u=Math.max(0,Math.min(1,t)),l()):u},n.copy=function(){return dy(r(),a).round(o).paddingInner(s).paddingOuter(c).align(u)},oy.apply(l(),arguments)}function py(t){var e=t.copy;return t.padding=t.paddingOuter,delete t.paddingInner,delete t.paddingOuter,t.copy=function(){return py(e())},t}function yy(){return py(dy.apply(null,arguments).paddingInner(1))}function gy(t){return+t}var my=[0,1];function vy(t){return t}function by(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:(n=isNaN(e)?NaN:.5,function(){return n});var n}function _y(t){var e,n=t[0],r=t[t.length-1];return n>r&&(e=n,n=r,r=e),function(t){return Math.max(n,Math.min(r,t))}}function xy(t,e,n){var r=t[0],i=t[1],a=e[0],o=e[1];return i<r?(r=by(i,r),a=n(o,a)):(r=by(r,i),a=n(a,o)),function(t){return a(r(t))}}function wy(t,e,n){var r=Math.min(t.length,e.length)-1,i=new Array(r),a=new Array(r),o=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),e=e.slice().reverse());++o<r;)i[o]=by(t[o],t[o+1]),a[o]=n(e[o],e[o+1]);return function(e){var n=u(t,e,1,r)-1;return a[n](i[n](e))}}function ky(t,e){return e.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function Ty(){var t,e,n,r,i,a,o=my,s=my,c=Mn,u=vy;function l(){return r=Math.min(o.length,s.length)>2?wy:xy,i=a=null,h}function h(e){return isNaN(e=+e)?n:(i||(i=r(o.map(t),s,c)))(t(u(e)))}return h.invert=function(n){return u(e((a||(a=r(s,o.map(t),Tn)))(n)))},h.domain=function(t){return arguments.length?(o=uy.call(t,gy),u===vy||(u=_y(o)),l()):o.slice()},h.range=function(t){return arguments.length?(s=ly.call(t),l()):s.slice()},h.rangeRound=function(t){return s=ly.call(t),c=Mp,l()},h.clamp=function(t){return arguments.length?(u=t?_y(o):vy,h):u!==vy},h.interpolate=function(t){return arguments.length?(c=t,l()):c},h.unknown=function(t){return arguments.length?(n=t,h):n},function(n,r){return t=n,e=r,l()}}function Ey(t,e){return Ty()(t,e)}function Cy(t,e,n,r){var i,a=M(t,e,n);switch((r=cc(null==r?",f":r)).type){case"s":var o=Math.max(Math.abs(t),Math.abs(e));return null!=r.precision||isNaN(i=xc(a,o))||(r.precision=i),yc(r,o);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=wc(a,Math.max(Math.abs(t),Math.abs(e))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=_c(a))||(r.precision=i-2*("%"===r.type))}return pc(r)}function Sy(t){var e=t.domain;return t.ticks=function(t){var n=e();return S(n[0],n[n.length-1],null==t?10:t)},t.tickFormat=function(t,n){var r=e();return Cy(r[0],r[r.length-1],null==t?10:t,n)},t.nice=function(n){null==n&&(n=10);var r,i=e(),a=0,o=i.length-1,s=i[a],c=i[o];return c<s&&(r=s,s=c,c=r,r=a,a=o,o=r),(r=A(s,c,n))>0?r=A(s=Math.floor(s/r)*r,c=Math.ceil(c/r)*r,n):r<0&&(r=A(s=Math.ceil(s*r)/r,c=Math.floor(c*r)/r,n)),r>0?(i[a]=Math.floor(s/r)*r,i[o]=Math.ceil(c/r)*r,e(i)):r<0&&(i[a]=Math.ceil(s*r)/r,i[o]=Math.floor(c*r)/r,e(i)),t},t}function Ay(){var t=Ey(vy,vy);return t.copy=function(){return ky(t,Ay())},oy.apply(t,arguments),Sy(t)}function My(t){var e;function n(t){return isNaN(t=+t)?e:t}return n.invert=n,n.domain=n.range=function(e){return arguments.length?(t=uy.call(e,gy),n):t.slice()},n.unknown=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return My(t).unknown(e)},t=arguments.length?uy.call(t,gy):[0,1],Sy(n)}function Ny(t,e){var n,r=0,i=(t=t.slice()).length-1,a=t[r],o=t[i];return o<a&&(n=r,r=i,i=n,n=a,a=o,o=n),t[r]=e.floor(a),t[i]=e.ceil(o),t}function Dy(t){return Math.log(t)}function Oy(t){return Math.exp(t)}function By(t){return-Math.log(-t)}function Ly(t){return-Math.exp(-t)}function Iy(t){return isFinite(t)?+("1e"+t):t<0?0:t}function Ry(t){return function(e){return-t(-e)}}function Fy(t){var e,n,r=t(Dy,Oy),i=r.domain,a=10;function o(){return e=function(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),function(e){return Math.log(e)/t})}(a),n=function(t){return 10===t?Iy:t===Math.E?Math.exp:function(e){return Math.pow(t,e)}}(a),i()[0]<0?(e=Ry(e),n=Ry(n),t(By,Ly)):t(Dy,Oy),r}return r.base=function(t){return arguments.length?(a=+t,o()):a},r.domain=function(t){return arguments.length?(i(t),o()):i()},r.ticks=function(t){var r,o=i(),s=o[0],c=o[o.length-1];(r=c<s)&&(f=s,s=c,c=f);var u,l,h,f=e(s),d=e(c),p=null==t?10:+t,y=[];if(!(a%1)&&d-f<p){if(f=Math.round(f)-1,d=Math.round(d)+1,s>0){for(;f<d;++f)for(l=1,u=n(f);l<a;++l)if(!((h=u*l)<s)){if(h>c)break;y.push(h)}}else for(;f<d;++f)for(l=a-1,u=n(f);l>=1;--l)if(!((h=u*l)<s)){if(h>c)break;y.push(h)}}else y=S(f,d,Math.min(d-f,p)).map(n);return r?y.reverse():y},r.tickFormat=function(t,i){if(null==i&&(i=10===a?".0e":","),"function"!=typeof i&&(i=pc(i)),t===1/0)return i;null==t&&(t=10);var o=Math.max(1,a*t/r.ticks().length);return function(t){var r=t/n(Math.round(e(t)));return r*a<a-.5&&(r*=a),r<=o?i(t):""}},r.nice=function(){return i(Ny(i(),{floor:function(t){return n(Math.floor(e(t)))},ceil:function(t){return n(Math.ceil(e(t)))}}))},r}function Py(){var t=Fy(Ty()).domain([1,10]);return t.copy=function(){return ky(t,Py()).base(t.base())},oy.apply(t,arguments),t}function jy(t){return function(e){return Math.sign(e)*Math.log1p(Math.abs(e/t))}}function Yy(t){return function(e){return Math.sign(e)*Math.expm1(Math.abs(e))*t}}function zy(t){var e=1,n=t(jy(e),Yy(e));return n.constant=function(n){return arguments.length?t(jy(e=+n),Yy(e)):e},Sy(n)}function Uy(){var t=zy(Ty());return t.copy=function(){return ky(t,Uy()).constant(t.constant())},oy.apply(t,arguments)}function qy(t){return function(e){return e<0?-Math.pow(-e,t):Math.pow(e,t)}}function Hy(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function $y(t){return t<0?-t*t:t*t}function Wy(t){var e=t(vy,vy),n=1;function r(){return 1===n?t(vy,vy):.5===n?t(Hy,$y):t(qy(n),qy(1/n))}return e.exponent=function(t){return arguments.length?(n=+t,r()):n},Sy(e)}function Vy(){var t=Wy(Ty());return t.copy=function(){return ky(t,Vy()).exponent(t.exponent())},oy.apply(t,arguments),t}function Gy(){return Vy.apply(null,arguments).exponent(.5)}function Xy(){var t,e=[],n=[],r=[];function a(){var t=0,i=Math.max(1,n.length);for(r=new Array(i-1);++t<i;)r[t-1]=O(e,t/i);return o}function o(e){return isNaN(e=+e)?t:n[u(r,e)]}return o.invertExtent=function(t){var i=n.indexOf(t);return i<0?[NaN,NaN]:[i>0?r[i-1]:e[0],i<r.length?r[i]:e[e.length-1]]},o.domain=function(t){if(!arguments.length)return e.slice();e=[];for(var n,r=0,o=t.length;r<o;++r)null==(n=t[r])||isNaN(n=+n)||e.push(n);return e.sort(i),a()},o.range=function(t){return arguments.length?(n=ly.call(t),a()):n.slice()},o.unknown=function(e){return arguments.length?(t=e,o):t},o.quantiles=function(){return r.slice()},o.copy=function(){return Xy().domain(e).range(n).unknown(t)},oy.apply(o,arguments)}function Zy(){var t,e=0,n=1,r=1,i=[.5],a=[0,1];function o(e){return e<=e?a[u(i,e,0,r)]:t}function s(){var t=-1;for(i=new Array(r);++t<r;)i[t]=((t+1)*n-(t-r)*e)/(r+1);return o}return o.domain=function(t){return arguments.length?(e=+t[0],n=+t[1],s()):[e,n]},o.range=function(t){return arguments.length?(r=(a=ly.call(t)).length-1,s()):a.slice()},o.invertExtent=function(t){var o=a.indexOf(t);return o<0?[NaN,NaN]:o<1?[e,i[0]]:o>=r?[i[r-1],n]:[i[o-1],i[o]]},o.unknown=function(e){return arguments.length?(t=e,o):o},o.thresholds=function(){return i.slice()},o.copy=function(){return Zy().domain([e,n]).range(a).unknown(t)},oy.apply(Sy(o),arguments)}function Qy(){var t,e=[.5],n=[0,1],r=1;function i(i){return i<=i?n[u(e,i,0,r)]:t}return i.domain=function(t){return arguments.length?(e=ly.call(t),r=Math.min(e.length,n.length-1),i):e.slice()},i.range=function(t){return arguments.length?(n=ly.call(t),r=Math.min(e.length,n.length-1),i):n.slice()},i.invertExtent=function(t){var r=n.indexOf(t);return[e[r-1],e[r]]},i.unknown=function(e){return arguments.length?(t=e,i):t},i.copy=function(){return Qy().domain(e).range(n).unknown(t)},oy.apply(i,arguments)}var Ky=new Date,Jy=new Date;function tg(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return tg((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return Ky.setTime(+e),Jy.setTime(+r),t(Ky),t(Jy),Math.floor(n(Ky,Jy))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}var eg=tg((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));eg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?tg((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};const ng=eg;var rg=eg.range,ig=tg((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,e){t.setMonth(t.getMonth()+e)}),(function(t,e){return e.getMonth()-t.getMonth()+12*(e.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()}));const ag=ig;var og=ig.range,sg=1e3,cg=6e4,ug=36e5,lg=864e5,hg=6048e5;function fg(t){return tg((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*cg)/hg}))}var dg=fg(0),pg=fg(1),yg=fg(2),gg=fg(3),mg=fg(4),vg=fg(5),bg=fg(6),_g=dg.range,xg=pg.range,wg=yg.range,kg=gg.range,Tg=mg.range,Eg=vg.range,Cg=bg.range,Sg=tg((function(t){t.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+e)}),(function(t,e){return(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*cg)/lg}),(function(t){return t.getDate()-1}));const Ag=Sg;var Mg=Sg.range,Ng=tg((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*sg-t.getMinutes()*cg)}),(function(t,e){t.setTime(+t+e*ug)}),(function(t,e){return(e-t)/ug}),(function(t){return t.getHours()}));const Dg=Ng;var Og=Ng.range,Bg=tg((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*sg)}),(function(t,e){t.setTime(+t+e*cg)}),(function(t,e){return(e-t)/cg}),(function(t){return t.getMinutes()}));const Lg=Bg;var Ig=Bg.range,Rg=tg((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,e){t.setTime(+t+e*sg)}),(function(t,e){return(e-t)/sg}),(function(t){return t.getUTCSeconds()}));const Fg=Rg;var Pg=Rg.range,jg=tg((function(){}),(function(t,e){t.setTime(+t+e)}),(function(t,e){return e-t}));jg.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?tg((function(e){e.setTime(Math.floor(e/t)*t)}),(function(e,n){e.setTime(+e+n*t)}),(function(e,n){return(n-e)/t})):jg:null};const Yg=jg;var zg=jg.range;function Ug(t){return tg((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/hg}))}var qg=Ug(0),Hg=Ug(1),$g=Ug(2),Wg=Ug(3),Vg=Ug(4),Gg=Ug(5),Xg=Ug(6),Zg=qg.range,Qg=Hg.range,Kg=$g.range,Jg=Wg.range,tm=Vg.range,em=Gg.range,nm=Xg.range,rm=tg((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/lg}),(function(t){return t.getUTCDate()-1}));const im=rm;var am=rm.range,om=tg((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));om.every=function(t){return isFinite(t=Math.floor(t))&&t>0?tg((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};const sm=om;var cm=om.range;function um(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function lm(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function hm(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}function fm(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=Tm(i),l=Em(i),h=Tm(a),f=Em(a),d=Tm(o),p=Em(o),y=Tm(s),g=Em(s),m=Tm(c),v=Em(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:Wm,e:Wm,f:Qm,g:cv,G:lv,H:Vm,I:Gm,j:Xm,L:Zm,m:Km,M:Jm,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:Bv,s:Lv,S:tv,u:ev,U:nv,V:iv,w:av,W:ov,x:null,X:null,y:sv,Y:uv,Z:hv,"%":Ov},_={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:fv,e:fv,f:mv,g:Av,G:Nv,H:dv,I:pv,j:yv,L:gv,m:vv,M:bv,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:Bv,s:Lv,S:_v,u:xv,U:wv,V:Tv,w:Ev,W:Cv,x:null,X:null,y:Sv,Y:Mv,Z:Dv,"%":Ov},x={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p[r[0].toLowerCase()],n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f[r[0].toLowerCase()],n+r[0].length):-1},b:function(t,e,n){var r=m.exec(e.slice(n));return r?(t.m=v[r[0].toLowerCase()],n+r[0].length):-1},B:function(t,e,n){var r=y.exec(e.slice(n));return r?(t.m=g[r[0].toLowerCase()],n+r[0].length):-1},c:function(t,n,r){return T(t,e,n,r)},d:Rm,e:Rm,f:Um,g:Om,G:Dm,H:Pm,I:Pm,j:Fm,L:zm,m:Im,M:jm,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l[r[0].toLowerCase()],n+r[0].length):-1},q:Lm,Q:Hm,s:$m,S:Ym,u:Sm,U:Am,V:Mm,w:Cm,W:Nm,x:function(t,e,r){return T(t,n,e,r)},X:function(t,e,n){return T(t,r,e,n)},y:Om,Y:Dm,Z:Bm,"%":qm};function w(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=vm[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function k(t,e){return function(n){var r,i,a=hm(1900,void 0,1);if(T(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(e&&!("Z"in a)&&(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=lm(hm(a.y,0,1))).getUTCDay(),r=i>4||0===i?Hg.ceil(r):Hg(r),r=im.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=um(hm(a.y,0,1))).getDay(),r=i>4||0===i?pg.ceil(r):pg(r),r=Ag.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?lm(hm(a.y,0,1)).getUTCDay():um(hm(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,lm(a)):um(a)}}function T(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=x[i in vm?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return b.x=w(n,b),b.X=w(r,b),b.c=w(e,b),_.x=w(n,_),_.X=w(r,_),_.c=w(e,_),{format:function(t){var e=w(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=k(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=w(t+="",_);return e.toString=function(){return t},e},utcParse:function(t){var e=k(t+="",!0);return e.toString=function(){return t},e}}}var dm,pm,ym,gm,mm,vm={"-":"",_:" ",0:"0"},bm=/^\s*\d+/,_m=/^%/,xm=/[\\^$*+?|[\]().{}]/g;function wm(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function km(t){return t.replace(xm,"\\$&")}function Tm(t){return new RegExp("^(?:"+t.map(km).join("|")+")","i")}function Em(t){for(var e={},n=-1,r=t.length;++n<r;)e[t[n].toLowerCase()]=n;return e}function Cm(t,e,n){var r=bm.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function Sm(t,e,n){var r=bm.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function Am(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function Mm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function Nm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function Dm(t,e,n){var r=bm.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function Om(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function Bm(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function Lm(t,e,n){var r=bm.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function Im(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function Rm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function Fm(t,e,n){var r=bm.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function Pm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function jm(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function Ym(t,e,n){var r=bm.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function zm(t,e,n){var r=bm.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function Um(t,e,n){var r=bm.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function qm(t,e,n){var r=_m.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function Hm(t,e,n){var r=bm.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function $m(t,e,n){var r=bm.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function Wm(t,e){return wm(t.getDate(),e,2)}function Vm(t,e){return wm(t.getHours(),e,2)}function Gm(t,e){return wm(t.getHours()%12||12,e,2)}function Xm(t,e){return wm(1+Ag.count(ng(t),t),e,3)}function Zm(t,e){return wm(t.getMilliseconds(),e,3)}function Qm(t,e){return Zm(t,e)+"000"}function Km(t,e){return wm(t.getMonth()+1,e,2)}function Jm(t,e){return wm(t.getMinutes(),e,2)}function tv(t,e){return wm(t.getSeconds(),e,2)}function ev(t){var e=t.getDay();return 0===e?7:e}function nv(t,e){return wm(dg.count(ng(t)-1,t),e,2)}function rv(t){var e=t.getDay();return e>=4||0===e?mg(t):mg.ceil(t)}function iv(t,e){return t=rv(t),wm(mg.count(ng(t),t)+(4===ng(t).getDay()),e,2)}function av(t){return t.getDay()}function ov(t,e){return wm(pg.count(ng(t)-1,t),e,2)}function sv(t,e){return wm(t.getFullYear()%100,e,2)}function cv(t,e){return wm((t=rv(t)).getFullYear()%100,e,2)}function uv(t,e){return wm(t.getFullYear()%1e4,e,4)}function lv(t,e){var n=t.getDay();return wm((t=n>=4||0===n?mg(t):mg.ceil(t)).getFullYear()%1e4,e,4)}function hv(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+wm(e/60|0,"0",2)+wm(e%60,"0",2)}function fv(t,e){return wm(t.getUTCDate(),e,2)}function dv(t,e){return wm(t.getUTCHours(),e,2)}function pv(t,e){return wm(t.getUTCHours()%12||12,e,2)}function yv(t,e){return wm(1+im.count(sm(t),t),e,3)}function gv(t,e){return wm(t.getUTCMilliseconds(),e,3)}function mv(t,e){return gv(t,e)+"000"}function vv(t,e){return wm(t.getUTCMonth()+1,e,2)}function bv(t,e){return wm(t.getUTCMinutes(),e,2)}function _v(t,e){return wm(t.getUTCSeconds(),e,2)}function xv(t){var e=t.getUTCDay();return 0===e?7:e}function wv(t,e){return wm(qg.count(sm(t)-1,t),e,2)}function kv(t){var e=t.getUTCDay();return e>=4||0===e?Vg(t):Vg.ceil(t)}function Tv(t,e){return t=kv(t),wm(Vg.count(sm(t),t)+(4===sm(t).getUTCDay()),e,2)}function Ev(t){return t.getUTCDay()}function Cv(t,e){return wm(Hg.count(sm(t)-1,t),e,2)}function Sv(t,e){return wm(t.getUTCFullYear()%100,e,2)}function Av(t,e){return wm((t=kv(t)).getUTCFullYear()%100,e,2)}function Mv(t,e){return wm(t.getUTCFullYear()%1e4,e,4)}function Nv(t,e){var n=t.getUTCDay();return wm((t=n>=4||0===n?Vg(t):Vg.ceil(t)).getUTCFullYear()%1e4,e,4)}function Dv(){return"+0000"}function Ov(){return"%"}function Bv(t){return+t}function Lv(t){return Math.floor(+t/1e3)}function Iv(t){return dm=fm(t),pm=dm.format,ym=dm.parse,gm=dm.utcFormat,mm=dm.utcParse,dm}Iv({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var Rv=31536e6;function Fv(t){return new Date(t)}function Pv(t){return t instanceof Date?+t:+new Date(+t)}function jv(t,e,n,r,i,o,s,c,u){var l=Ey(vy,vy),h=l.invert,f=l.domain,d=u(".%L"),p=u(":%S"),y=u("%I:%M"),g=u("%I %p"),m=u("%a %d"),v=u("%b %d"),b=u("%B"),_=u("%Y"),x=[[s,1,1e3],[s,5,5e3],[s,15,15e3],[s,30,3e4],[o,1,6e4],[o,5,3e5],[o,15,9e5],[o,30,18e5],[i,1,36e5],[i,3,108e5],[i,6,216e5],[i,12,432e5],[r,1,864e5],[r,2,1728e5],[n,1,6048e5],[e,1,2592e6],[e,3,7776e6],[t,1,Rv]];function w(a){return(s(a)<a?d:o(a)<a?p:i(a)<a?y:r(a)<a?g:e(a)<a?n(a)<a?m:v:t(a)<a?b:_)(a)}function k(e,n,r,i){if(null==e&&(e=10),"number"==typeof e){var o=Math.abs(r-n)/e,s=a((function(t){return t[2]})).right(x,o);s===x.length?(i=M(n/Rv,r/Rv,e),e=t):s?(i=(s=x[o/x[s-1][2]<x[s][2]/o?s-1:s])[1],e=s[0]):(i=Math.max(M(n,r,e),1),e=c)}return null==i?e:e.every(i)}return l.invert=function(t){return new Date(h(t))},l.domain=function(t){return arguments.length?f(uy.call(t,Pv)):f().map(Fv)},l.ticks=function(t,e){var n,r=f(),i=r[0],a=r[r.length-1],o=a<i;return o&&(n=i,i=a,a=n),n=(n=k(t,i,a,e))?n.range(i,a+1):[],o?n.reverse():n},l.tickFormat=function(t,e){return null==e?w:u(e)},l.nice=function(t,e){var n=f();return(t=k(t,n[0],n[n.length-1],e))?f(Ny(n,t)):l},l.copy=function(){return ky(l,jv(t,e,n,r,i,o,s,c,u))},l}function Yv(){return oy.apply(jv(ng,ag,dg,Ag,Dg,Lg,Fg,Yg,pm).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}var zv=tg((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCMonth(t.getUTCMonth()+e)}),(function(t,e){return e.getUTCMonth()-t.getUTCMonth()+12*(e.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()}));const Uv=zv;var qv=zv.range,Hv=tg((function(t){t.setUTCMinutes(0,0,0)}),(function(t,e){t.setTime(+t+e*ug)}),(function(t,e){return(e-t)/ug}),(function(t){return t.getUTCHours()}));const $v=Hv;var Wv=Hv.range,Vv=tg((function(t){t.setUTCSeconds(0,0)}),(function(t,e){t.setTime(+t+e*cg)}),(function(t,e){return(e-t)/cg}),(function(t){return t.getUTCMinutes()}));const Gv=Vv;var Xv=Vv.range;function Zv(){return oy.apply(jv(sm,Uv,qg,im,$v,Gv,Fg,Yg,gm).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)}function Qv(){var t,e,n,r,i,a=0,o=1,s=vy,c=!1;function u(e){return isNaN(e=+e)?i:s(0===n?.5:(e=(r(e)-t)*n,c?Math.max(0,Math.min(1,e)):e))}return u.domain=function(i){return arguments.length?(t=r(a=+i[0]),e=r(o=+i[1]),n=t===e?0:1/(e-t),u):[a,o]},u.clamp=function(t){return arguments.length?(c=!!t,u):c},u.interpolator=function(t){return arguments.length?(s=t,u):s},u.unknown=function(t){return arguments.length?(i=t,u):i},function(i){return r=i,t=i(a),e=i(o),n=t===e?0:1/(e-t),u}}function Kv(t,e){return e.domain(t.domain()).interpolator(t.interpolator()).clamp(t.clamp()).unknown(t.unknown())}function Jv(){var t=Sy(Qv()(vy));return t.copy=function(){return Kv(t,Jv())},sy.apply(t,arguments)}function tb(){var t=Fy(Qv()).domain([1,10]);return t.copy=function(){return Kv(t,tb()).base(t.base())},sy.apply(t,arguments)}function eb(){var t=zy(Qv());return t.copy=function(){return Kv(t,eb()).constant(t.constant())},sy.apply(t,arguments)}function nb(){var t=Wy(Qv());return t.copy=function(){return Kv(t,nb()).exponent(t.exponent())},sy.apply(t,arguments)}function rb(){return nb.apply(null,arguments).exponent(.5)}function ib(){var t=[],e=vy;function n(n){if(!isNaN(n=+n))return e((u(t,n)-1)/(t.length-1))}return n.domain=function(e){if(!arguments.length)return t.slice();t=[];for(var r,a=0,o=e.length;a<o;++a)null==(r=e[a])||isNaN(r=+r)||t.push(r);return t.sort(i),n},n.interpolator=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return ib(e).domain(t)},sy.apply(n,arguments)}function ab(){var t,e,n,r,i,a,o,s=0,c=.5,u=1,l=vy,h=!1;function f(t){return isNaN(t=+t)?o:(t=.5+((t=+a(t))-e)*(t<e?r:i),l(h?Math.max(0,Math.min(1,t)):t))}return f.domain=function(o){return arguments.length?(t=a(s=+o[0]),e=a(c=+o[1]),n=a(u=+o[2]),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f):[s,c,u]},f.clamp=function(t){return arguments.length?(h=!!t,f):h},f.interpolator=function(t){return arguments.length?(l=t,f):l},f.unknown=function(t){return arguments.length?(o=t,f):o},function(o){return a=o,t=o(s),e=o(c),n=o(u),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f}}function ob(){var t=Sy(ab()(vy));return t.copy=function(){return Kv(t,ob())},sy.apply(t,arguments)}function sb(){var t=Fy(ab()).domain([.1,1,10]);return t.copy=function(){return Kv(t,sb()).base(t.base())},sy.apply(t,arguments)}function cb(){var t=zy(ab());return t.copy=function(){return Kv(t,cb()).constant(t.constant())},sy.apply(t,arguments)}function ub(){var t=Wy(ab());return t.copy=function(){return Kv(t,ub()).exponent(t.exponent())},sy.apply(t,arguments)}function lb(){return ub.apply(null,arguments).exponent(.5)}function hb(t){for(var e=t.length/6|0,n=new Array(e),r=0;r<e;)n[r]="#"+t.slice(6*r,6*++r);return n}const fb=hb("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),db=hb("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666"),pb=hb("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666"),yb=hb("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928"),gb=hb("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2"),mb=hb("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc"),vb=hb("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999"),bb=hb("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3"),_b=hb("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f"),xb=hb("4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab");function wb(t){return mn(t[t.length-1])}var kb=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(hb);const Tb=wb(kb);var Eb=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(hb);const Cb=wb(Eb);var Sb=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(hb);const Ab=wb(Sb);var Mb=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(hb);const Nb=wb(Mb);var Db=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(hb);const Ob=wb(Db);var Bb=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(hb);const Lb=wb(Bb);var Ib=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(hb);const Rb=wb(Ib);var Fb=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(hb);const Pb=wb(Fb);var jb=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(hb);const Yb=wb(jb);var zb=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(hb);const Ub=wb(zb);var qb=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(hb);const Hb=wb(qb);var $b=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(hb);const Wb=wb($b);var Vb=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(hb);const Gb=wb(Vb);var Xb=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(hb);const Zb=wb(Xb);var Qb=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(hb);const Kb=wb(Qb);var Jb=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(hb);const t_=wb(Jb);var e_=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(hb);const n_=wb(e_);var r_=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(hb);const i_=wb(r_);var a_=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(hb);const o_=wb(a_);var s_=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(hb);const c_=wb(s_);var u_=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(hb);const l_=wb(u_);var h_=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(hb);const f_=wb(h_);var d_=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(hb);const p_=wb(d_);var y_=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(hb);const g_=wb(y_);var m_=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(hb);const v_=wb(m_);var b_=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(hb);const __=wb(b_);var x_=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(hb);const w_=wb(x_);function k_(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"}const T_=Up(Ha(300,.5,0),Ha(-240,.5,1));var E_=Up(Ha(-100,.75,.35),Ha(80,1.5,.8)),C_=Up(Ha(260,.75,.35),Ha(80,1.5,.8)),S_=Ha();function A_(t){(t<0||t>1)&&(t-=Math.floor(t));var e=Math.abs(t-.5);return S_.h=360*t-100,S_.s=1.5-1.5*e,S_.l=.8-.9*e,S_+""}var M_=Qe(),N_=Math.PI/3,D_=2*Math.PI/3;function O_(t){var e;return t=(.5-t)*Math.PI,M_.r=255*(e=Math.sin(t))*e,M_.g=255*(e=Math.sin(t+N_))*e,M_.b=255*(e=Math.sin(t+D_))*e,M_+""}function B_(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"}function L_(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}}const I_=L_(hb("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"));var R_=L_(hb("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),F_=L_(hb("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),P_=L_(hb("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function j_(t){return Te(ie(t).call(document.documentElement))}var Y_=0;function z_(){return new U_}function U_(){this._="@"+(++Y_).toString(36)}function q_(t){return"string"==typeof t?new xe([document.querySelectorAll(t)],[document.documentElement]):new xe([null==t?[]:t],_e)}function H_(t,e){null==e&&(e=Nn().touches);for(var n=0,r=e?e.length:0,i=new Array(r);n<r;++n)i[n]=Dn(t,e[n]);return i}function $_(t){return function(){return t}}U_.prototype=z_.prototype={constructor:U_,get:function(t){for(var e=this._;!(e in t);)if(!(t=t.parentNode))return;return t[e]},set:function(t,e){return t[this._]=e},remove:function(t){return this._ in t&&delete t[this._]},toString:function(){return this._}};var W_=Math.abs,V_=Math.atan2,G_=Math.cos,X_=Math.max,Z_=Math.min,Q_=Math.sin,K_=Math.sqrt,J_=1e-12,tx=Math.PI,ex=tx/2,nx=2*tx;function rx(t){return t>1?0:t<-1?tx:Math.acos(t)}function ix(t){return t>=1?ex:t<=-1?-ex:Math.asin(t)}function ax(t){return t.innerRadius}function ox(t){return t.outerRadius}function sx(t){return t.startAngle}function cx(t){return t.endAngle}function ux(t){return t&&t.padAngle}function lx(t,e,n,r,i,a,o,s){var c=n-t,u=r-e,l=o-i,h=s-a,f=h*c-l*u;if(!(f*f<J_))return[t+(f=(l*(e-a)-h*(t-i))/f)*c,e+f*u]}function hx(t,e,n,r,i,a,o){var s=t-n,c=e-r,u=(o?a:-a)/K_(s*s+c*c),l=u*c,h=-u*s,f=t+l,d=e+h,p=n+l,y=r+h,g=(f+p)/2,m=(d+y)/2,v=p-f,b=y-d,_=v*v+b*b,x=i-a,w=f*y-p*d,k=(b<0?-1:1)*K_(X_(0,x*x*_-w*w)),T=(w*b-v*k)/_,E=(-w*v-b*k)/_,C=(w*b+v*k)/_,S=(-w*v+b*k)/_,A=T-g,M=E-m,N=C-g,D=S-m;return A*A+M*M>N*N+D*D&&(T=C,E=S),{cx:T,cy:E,x01:-l,y01:-h,x11:T*(i/x-1),y11:E*(i/x-1)}}function fx(){var t=ax,e=ox,n=$_(0),r=null,i=sx,a=cx,o=ux,s=null;function c(){var c,u,l=+t.apply(this,arguments),h=+e.apply(this,arguments),f=i.apply(this,arguments)-ex,d=a.apply(this,arguments)-ex,p=W_(d-f),y=d>f;if(s||(s=c=Wi()),h<l&&(u=h,h=l,l=u),h>J_)if(p>nx-J_)s.moveTo(h*G_(f),h*Q_(f)),s.arc(0,0,h,f,d,!y),l>J_&&(s.moveTo(l*G_(d),l*Q_(d)),s.arc(0,0,l,d,f,y));else{var g,m,v=f,b=d,_=f,x=d,w=p,k=p,T=o.apply(this,arguments)/2,E=T>J_&&(r?+r.apply(this,arguments):K_(l*l+h*h)),C=Z_(W_(h-l)/2,+n.apply(this,arguments)),S=C,A=C;if(E>J_){var M=ix(E/l*Q_(T)),N=ix(E/h*Q_(T));(w-=2*M)>J_?(_+=M*=y?1:-1,x-=M):(w=0,_=x=(f+d)/2),(k-=2*N)>J_?(v+=N*=y?1:-1,b-=N):(k=0,v=b=(f+d)/2)}var D=h*G_(v),O=h*Q_(v),B=l*G_(x),L=l*Q_(x);if(C>J_){var I,R=h*G_(b),F=h*Q_(b),P=l*G_(_),j=l*Q_(_);if(p<tx&&(I=lx(D,O,P,j,R,F,B,L))){var Y=D-I[0],z=O-I[1],U=R-I[0],q=F-I[1],H=1/Q_(rx((Y*U+z*q)/(K_(Y*Y+z*z)*K_(U*U+q*q)))/2),$=K_(I[0]*I[0]+I[1]*I[1]);S=Z_(C,(l-$)/(H-1)),A=Z_(C,(h-$)/(H+1))}}k>J_?A>J_?(g=hx(P,j,D,O,h,A,y),m=hx(R,F,B,L,h,A,y),s.moveTo(g.cx+g.x01,g.cy+g.y01),A<C?s.arc(g.cx,g.cy,A,V_(g.y01,g.x01),V_(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,A,V_(g.y01,g.x01),V_(g.y11,g.x11),!y),s.arc(0,0,h,V_(g.cy+g.y11,g.cx+g.x11),V_(m.cy+m.y11,m.cx+m.x11),!y),s.arc(m.cx,m.cy,A,V_(m.y11,m.x11),V_(m.y01,m.x01),!y))):(s.moveTo(D,O),s.arc(0,0,h,v,b,!y)):s.moveTo(D,O),l>J_&&w>J_?S>J_?(g=hx(B,L,R,F,l,-S,y),m=hx(D,O,P,j,l,-S,y),s.lineTo(g.cx+g.x01,g.cy+g.y01),S<C?s.arc(g.cx,g.cy,S,V_(g.y01,g.x01),V_(m.y01,m.x01),!y):(s.arc(g.cx,g.cy,S,V_(g.y01,g.x01),V_(g.y11,g.x11),!y),s.arc(0,0,l,V_(g.cy+g.y11,g.cx+g.x11),V_(m.cy+m.y11,m.cx+m.x11),y),s.arc(m.cx,m.cy,S,V_(m.y11,m.x11),V_(m.y01,m.x01),!y))):s.arc(0,0,l,x,_,y):s.lineTo(B,L)}else s.moveTo(0,0);if(s.closePath(),c)return s=null,c+""||null}return c.centroid=function(){var n=(+t.apply(this,arguments)+ +e.apply(this,arguments))/2,r=(+i.apply(this,arguments)+ +a.apply(this,arguments))/2-tx/2;return[G_(r)*n,Q_(r)*n]},c.innerRadius=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),c):t},c.outerRadius=function(t){return arguments.length?(e="function"==typeof t?t:$_(+t),c):e},c.cornerRadius=function(t){return arguments.length?(n="function"==typeof t?t:$_(+t),c):n},c.padRadius=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:$_(+t),c):r},c.startAngle=function(t){return arguments.length?(i="function"==typeof t?t:$_(+t),c):i},c.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:$_(+t),c):a},c.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:$_(+t),c):o},c.context=function(t){return arguments.length?(s=null==t?null:t,c):s},c}function dx(t){this._context=t}function px(t){return new dx(t)}function yx(t){return t[0]}function gx(t){return t[1]}function mx(){var t=yx,e=gx,n=$_(!0),r=null,i=px,a=null;function o(o){var s,c,u,l=o.length,h=!1;for(null==r&&(a=i(u=Wi())),s=0;s<=l;++s)!(s<l&&n(c=o[s],s,o))===h&&((h=!h)?a.lineStart():a.lineEnd()),h&&a.point(+t(c,s,o),+e(c,s,o));if(u)return a=null,u+""||null}return o.x=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),o):t},o.y=function(t){return arguments.length?(e="function"==typeof t?t:$_(+t),o):e},o.defined=function(t){return arguments.length?(n="function"==typeof t?t:$_(!!t),o):n},o.curve=function(t){return arguments.length?(i=t,null!=r&&(a=i(r)),o):i},o.context=function(t){return arguments.length?(null==t?r=a=null:a=i(r=t),o):r},o}function vx(){var t=yx,e=null,n=$_(0),r=gx,i=$_(!0),a=null,o=px,s=null;function c(c){var u,l,h,f,d,p=c.length,y=!1,g=new Array(p),m=new Array(p);for(null==a&&(s=o(d=Wi())),u=0;u<=p;++u){if(!(u<p&&i(f=c[u],u,c))===y)if(y=!y)l=u,s.areaStart(),s.lineStart();else{for(s.lineEnd(),s.lineStart(),h=u-1;h>=l;--h)s.point(g[h],m[h]);s.lineEnd(),s.areaEnd()}y&&(g[u]=+t(f,u,c),m[u]=+n(f,u,c),s.point(e?+e(f,u,c):g[u],r?+r(f,u,c):m[u]))}if(d)return s=null,d+""||null}function u(){return mx().defined(i).curve(o).context(a)}return c.x=function(n){return arguments.length?(t="function"==typeof n?n:$_(+n),e=null,c):t},c.x0=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),c):t},c.x1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:$_(+t),c):e},c.y=function(t){return arguments.length?(n="function"==typeof t?t:$_(+t),r=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:$_(+t),c):n},c.y1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:$_(+t),c):r},c.lineX0=c.lineY0=function(){return u().x(t).y(n)},c.lineY1=function(){return u().x(t).y(r)},c.lineX1=function(){return u().x(e).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:$_(!!t),c):i},c.curve=function(t){return arguments.length?(o=t,null!=a&&(s=o(a)),c):o},c.context=function(t){return arguments.length?(null==t?a=s=null:s=o(a=t),c):a},c}function bx(t,e){return e<t?-1:e>t?1:e>=t?0:NaN}function _x(t){return t}function xx(){var t=_x,e=bx,n=null,r=$_(0),i=$_(nx),a=$_(0);function o(o){var s,c,u,l,h,f=o.length,d=0,p=new Array(f),y=new Array(f),g=+r.apply(this,arguments),m=Math.min(nx,Math.max(-nx,i.apply(this,arguments)-g)),v=Math.min(Math.abs(m)/f,a.apply(this,arguments)),b=v*(m<0?-1:1);for(s=0;s<f;++s)(h=y[p[s]=s]=+t(o[s],s,o))>0&&(d+=h);for(null!=e?p.sort((function(t,n){return e(y[t],y[n])})):null!=n&&p.sort((function(t,e){return n(o[t],o[e])})),s=0,u=d?(m-f*b)/d:0;s<f;++s,g=l)c=p[s],l=g+((h=y[c])>0?h*u:0)+b,y[c]={data:o[c],index:s,value:h,startAngle:g,endAngle:l,padAngle:v};return y}return o.value=function(e){return arguments.length?(t="function"==typeof e?e:$_(+e),o):t},o.sortValues=function(t){return arguments.length?(e=t,n=null,o):e},o.sort=function(t){return arguments.length?(n=t,e=null,o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:$_(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:$_(+t),o):i},o.padAngle=function(t){return arguments.length?(a="function"==typeof t?t:$_(+t),o):a},o}dx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:this._context.lineTo(t,e)}}};var wx=Tx(px);function kx(t){this._curve=t}function Tx(t){function e(e){return new kx(t(e))}return e._curve=t,e}function Ex(t){var e=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?e(Tx(t)):e()._curve},t}function Cx(){return Ex(mx().curve(wx))}function Sx(){var t=vx().curve(wx),e=t.curve,n=t.lineX0,r=t.lineX1,i=t.lineY0,a=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Ex(n())},delete t.lineX0,t.lineEndAngle=function(){return Ex(r())},delete t.lineX1,t.lineInnerRadius=function(){return Ex(i())},delete t.lineY0,t.lineOuterRadius=function(){return Ex(a())},delete t.lineY1,t.curve=function(t){return arguments.length?e(Tx(t)):e()._curve},t}function Ax(t,e){return[(e=+e)*Math.cos(t-=Math.PI/2),e*Math.sin(t)]}kx.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,e){this._curve.point(e*Math.sin(t),e*-Math.cos(t))}};var Mx=Array.prototype.slice;function Nx(t){return t.source}function Dx(t){return t.target}function Ox(t){var e=Nx,n=Dx,r=yx,i=gx,a=null;function o(){var o,s=Mx.call(arguments),c=e.apply(this,s),u=n.apply(this,s);if(a||(a=o=Wi()),t(a,+r.apply(this,(s[0]=c,s)),+i.apply(this,s),+r.apply(this,(s[0]=u,s)),+i.apply(this,s)),o)return a=null,o+""||null}return o.source=function(t){return arguments.length?(e=t,o):e},o.target=function(t){return arguments.length?(n=t,o):n},o.x=function(t){return arguments.length?(r="function"==typeof t?t:$_(+t),o):r},o.y=function(t){return arguments.length?(i="function"==typeof t?t:$_(+t),o):i},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o}function Bx(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e=(e+r)/2,n,e,i,r,i)}function Lx(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e,n=(n+i)/2,r,n,r,i)}function Ix(t,e,n,r,i){var a=Ax(e,n),o=Ax(e,n=(n+i)/2),s=Ax(r,n),c=Ax(r,i);t.moveTo(a[0],a[1]),t.bezierCurveTo(o[0],o[1],s[0],s[1],c[0],c[1])}function Rx(){return Ox(Bx)}function Fx(){return Ox(Lx)}function Px(){var t=Ox(Ix);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t}const jx={draw:function(t,e){var n=Math.sqrt(e/tx);t.moveTo(n,0),t.arc(0,0,n,0,nx)}},Yx={draw:function(t,e){var n=Math.sqrt(e/5)/2;t.moveTo(-3*n,-n),t.lineTo(-n,-n),t.lineTo(-n,-3*n),t.lineTo(n,-3*n),t.lineTo(n,-n),t.lineTo(3*n,-n),t.lineTo(3*n,n),t.lineTo(n,n),t.lineTo(n,3*n),t.lineTo(-n,3*n),t.lineTo(-n,n),t.lineTo(-3*n,n),t.closePath()}};var zx=Math.sqrt(1/3),Ux=2*zx;const qx={draw:function(t,e){var n=Math.sqrt(e/Ux),r=n*zx;t.moveTo(0,-n),t.lineTo(r,0),t.lineTo(0,n),t.lineTo(-r,0),t.closePath()}};var Hx=Math.sin(tx/10)/Math.sin(7*tx/10),$x=Math.sin(nx/10)*Hx,Wx=-Math.cos(nx/10)*Hx;const Vx={draw:function(t,e){var n=Math.sqrt(.8908130915292852*e),r=$x*n,i=Wx*n;t.moveTo(0,-n),t.lineTo(r,i);for(var a=1;a<5;++a){var o=nx*a/5,s=Math.cos(o),c=Math.sin(o);t.lineTo(c*n,-s*n),t.lineTo(s*r-c*i,c*r+s*i)}t.closePath()}},Gx={draw:function(t,e){var n=Math.sqrt(e),r=-n/2;t.rect(r,r,n,n)}};var Xx=Math.sqrt(3);const Zx={draw:function(t,e){var n=-Math.sqrt(e/(3*Xx));t.moveTo(0,2*n),t.lineTo(-Xx*n,-n),t.lineTo(Xx*n,-n),t.closePath()}};var Qx=-.5,Kx=Math.sqrt(3)/2,Jx=1/Math.sqrt(12),tw=3*(Jx/2+1);const ew={draw:function(t,e){var n=Math.sqrt(e/tw),r=n/2,i=n*Jx,a=r,o=n*Jx+n,s=-a,c=o;t.moveTo(r,i),t.lineTo(a,o),t.lineTo(s,c),t.lineTo(Qx*r-Kx*i,Kx*r+Qx*i),t.lineTo(Qx*a-Kx*o,Kx*a+Qx*o),t.lineTo(Qx*s-Kx*c,Kx*s+Qx*c),t.lineTo(Qx*r+Kx*i,Qx*i-Kx*r),t.lineTo(Qx*a+Kx*o,Qx*o-Kx*a),t.lineTo(Qx*s+Kx*c,Qx*c-Kx*s),t.closePath()}};var nw=[jx,Yx,qx,Gx,Vx,Zx,ew];function rw(){var t=$_(jx),e=$_(64),n=null;function r(){var r;if(n||(n=r=Wi()),t.apply(this,arguments).draw(n,+e.apply(this,arguments)),r)return n=null,r+""||null}return r.type=function(e){return arguments.length?(t="function"==typeof e?e:$_(e),r):t},r.size=function(t){return arguments.length?(e="function"==typeof t?t:$_(+t),r):e},r.context=function(t){return arguments.length?(n=null==t?null:t,r):n},r}function iw(){}function aw(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function ow(t){this._context=t}function sw(t){return new ow(t)}function cw(t){this._context=t}function uw(t){return new cw(t)}function lw(t){this._context=t}function hw(t){return new lw(t)}function fw(t,e){this._basis=new ow(t),this._beta=e}ow.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:aw(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:aw(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},cw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:aw(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},lw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:aw(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}},fw.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,e=this._y,n=t.length-1;if(n>0)for(var r,i=t[0],a=e[0],o=t[n]-i,s=e[n]-a,c=-1;++c<=n;)r=c/n,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*o),this._beta*e[c]+(1-this._beta)*(a+r*s));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};const dw=function t(e){function n(t){return 1===e?new ow(t):new fw(t,e)}return n.beta=function(e){return t(+e)},n}(.85);function pw(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function yw(t,e){this._context=t,this._k=(1-e)/6}yw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:pw(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:pw(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const gw=function t(e){function n(t){return new yw(t,e)}return n.tension=function(e){return t(+e)},n}(0);function mw(t,e){this._context=t,this._k=(1-e)/6}mw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:pw(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const vw=function t(e){function n(t){return new mw(t,e)}return n.tension=function(e){return t(+e)},n}(0);function bw(t,e){this._context=t,this._k=(1-e)/6}bw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:pw(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const _w=function t(e){function n(t){return new bw(t,e)}return n.tension=function(e){return t(+e)},n}(0);function xw(t,e,n){var r=t._x1,i=t._y1,a=t._x2,o=t._y2;if(t._l01_a>J_){var s=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*s-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*s-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>J_){var u=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,l=3*t._l23_a*(t._l23_a+t._l12_a);a=(a*u+t._x1*t._l23_2a-e*t._l12_2a)/l,o=(o*u+t._y1*t._l23_2a-n*t._l12_2a)/l}t._context.bezierCurveTo(r,i,a,o,t._x2,t._y2)}function ww(t,e){this._context=t,this._alpha=e}ww.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:xw(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const kw=function t(e){function n(t){return e?new ww(t,e):new yw(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function Tw(t,e){this._context=t,this._alpha=e}Tw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:xw(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const Ew=function t(e){function n(t){return e?new Tw(t,e):new mw(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function Cw(t,e){this._context=t,this._alpha=e}Cw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:xw(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const Sw=function t(e){function n(t){return e?new Cw(t,e):new bw(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function Aw(t){this._context=t}function Mw(t){return new Aw(t)}function Nw(t){return t<0?-1:1}function Dw(t,e,n){var r=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(r||i<0&&-0),o=(n-t._y1)/(i||r<0&&-0),s=(a*i+o*r)/(r+i);return(Nw(a)+Nw(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(s))||0}function Ow(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function Bw(t,e,n){var r=t._x0,i=t._y0,a=t._x1,o=t._y1,s=(a-r)/3;t._context.bezierCurveTo(r+s,i+s*e,a-s,o-s*n,a,o)}function Lw(t){this._context=t}function Iw(t){this._context=new Rw(t)}function Rw(t){this._context=t}function Fw(t){return new Lw(t)}function Pw(t){return new Iw(t)}function jw(t){this._context=t}function Yw(t){var e,n,r=t.length-1,i=new Array(r),a=new Array(r),o=new Array(r);for(i[0]=0,a[0]=2,o[0]=t[0]+2*t[1],e=1;e<r-1;++e)i[e]=1,a[e]=4,o[e]=4*t[e]+2*t[e+1];for(i[r-1]=2,a[r-1]=7,o[r-1]=8*t[r-1]+t[r],e=1;e<r;++e)n=i[e]/a[e-1],a[e]-=n,o[e]-=n*o[e-1];for(i[r-1]=o[r-1]/a[r-1],e=r-2;e>=0;--e)i[e]=(o[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e<r-1;++e)a[e]=2*t[e+1]-i[e+1];return[i,a]}function zw(t){return new jw(t)}function Uw(t,e){this._context=t,this._t=e}function qw(t){return new Uw(t,.5)}function Hw(t){return new Uw(t,0)}function $w(t){return new Uw(t,1)}function Ww(t,e){if((i=t.length)>1)for(var n,r,i,a=1,o=t[e[0]],s=o.length;a<i;++a)for(r=o,o=t[e[a]],n=0;n<s;++n)o[n][1]+=o[n][0]=isNaN(r[n][1])?r[n][0]:r[n][1]}function Vw(t){for(var e=t.length,n=new Array(e);--e>=0;)n[e]=e;return n}function Gw(t,e){return t[e]}function Xw(){var t=$_([]),e=Vw,n=Ww,r=Gw;function i(i){var a,o,s=t.apply(this,arguments),c=i.length,u=s.length,l=new Array(u);for(a=0;a<u;++a){for(var h,f=s[a],d=l[a]=new Array(c),p=0;p<c;++p)d[p]=h=[0,+r(i[p],f,p,i)],h.data=i[p];d.key=f}for(a=0,o=e(l);a<u;++a)l[o[a]].index=a;return n(l,o),l}return i.keys=function(e){return arguments.length?(t="function"==typeof e?e:$_(Mx.call(e)),i):t},i.value=function(t){return arguments.length?(r="function"==typeof t?t:$_(+t),i):r},i.order=function(t){return arguments.length?(e=null==t?Vw:"function"==typeof t?t:$_(Mx.call(t)),i):e},i.offset=function(t){return arguments.length?(n=null==t?Ww:t,i):n},i}function Zw(t,e){if((r=t.length)>0){for(var n,r,i,a=0,o=t[0].length;a<o;++a){for(i=n=0;n<r;++n)i+=t[n][a][1]||0;if(i)for(n=0;n<r;++n)t[n][a][1]/=i}Ww(t,e)}}function Qw(t,e){if((s=t.length)>0)for(var n,r,i,a,o,s,c=0,u=t[e[0]].length;c<u;++c)for(a=o=0,n=0;n<s;++n)(i=(r=t[e[n]][c])[1]-r[0])>0?(r[0]=a,r[1]=a+=i):i<0?(r[1]=o,r[0]=o+=i):(r[0]=0,r[1]=i)}function Kw(t,e){if((n=t.length)>0){for(var n,r=0,i=t[e[0]],a=i.length;r<a;++r){for(var o=0,s=0;o<n;++o)s+=t[o][r][1]||0;i[r][1]+=i[r][0]=-s/2}Ww(t,e)}}function Jw(t,e){if((i=t.length)>0&&(r=(n=t[e[0]]).length)>0){for(var n,r,i,a=0,o=1;o<r;++o){for(var s=0,c=0,u=0;s<i;++s){for(var l=t[e[s]],h=l[o][1]||0,f=(h-(l[o-1][1]||0))/2,d=0;d<s;++d){var p=t[e[d]];f+=(p[o][1]||0)-(p[o-1][1]||0)}c+=h,u+=f*h}n[o-1][1]+=n[o-1][0]=a,c&&(a-=u/c)}n[o-1][1]+=n[o-1][0]=a,Ww(t,e)}}function tk(t){var e=t.map(ek);return Vw(t).sort((function(t,n){return e[t]-e[n]}))}function ek(t){for(var e,n=-1,r=0,i=t.length,a=-1/0;++n<i;)(e=+t[n][1])>a&&(a=e,r=n);return r}function nk(t){var e=t.map(rk);return Vw(t).sort((function(t,n){return e[t]-e[n]}))}function rk(t){for(var e,n=0,r=-1,i=t.length;++r<i;)(e=+t[r][1])&&(n+=e);return n}function ik(t){return nk(t).reverse()}function ak(t){var e,n,r=t.length,i=t.map(rk),a=tk(t),o=0,s=0,c=[],u=[];for(e=0;e<r;++e)n=a[e],o<s?(o+=i[n],c.push(n)):(s+=i[n],u.push(n));return u.reverse().concat(c)}function ok(t){return Vw(t).reverse()}Aw.prototype={areaStart:iw,areaEnd:iw,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}},Lw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Bw(this,this._t0,Ow(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(e=+e,(t=+t)!==this._x1||e!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,Bw(this,Ow(this,n=Dw(this,t,e)),n);break;default:Bw(this,this._t0,n=Dw(this,t,e))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}},(Iw.prototype=Object.create(Lw.prototype)).point=function(t,e){Lw.prototype.point.call(this,e,t)},Rw.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,r,i,a){this._context.bezierCurveTo(e,t,r,n,a,i)}},jw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),2===n)this._context.lineTo(t[1],e[1]);else for(var r=Yw(t),i=Yw(e),a=0,o=1;o<n;++a,++o)this._context.bezierCurveTo(r[0][a],i[0][a],r[1][a],i[1][a],t[o],e[o]);(this._line||0!==this._line&&1===n)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(t,e){this._x.push(+t),this._y.push(+e)}},Uw.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&2===this._point&&this._context.lineTo(this._x,this._y),(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var sk="%Y-%m-%dT%H:%M:%S.%LZ",ck=Date.prototype.toISOString?function(t){return t.toISOString()}:gm(sk);const uk=ck;var lk=+new Date("2000-01-01T00:00:00.000Z")?function(t){var e=new Date(t);return isNaN(e)?null:e}:mm(sk);const hk=lk;function fk(t,e,n){var r=new Wn,i=e;return null==e?(r.restart(t,e,n),r):(e=+e,n=null==n?Hn():+n,r.restart((function a(o){o+=i,r.restart(a,i+=e,n),t(o)}),e,n),r)}function dk(t){return function(){return t}}function pk(t){return t[0]}function yk(t){return t[1]}function gk(){this._=null}function mk(t){t.U=t.C=t.L=t.R=t.P=t.N=null}function vk(t,e){var n=e,r=e.R,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.R=r.L,n.R&&(n.R.U=n),r.L=n}function bk(t,e){var n=e,r=e.L,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.L=r.R,n.L&&(n.L.U=n),r.R=n}function _k(t){for(;t.L;)t=t.L;return t}gk.prototype={constructor:gk,insert:function(t,e){var n,r,i;if(t){if(e.P=t,e.N=t.N,t.N&&(t.N.P=e),t.N=e,t.R){for(t=t.R;t.L;)t=t.L;t.L=e}else t.R=e;n=t}else this._?(t=_k(this._),e.P=null,e.N=t,t.P=t.L=e,n=t):(e.P=e.N=null,this._=e,n=null);for(e.L=e.R=null,e.U=n,e.C=!0,t=e;n&&n.C;)n===(r=n.U).L?(i=r.R)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.R&&(vk(this,n),n=(t=n).U),n.C=!1,r.C=!0,bk(this,r)):(i=r.L)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.L&&(bk(this,n),n=(t=n).U),n.C=!1,r.C=!0,vk(this,r)),n=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var e,n,r,i=t.U,a=t.L,o=t.R;if(n=a?o?_k(o):a:o,i?i.L===t?i.L=n:i.R=n:this._=n,a&&o?(r=n.C,n.C=t.C,n.L=a,a.U=n,n!==o?(i=n.U,n.U=t.U,t=n.R,i.L=t,n.R=o,o.U=n):(n.U=i,i=n,t=n.R)):(r=t.C,t=n),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((e=i.R).C&&(e.C=!1,i.C=!0,vk(this,i),e=i.R),e.L&&e.L.C||e.R&&e.R.C){e.R&&e.R.C||(e.L.C=!1,e.C=!0,bk(this,e),e=i.R),e.C=i.C,i.C=e.R.C=!1,vk(this,i),t=this._;break}}else if((e=i.L).C&&(e.C=!1,i.C=!0,bk(this,i),e=i.L),e.L&&e.L.C||e.R&&e.R.C){e.L&&e.L.C||(e.R.C=!1,e.C=!0,vk(this,e),e=i.L),e.C=i.C,i.C=e.L.C=!1,bk(this,i),t=this._;break}e.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};const xk=gk;function wk(t,e,n,r){var i=[null,null],a=Wk.push(i)-1;return i.left=t,i.right=e,n&&Tk(i,t,e,n),r&&Tk(i,e,t,r),Hk[t.index].halfedges.push(a),Hk[e.index].halfedges.push(a),i}function kk(t,e,n){var r=[e,n];return r.left=t,r}function Tk(t,e,n,r){t[0]||t[1]?t.left===n?t[1]=r:t[0]=r:(t[0]=r,t.left=e,t.right=n)}function Ek(t,e,n,r,i){var a,o=t[0],s=t[1],c=o[0],u=o[1],l=0,h=1,f=s[0]-c,d=s[1]-u;if(a=e-c,f||!(a>0)){if(a/=f,f<0){if(a<l)return;a<h&&(h=a)}else if(f>0){if(a>h)return;a>l&&(l=a)}if(a=r-c,f||!(a<0)){if(a/=f,f<0){if(a>h)return;a>l&&(l=a)}else if(f>0){if(a<l)return;a<h&&(h=a)}if(a=n-u,d||!(a>0)){if(a/=d,d<0){if(a<l)return;a<h&&(h=a)}else if(d>0){if(a>h)return;a>l&&(l=a)}if(a=i-u,d||!(a<0)){if(a/=d,d<0){if(a>h)return;a>l&&(l=a)}else if(d>0){if(a<l)return;a<h&&(h=a)}return!(l>0||h<1)||(l>0&&(t[0]=[c+l*f,u+l*d]),h<1&&(t[1]=[c+h*f,u+h*d]),!0)}}}}}function Ck(t,e,n,r,i){var a=t[1];if(a)return!0;var o,s,c=t[0],u=t.left,l=t.right,h=u[0],f=u[1],d=l[0],p=l[1],y=(h+d)/2,g=(f+p)/2;if(p===f){if(y<e||y>=r)return;if(h>d){if(c){if(c[1]>=i)return}else c=[y,n];a=[y,i]}else{if(c){if(c[1]<n)return}else c=[y,i];a=[y,n]}}else if(s=g-(o=(h-d)/(p-f))*y,o<-1||o>1)if(h>d){if(c){if(c[1]>=i)return}else c=[(n-s)/o,n];a=[(i-s)/o,i]}else{if(c){if(c[1]<n)return}else c=[(i-s)/o,i];a=[(n-s)/o,n]}else if(f<p){if(c){if(c[0]>=r)return}else c=[e,o*e+s];a=[r,o*r+s]}else{if(c){if(c[0]<e)return}else c=[r,o*r+s];a=[e,o*e+s]}return t[0]=c,t[1]=a,!0}function Sk(t,e){var n=t.site,r=e.left,i=e.right;return n===i&&(i=r,r=n),i?Math.atan2(i[1]-r[1],i[0]-r[0]):(n===r?(r=e[1],i=e[0]):(r=e[0],i=e[1]),Math.atan2(r[0]-i[0],i[1]-r[1]))}function Ak(t,e){return e[+(e.left!==t.site)]}function Mk(t,e){return e[+(e.left===t.site)]}var Nk,Dk=[];function Ok(){mk(this),this.x=this.y=this.arc=this.site=this.cy=null}function Bk(t){var e=t.P,n=t.N;if(e&&n){var r=e.site,i=t.site,a=n.site;if(r!==a){var o=i[0],s=i[1],c=r[0]-o,u=r[1]-s,l=a[0]-o,h=a[1]-s,f=2*(c*h-u*l);if(!(f>=-Gk)){var d=c*c+u*u,p=l*l+h*h,y=(h*d-u*p)/f,g=(c*p-l*d)/f,m=Dk.pop()||new Ok;m.arc=t,m.site=i,m.x=y+o,m.y=(m.cy=g+s)+Math.sqrt(y*y+g*g),t.circle=m;for(var v=null,b=$k._;b;)if(m.y<b.y||m.y===b.y&&m.x<=b.x){if(!b.L){v=b.P;break}b=b.L}else{if(!b.R){v=b;break}b=b.R}$k.insert(v,m),v||(Nk=m)}}}}function Lk(t){var e=t.circle;e&&(e.P||(Nk=e.N),$k.remove(e),Dk.push(e),mk(e),t.circle=null)}var Ik=[];function Rk(){mk(this),this.edge=this.site=this.circle=null}function Fk(t){var e=Ik.pop()||new Rk;return e.site=t,e}function Pk(t){Lk(t),qk.remove(t),Ik.push(t),mk(t)}function jk(t){var e=t.circle,n=e.x,r=e.cy,i=[n,r],a=t.P,o=t.N,s=[t];Pk(t);for(var c=a;c.circle&&Math.abs(n-c.circle.x)<Vk&&Math.abs(r-c.circle.cy)<Vk;)a=c.P,s.unshift(c),Pk(c),c=a;s.unshift(c),Lk(c);for(var u=o;u.circle&&Math.abs(n-u.circle.x)<Vk&&Math.abs(r-u.circle.cy)<Vk;)o=u.N,s.push(u),Pk(u),u=o;s.push(u),Lk(u);var l,h=s.length;for(l=1;l<h;++l)u=s[l],c=s[l-1],Tk(u.edge,c.site,u.site,i);c=s[0],(u=s[h-1]).edge=wk(c.site,u.site,null,i),Bk(c),Bk(u)}function Yk(t){for(var e,n,r,i,a=t[0],o=t[1],s=qk._;s;)if((r=zk(s,o)-a)>Vk)s=s.L;else{if(!((i=a-Uk(s,o))>Vk)){r>-Vk?(e=s.P,n=s):i>-Vk?(e=s,n=s.N):e=n=s;break}if(!s.R){e=s;break}s=s.R}!function(t){Hk[t.index]={site:t,halfedges:[]}}(t);var c=Fk(t);if(qk.insert(e,c),e||n){if(e===n)return Lk(e),n=Fk(e.site),qk.insert(c,n),c.edge=n.edge=wk(e.site,c.site),Bk(e),void Bk(n);if(n){Lk(e),Lk(n);var u=e.site,l=u[0],h=u[1],f=t[0]-l,d=t[1]-h,p=n.site,y=p[0]-l,g=p[1]-h,m=2*(f*g-d*y),v=f*f+d*d,b=y*y+g*g,_=[(g*v-d*b)/m+l,(f*b-y*v)/m+h];Tk(n.edge,u,p,_),c.edge=wk(u,t,null,_),n.edge=wk(t,p,null,_),Bk(e),Bk(n)}else c.edge=wk(e.site,c.site)}}function zk(t,e){var n=t.site,r=n[0],i=n[1],a=i-e;if(!a)return r;var o=t.P;if(!o)return-1/0;var s=(n=o.site)[0],c=n[1],u=c-e;if(!u)return s;var l=s-r,h=1/a-1/u,f=l/u;return h?(-f+Math.sqrt(f*f-2*h*(l*l/(-2*u)-c+u/2+i-a/2)))/h+r:(r+s)/2}function Uk(t,e){var n=t.N;if(n)return zk(n,e);var r=t.site;return r[1]===e?r[0]:1/0}var qk,Hk,$k,Wk,Vk=1e-6,Gk=1e-12;function Xk(t,e,n){return(t[0]-n[0])*(e[1]-t[1])-(t[0]-e[0])*(n[1]-t[1])}function Zk(t,e){return e[1]-t[1]||e[0]-t[0]}function Qk(t,e){var n,r,i,a=t.sort(Zk).pop();for(Wk=[],Hk=new Array(t.length),qk=new xk,$k=new xk;;)if(i=Nk,a&&(!i||a[1]<i.y||a[1]===i.y&&a[0]<i.x))a[0]===n&&a[1]===r||(Yk(a),n=a[0],r=a[1]),a=t.pop();else{if(!i)break;jk(i.arc)}if(function(){for(var t,e,n,r,i=0,a=Hk.length;i<a;++i)if((t=Hk[i])&&(r=(e=t.halfedges).length)){var o=new Array(r),s=new Array(r);for(n=0;n<r;++n)o[n]=n,s[n]=Sk(t,Wk[e[n]]);for(o.sort((function(t,e){return s[e]-s[t]})),n=0;n<r;++n)s[n]=e[o[n]];for(n=0;n<r;++n)e[n]=s[n]}}(),e){var o=+e[0][0],s=+e[0][1],c=+e[1][0],u=+e[1][1];!function(t,e,n,r){for(var i,a=Wk.length;a--;)Ck(i=Wk[a],t,e,n,r)&&Ek(i,t,e,n,r)&&(Math.abs(i[0][0]-i[1][0])>Vk||Math.abs(i[0][1]-i[1][1])>Vk)||delete Wk[a]}(o,s,c,u),function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,y,g=Hk.length,m=!0;for(i=0;i<g;++i)if(a=Hk[i]){for(o=a.site,s=(c=a.halfedges).length;s--;)Wk[c[s]]||c.splice(s,1);for(s=0,u=c.length;s<u;)p=(d=Mk(a,Wk[c[s]]))[0],y=d[1],h=(l=Ak(a,Wk[c[++s%u]]))[0],f=l[1],(Math.abs(p-h)>Vk||Math.abs(y-f)>Vk)&&(c.splice(s,0,Wk.push(kk(o,d,Math.abs(p-t)<Vk&&r-y>Vk?[t,Math.abs(h-t)<Vk?f:r]:Math.abs(y-r)<Vk&&n-p>Vk?[Math.abs(f-r)<Vk?h:n,r]:Math.abs(p-n)<Vk&&y-e>Vk?[n,Math.abs(h-n)<Vk?f:e]:Math.abs(y-e)<Vk&&p-t>Vk?[Math.abs(f-e)<Vk?h:t,e]:null))-1),++u);u&&(m=!1)}if(m){var v,b,_,x=1/0;for(i=0,m=null;i<g;++i)(a=Hk[i])&&(_=(v=(o=a.site)[0]-t)*v+(b=o[1]-e)*b)<x&&(x=_,m=a);if(m){var w=[t,e],k=[t,r],T=[n,r],E=[n,e];m.halfedges.push(Wk.push(kk(o=m.site,w,k))-1,Wk.push(kk(o,k,T))-1,Wk.push(kk(o,T,E))-1,Wk.push(kk(o,E,w))-1)}}for(i=0;i<g;++i)(a=Hk[i])&&(a.halfedges.length||delete Hk[i])}(o,s,c,u)}this.edges=Wk,this.cells=Hk,qk=$k=Wk=Hk=null}function Kk(){var t=pk,e=yk,n=null;function r(r){return new Qk(r.map((function(n,i){var a=[Math.round(t(n,i,r)/Vk)*Vk,Math.round(e(n,i,r)/Vk)*Vk];return a.index=i,a.data=n,a})),n)}return r.polygons=function(t){return r(t).polygons()},r.links=function(t){return r(t).links()},r.triangles=function(t){return r(t).triangles()},r.x=function(e){return arguments.length?(t="function"==typeof e?e:dk(+e),r):t},r.y=function(t){return arguments.length?(e="function"==typeof t?t:dk(+t),r):e},r.extent=function(t){return arguments.length?(n=null==t?null:[[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]],r):n&&[[n[0][0],n[0][1]],[n[1][0],n[1][1]]]},r.size=function(t){return arguments.length?(n=null==t?null:[[0,0],[+t[0],+t[1]]],r):n&&[n[1][0]-n[0][0],n[1][1]-n[0][1]]},r}function Jk(t){return function(){return t}}function tT(t,e,n){this.target=t,this.type=e,this.transform=n}function eT(t,e,n){this.k=t,this.x=e,this.y=n}Qk.prototype={constructor:Qk,polygons:function(){var t=this.edges;return this.cells.map((function(e){var n=e.halfedges.map((function(n){return Ak(e,t[n])}));return n.data=e.site.data,n}))},triangles:function(){var t=[],e=this.edges;return this.cells.forEach((function(n,r){if(a=(i=n.halfedges).length)for(var i,a,o,s=n.site,c=-1,u=e[i[a-1]],l=u.left===s?u.right:u.left;++c<a;)o=l,l=(u=e[i[c]]).left===s?u.right:u.left,o&&l&&r<o.index&&r<l.index&&Xk(s,o,l)<0&&t.push([s.data,o.data,l.data])})),t},links:function(){return this.edges.filter((function(t){return t.right})).map((function(t){return{source:t.left.data,target:t.right.data}}))},find:function(t,e,n){for(var r,i,a=this,o=a._found||0,s=a.cells.length;!(i=a.cells[o]);)if(++o>=s)return null;var c=t-i.site[0],u=e-i.site[1],l=c*c+u*u;do{i=a.cells[r=o],o=null,i.halfedges.forEach((function(n){var r=a.edges[n],s=r.left;if(s!==i.site&&s||(s=r.right)){var c=t-s[0],u=e-s[1],h=c*c+u*u;h<l&&(l=h,o=s.index)}}))}while(null!==o);return a._found=r,null==n||l<=n*n?i.site:null}},eT.prototype={constructor:eT,scale:function(t){return 1===t?this:new eT(this.k*t,this.x,this.y)},translate:function(t,e){return 0===t&0===e?this:new eT(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var nT=new eT(1,0,0);function rT(t){for(;!t.__zoom;)if(!(t=t.parentNode))return nT;return t.__zoom}function iT(){le.stopImmediatePropagation()}function aT(){le.preventDefault(),le.stopImmediatePropagation()}function oT(){return!le.ctrlKey&&!le.button}function sT(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function cT(){return this.__zoom||nT}function uT(){return-le.deltaY*(1===le.deltaMode?.05:le.deltaMode?1:.002)}function lT(){return navigator.maxTouchPoints||"ontouchstart"in this}function hT(t,e,n){var r=t.invertX(e[0][0])-n[0][0],i=t.invertX(e[1][0])-n[1][0],a=t.invertY(e[0][1])-n[0][1],o=t.invertY(e[1][1])-n[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),o>a?(a+o)/2:Math.min(0,a)||Math.max(0,o))}function fT(){var t,e,n=oT,r=sT,i=hT,a=uT,o=lT,s=[0,1/0],c=[[-1/0,-1/0],[1/0,1/0]],u=250,l=Op,h=ft("start","zoom","end"),f=500,d=0;function p(t){t.property("__zoom",cT).on("wheel.zoom",x).on("mousedown.zoom",w).on("dblclick.zoom",k).filter(o).on("touchstart.zoom",T).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",C).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function y(t,e){return(e=Math.max(s[0],Math.min(s[1],e)))===t.k?t:new eT(e,t.x,t.y)}function g(t,e,n){var r=e[0]-n[0]*t.k,i=e[1]-n[1]*t.k;return r===t.x&&i===t.y?t:new eT(t.k,r,i)}function m(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function v(t,e,n){t.on("start.zoom",(function(){b(this,arguments).start()})).on("interrupt.zoom end.zoom",(function(){b(this,arguments).end()})).tween("zoom",(function(){var t=this,i=arguments,a=b(t,i),o=r.apply(t,i),s=null==n?m(o):"function"==typeof n?n.apply(t,i):n,c=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),u=t.__zoom,h="function"==typeof e?e.apply(t,i):e,f=l(u.invert(s).concat(c/u.k),h.invert(s).concat(c/h.k));return function(t){if(1===t)t=h;else{var e=f(t),n=c/e[2];t=new eT(n,s[0]-e[0]*n,s[1]-e[1]*n)}a.zoom(null,t)}}))}function b(t,e,n){return!n&&t.__zooming||new _(t,e)}function _(t,e){this.that=t,this.args=e,this.active=0,this.extent=r.apply(t,e),this.taps=0}function x(){if(n.apply(this,arguments)){var t=b(this,arguments),e=this.__zoom,r=Math.max(s[0],Math.min(s[1],e.k*Math.pow(2,a.apply(this,arguments)))),o=Bn(this);if(t.wheel)t.mouse[0][0]===o[0]&&t.mouse[0][1]===o[1]||(t.mouse[1]=e.invert(t.mouse[0]=o)),clearTimeout(t.wheel);else{if(e.k===r)return;t.mouse=[o,e.invert(o)],ar(this),t.start()}aT(),t.wheel=setTimeout(u,150),t.zoom("mouse",i(g(y(e,r),t.mouse[0],t.mouse[1]),t.extent,c))}function u(){t.wheel=null,t.end()}}function w(){if(!e&&n.apply(this,arguments)){var t=b(this,arguments,!0),r=Te(le.view).on("mousemove.zoom",u,!0).on("mouseup.zoom",l,!0),a=Bn(this),o=le.clientX,s=le.clientY;Se(le.view),iT(),t.mouse=[a,this.__zoom.invert(a)],ar(this),t.start()}function u(){if(aT(),!t.moved){var e=le.clientX-o,n=le.clientY-s;t.moved=e*e+n*n>d}t.zoom("mouse",i(g(t.that.__zoom,t.mouse[0]=Bn(t.that),t.mouse[1]),t.extent,c))}function l(){r.on("mousemove.zoom mouseup.zoom",null),Ae(le.view,t.moved),aT(),t.end()}}function k(){if(n.apply(this,arguments)){var t=this.__zoom,e=Bn(this),a=t.invert(e),o=t.k*(le.shiftKey?.5:2),s=i(g(y(t,o),e,a),r.apply(this,arguments),c);aT(),u>0?Te(this).transition().duration(u).call(v,s,e):Te(this).call(p.transform,s)}}function T(){if(n.apply(this,arguments)){var e,r,i,a,o=le.touches,s=o.length,c=b(this,arguments,le.changedTouches.length===s);for(iT(),r=0;r<s;++r)a=[a=On(this,o,(i=o[r]).identifier),this.__zoom.invert(a),i.identifier],c.touch0?c.touch1||c.touch0[2]===a[2]||(c.touch1=a,c.taps=0):(c.touch0=a,e=!0,c.taps=1+!!t);t&&(t=clearTimeout(t)),e&&(c.taps<2&&(t=setTimeout((function(){t=null}),f)),ar(this),c.start())}}function E(){if(this.__zooming){var e,n,r,a,o=b(this,arguments),s=le.changedTouches,u=s.length;for(aT(),t&&(t=clearTimeout(t)),o.taps=0,e=0;e<u;++e)r=On(this,s,(n=s[e]).identifier),o.touch0&&o.touch0[2]===n.identifier?o.touch0[0]=r:o.touch1&&o.touch1[2]===n.identifier&&(o.touch1[0]=r);if(n=o.that.__zoom,o.touch1){var l=o.touch0[0],h=o.touch0[1],f=o.touch1[0],d=o.touch1[1],p=(p=f[0]-l[0])*p+(p=f[1]-l[1])*p,m=(m=d[0]-h[0])*m+(m=d[1]-h[1])*m;n=y(n,Math.sqrt(p/m)),r=[(l[0]+f[0])/2,(l[1]+f[1])/2],a=[(h[0]+d[0])/2,(h[1]+d[1])/2]}else{if(!o.touch0)return;r=o.touch0[0],a=o.touch0[1]}o.zoom("touch",i(g(n,r,a),o.extent,c))}}function C(){if(this.__zooming){var t,n,r=b(this,arguments),i=le.changedTouches,a=i.length;for(iT(),e&&clearTimeout(e),e=setTimeout((function(){e=null}),f),t=0;t<a;++t)n=i[t],r.touch0&&r.touch0[2]===n.identifier?delete r.touch0:r.touch1&&r.touch1[2]===n.identifier&&delete r.touch1;if(r.touch1&&!r.touch0&&(r.touch0=r.touch1,delete r.touch1),r.touch0)r.touch0[1]=this.__zoom.invert(r.touch0[0]);else if(r.end(),2===r.taps){var o=Te(this).on("dblclick.zoom");o&&o.apply(this,arguments)}}}return p.transform=function(t,e,n){var r=t.selection?t.selection():t;r.property("__zoom",cT),t!==r?v(t,e,n):r.interrupt().each((function(){b(this,arguments).start().zoom(null,"function"==typeof e?e.apply(this,arguments):e).end()}))},p.scaleBy=function(t,e,n){p.scaleTo(t,(function(){var t=this.__zoom.k,n="function"==typeof e?e.apply(this,arguments):e;return t*n}),n)},p.scaleTo=function(t,e,n){p.transform(t,(function(){var t=r.apply(this,arguments),a=this.__zoom,o=null==n?m(t):"function"==typeof n?n.apply(this,arguments):n,s=a.invert(o),u="function"==typeof e?e.apply(this,arguments):e;return i(g(y(a,u),o,s),t,c)}),n)},p.translateBy=function(t,e,n){p.transform(t,(function(){return i(this.__zoom.translate("function"==typeof e?e.apply(this,arguments):e,"function"==typeof n?n.apply(this,arguments):n),r.apply(this,arguments),c)}))},p.translateTo=function(t,e,n,a){p.transform(t,(function(){var t=r.apply(this,arguments),o=this.__zoom,s=null==a?m(t):"function"==typeof a?a.apply(this,arguments):a;return i(nT.translate(s[0],s[1]).scale(o.k).translate("function"==typeof e?-e.apply(this,arguments):-e,"function"==typeof n?-n.apply(this,arguments):-n),t,c)}),a)},_.prototype={start:function(){return 1==++this.active&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(t,e){return this.mouse&&"mouse"!==t&&(this.mouse[1]=e.invert(this.mouse[0])),this.touch0&&"touch"!==t&&(this.touch0[1]=e.invert(this.touch0[0])),this.touch1&&"touch"!==t&&(this.touch1[1]=e.invert(this.touch1[0])),this.that.__zoom=e,this.emit("zoom"),this},end:function(){return 0==--this.active&&(delete this.that.__zooming,this.emit("end")),this},emit:function(t){ge(new tT(p,t,this.that.__zoom),h.apply,h,[t,this.that,this.args])}},p.wheelDelta=function(t){return arguments.length?(a="function"==typeof t?t:Jk(+t),p):a},p.filter=function(t){return arguments.length?(n="function"==typeof t?t:Jk(!!t),p):n},p.touchable=function(t){return arguments.length?(o="function"==typeof t?t:Jk(!!t),p):o},p.extent=function(t){return arguments.length?(r="function"==typeof t?t:Jk([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),p):r},p.scaleExtent=function(t){return arguments.length?(s[0]=+t[0],s[1]=+t[1],p):[s[0],s[1]]},p.translateExtent=function(t){return arguments.length?(c[0][0]=+t[0][0],c[1][0]=+t[1][0],c[0][1]=+t[0][1],c[1][1]=+t[1][1],p):[[c[0][0],c[0][1]],[c[1][0],c[1][1]]]},p.constrain=function(t){return arguments.length?(i=t,p):i},p.duration=function(t){return arguments.length?(u=+t,p):u},p.interpolate=function(t){return arguments.length?(l=t,p):l},p.on=function(){var t=h.on.apply(h,arguments);return t===h?p:t},p.clickDistance=function(t){return arguments.length?(d=(t=+t)*t,p):Math.sqrt(d)},p}rT.prototype=eT.prototype},681:(t,e,n)=>{t.exports={graphlib:n(574),layout:n(8123),debug:n(7570),util:{time:n(1138).time,notime:n(1138).notime},version:n(8177)}},1207:(t,e,n)=>{"use strict";var r=n(8436),i=n(4079);t.exports={run:function(t){var e="greedy"===t.graph().acyclicer?i(t,function(t){return function(e){return t.edge(e).weight}}(t)):function(t){var e=[],n={},i={};return r.forEach(t.nodes(),(function a(o){r.has(i,o)||(i[o]=!0,n[o]=!0,r.forEach(t.outEdges(o),(function(t){r.has(n,t.w)?e.push(t):a(t.w)})),delete n[o])})),e}(t);r.forEach(e,(function(e){var n=t.edge(e);t.removeEdge(e),n.forwardName=e.name,n.reversed=!0,t.setEdge(e.w,e.v,n,r.uniqueId("rev"))}))},undo:function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.reversed){t.removeEdge(e);var r=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,r)}}))}}},1133:(t,e,n)=>{var r=n(8436),i=n(1138);function a(t,e,n,r,a,o){var s={width:0,height:0,rank:o,borderType:e},c=a[e][o-1],u=i.addDummyNode(t,"border",s,n);a[e][o]=u,t.setParent(u,r),c&&t.setEdge(c,u,{weight:1})}t.exports=function(t){r.forEach(t.children(),(function e(n){var i=t.children(n),o=t.node(n);if(i.length&&r.forEach(i,e),r.has(o,"minRank")){o.borderLeft=[],o.borderRight=[];for(var s=o.minRank,c=o.maxRank+1;s<c;++s)a(t,"borderLeft","_bl",n,o,s),a(t,"borderRight","_br",n,o,s)}}))}},3258:(t,e,n)=>{"use strict";var r=n(8436);function i(t){r.forEach(t.nodes(),(function(e){a(t.node(e))})),r.forEach(t.edges(),(function(e){a(t.edge(e))}))}function a(t){var e=t.width;t.width=t.height,t.height=e}function o(t){t.y=-t.y}function s(t){var e=t.x;t.x=t.y,t.y=e}t.exports={adjust:function(t){var e=t.graph().rankdir.toLowerCase();"lr"!==e&&"rl"!==e||i(t)},undo:function(t){var e=t.graph().rankdir.toLowerCase();"bt"!==e&&"rl"!==e||function(t){r.forEach(t.nodes(),(function(e){o(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,o),r.has(n,"y")&&o(n)}))}(t),"lr"!==e&&"rl"!==e||(function(t){r.forEach(t.nodes(),(function(e){s(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,s),r.has(n,"x")&&s(n)}))}(t),i(t))}}},7822:t=>{function e(){var t={};t._next=t._prev=t,this._sentinel=t}function n(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function r(t,e){if("_next"!==t&&"_prev"!==t)return e}t.exports=e,e.prototype.dequeue=function(){var t=this._sentinel,e=t._prev;if(e!==t)return n(e),e},e.prototype.enqueue=function(t){var e=this._sentinel;t._prev&&t._next&&n(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e},e.prototype.toString=function(){for(var t=[],e=this._sentinel,n=e._prev;n!==e;)t.push(JSON.stringify(n,r)),n=n._prev;return"["+t.join(", ")+"]"}},7570:(t,e,n)=>{var r=n(8436),i=n(1138),a=n(574).Graph;t.exports={debugOrdering:function(t){var e=i.buildLayerMatrix(t),n=new a({compound:!0,multigraph:!0}).setGraph({});return r.forEach(t.nodes(),(function(e){n.setNode(e,{label:e}),n.setParent(e,"layer"+t.node(e).rank)})),r.forEach(t.edges(),(function(t){n.setEdge(t.v,t.w,{},t.name)})),r.forEach(e,(function(t,e){var i="layer"+e;n.setNode(i,{rank:"same"}),r.reduce(t,(function(t,e){return n.setEdge(t,e,{style:"invis"}),e}))})),n}}},574:(t,e,n)=>{var r;try{r=n(8282)}catch(t){}r||(r=window.graphlib),t.exports=r},4079:(t,e,n)=>{var r=n(8436),i=n(574).Graph,a=n(7822);t.exports=function(t,e){if(t.nodeCount()<=1)return[];var n=function(t,e){var n=new i,o=0,s=0;r.forEach(t.nodes(),(function(t){n.setNode(t,{v:t,in:0,out:0})})),r.forEach(t.edges(),(function(t){var r=n.edge(t.v,t.w)||0,i=e(t),a=r+i;n.setEdge(t.v,t.w,a),s=Math.max(s,n.node(t.v).out+=i),o=Math.max(o,n.node(t.w).in+=i)}));var u=r.range(s+o+3).map((function(){return new a})),l=o+1;return r.forEach(n.nodes(),(function(t){c(u,l,n.node(t))})),{graph:n,buckets:u,zeroIdx:l}}(t,e||o),u=function(t,e,n){for(var r,i=[],a=e[e.length-1],o=e[0];t.nodeCount();){for(;r=o.dequeue();)s(t,e,n,r);for(;r=a.dequeue();)s(t,e,n,r);if(t.nodeCount())for(var c=e.length-2;c>0;--c)if(r=e[c].dequeue()){i=i.concat(s(t,e,n,r,!0));break}}return i}(n.graph,n.buckets,n.zeroIdx);return r.flatten(r.map(u,(function(e){return t.outEdges(e.v,e.w)})),!0)};var o=r.constant(1);function s(t,e,n,i,a){var o=a?[]:void 0;return r.forEach(t.inEdges(i.v),(function(r){var i=t.edge(r),s=t.node(r.v);a&&o.push({v:r.v,w:r.w}),s.out-=i,c(e,n,s)})),r.forEach(t.outEdges(i.v),(function(r){var i=t.edge(r),a=r.w,o=t.node(a);o.in-=i,c(e,n,o)})),t.removeNode(i.v),o}function c(t,e,n){n.out?n.in?t[n.out-n.in+e].enqueue(n):t[t.length-1].enqueue(n):t[0].enqueue(n)}},8123:(t,e,n)=>{"use strict";var r=n(8436),i=n(1207),a=n(5995),o=n(8093),s=n(1138).normalizeRanks,c=n(4219),u=n(1138).removeEmptyRanks,l=n(2981),h=n(1133),f=n(3258),d=n(3408),p=n(7873),y=n(1138),g=n(574).Graph;t.exports=function(t,e){var n=e&&e.debugTiming?y.time:y.notime;n("layout",(function(){var e=n("  buildLayoutGraph",(function(){return function(t){var e=new g({multigraph:!0,compound:!0}),n=C(t.graph());return e.setGraph(r.merge({},v,E(n,m),r.pick(n,b))),r.forEach(t.nodes(),(function(n){var i=C(t.node(n));e.setNode(n,r.defaults(E(i,_),x)),e.setParent(n,t.parent(n))})),r.forEach(t.edges(),(function(n){var i=C(t.edge(n));e.setEdge(n,r.merge({},k,E(i,w),r.pick(i,T)))})),e}(t)}));n("  runLayout",(function(){!function(t,e){e("    makeSpaceForEdgeLabels",(function(){!function(t){var e=t.graph();e.ranksep/=2,r.forEach(t.edges(),(function(n){var r=t.edge(n);r.minlen*=2,"c"!==r.labelpos.toLowerCase()&&("TB"===e.rankdir||"BT"===e.rankdir?r.width+=r.labeloffset:r.height+=r.labeloffset)}))}(t)})),e("    removeSelfEdges",(function(){!function(t){r.forEach(t.edges(),(function(e){if(e.v===e.w){var n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}}))}(t)})),e("    acyclic",(function(){i.run(t)})),e("    nestingGraph.run",(function(){l.run(t)})),e("    rank",(function(){o(y.asNonCompoundGraph(t))})),e("    injectEdgeLabelProxies",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.width&&n.height){var r=t.node(e.v),i={rank:(t.node(e.w).rank-r.rank)/2+r.rank,e};y.addDummyNode(t,"edge-proxy",i,"_ep")}}))}(t)})),e("    removeEmptyRanks",(function(){u(t)})),e("    nestingGraph.cleanup",(function(){l.cleanup(t)})),e("    normalizeRanks",(function(){s(t)})),e("    assignRankMinMax",(function(){!function(t){var e=0;r.forEach(t.nodes(),(function(n){var i=t.node(n);i.borderTop&&(i.minRank=t.node(i.borderTop).rank,i.maxRank=t.node(i.borderBottom).rank,e=r.max(e,i.maxRank))})),t.graph().maxRank=e}(t)})),e("    removeEdgeLabelProxies",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);"edge-proxy"===n.dummy&&(t.edge(n.e).labelRank=n.rank,t.removeNode(e))}))}(t)})),e("    normalize.run",(function(){a.run(t)})),e("    parentDummyChains",(function(){c(t)})),e("    addBorderSegments",(function(){h(t)})),e("    order",(function(){d(t)})),e("    insertSelfEdges",(function(){!function(t){var e=y.buildLayerMatrix(t);r.forEach(e,(function(e){var n=0;r.forEach(e,(function(e,i){var a=t.node(e);a.order=i+n,r.forEach(a.selfEdges,(function(e){y.addDummyNode(t,"selfedge",{width:e.label.width,height:e.label.height,rank:a.rank,order:i+ ++n,e:e.e,label:e.label},"_se")})),delete a.selfEdges}))}))}(t)})),e("    adjustCoordinateSystem",(function(){f.adjust(t)})),e("    position",(function(){p(t)})),e("    positionSelfEdges",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);if("selfedge"===n.dummy){var r=t.node(n.e.v),i=r.x+r.width/2,a=r.y,o=n.x-i,s=r.height/2;t.setEdge(n.e,n.label),t.removeNode(e),n.label.points=[{x:i+2*o/3,y:a-s},{x:i+5*o/6,y:a-s},{x:i+o,y:a},{x:i+5*o/6,y:a+s},{x:i+2*o/3,y:a+s}],n.label.x=n.x,n.label.y=n.y}}))}(t)})),e("    removeBorderNodes",(function(){!function(t){r.forEach(t.nodes(),(function(e){if(t.children(e).length){var n=t.node(e),i=t.node(n.borderTop),a=t.node(n.borderBottom),o=t.node(r.last(n.borderLeft)),s=t.node(r.last(n.borderRight));n.width=Math.abs(s.x-o.x),n.height=Math.abs(a.y-i.y),n.x=o.x+n.width/2,n.y=i.y+n.height/2}})),r.forEach(t.nodes(),(function(e){"border"===t.node(e).dummy&&t.removeNode(e)}))}(t)})),e("    normalize.undo",(function(){a.undo(t)})),e("    fixupEdgeLabelCoords",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(r.has(n,"x"))switch("l"!==n.labelpos&&"r"!==n.labelpos||(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset}}))}(t)})),e("    undoCoordinateSystem",(function(){f.undo(t)})),e("    translateGraph",(function(){!function(t){var e=Number.POSITIVE_INFINITY,n=0,i=Number.POSITIVE_INFINITY,a=0,o=t.graph(),s=o.marginx||0,c=o.marginy||0;function u(t){var r=t.x,o=t.y,s=t.width,c=t.height;e=Math.min(e,r-s/2),n=Math.max(n,r+s/2),i=Math.min(i,o-c/2),a=Math.max(a,o+c/2)}r.forEach(t.nodes(),(function(e){u(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.has(n,"x")&&u(n)})),e-=s,i-=c,r.forEach(t.nodes(),(function(n){var r=t.node(n);r.x-=e,r.y-=i})),r.forEach(t.edges(),(function(n){var a=t.edge(n);r.forEach(a.points,(function(t){t.x-=e,t.y-=i})),r.has(a,"x")&&(a.x-=e),r.has(a,"y")&&(a.y-=i)})),o.width=n-e+s,o.height=a-i+c}(t)})),e("    assignNodeIntersects",(function(){!function(t){r.forEach(t.edges(),(function(e){var n,r,i=t.edge(e),a=t.node(e.v),o=t.node(e.w);i.points?(n=i.points[0],r=i.points[i.points.length-1]):(i.points=[],n=o,r=a),i.points.unshift(y.intersectRect(a,n)),i.points.push(y.intersectRect(o,r))}))}(t)})),e("    reversePoints",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);n.reversed&&n.points.reverse()}))}(t)})),e("    acyclic.undo",(function(){i.undo(t)}))}(e,n)})),n("  updateInputGraph",(function(){!function(t,e){r.forEach(t.nodes(),(function(n){var r=t.node(n),i=e.node(n);r&&(r.x=i.x,r.y=i.y,e.children(n).length&&(r.width=i.width,r.height=i.height))})),r.forEach(t.edges(),(function(n){var i=t.edge(n),a=e.edge(n);i.points=a.points,r.has(a,"x")&&(i.x=a.x,i.y=a.y)})),t.graph().width=e.graph().width,t.graph().height=e.graph().height}(t,e)}))}))};var m=["nodesep","edgesep","ranksep","marginx","marginy"],v={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},b=["acyclicer","ranker","rankdir","align"],_=["width","height"],x={width:0,height:0},w=["minlen","weight","width","height","labeloffset"],k={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},T=["labelpos"];function E(t,e){return r.mapValues(r.pick(t,e),Number)}function C(t){var e={};return r.forEach(t,(function(t,n){e[n.toLowerCase()]=t})),e}},8436:(t,e,n)=>{var r;try{r={cloneDeep:n(361),constant:n(5703),defaults:n(1747),each:n(6073),filter:n(3105),find:n(3311),flatten:n(5564),forEach:n(4486),forIn:n(2620),has:n(8721),isUndefined:n(2353),last:n(928),map:n(5161),mapValues:n(6604),max:n(6162),merge:n(3857),min:n(3632),minBy:n(2762),now:n(7771),pick:n(9722),range:n(6026),reduce:n(4061),sortBy:n(9734),uniqueId:n(3955),values:n(2628),zipObject:n(7287)}}catch(t){}r||(r=window._),t.exports=r},2981:(t,e,n)=>{var r=n(8436),i=n(1138);function a(t,e,n,o,s,c,u){var l=t.children(u);if(l.length){var h=i.addBorderNode(t,"_bt"),f=i.addBorderNode(t,"_bb"),d=t.node(u);t.setParent(h,u),d.borderTop=h,t.setParent(f,u),d.borderBottom=f,r.forEach(l,(function(r){a(t,e,n,o,s,c,r);var i=t.node(r),l=i.borderTop?i.borderTop:r,d=i.borderBottom?i.borderBottom:r,p=i.borderTop?o:2*o,y=l!==d?1:s-c[u]+1;t.setEdge(h,l,{weight:p,minlen:y,nestingEdge:!0}),t.setEdge(d,f,{weight:p,minlen:y,nestingEdge:!0})})),t.parent(u)||t.setEdge(e,h,{weight:0,minlen:s+c[u]})}else u!==e&&t.setEdge(e,u,{weight:0,minlen:n})}t.exports={run:function(t){var e=i.addDummyNode(t,"root",{},"_root"),n=function(t){var e={};function n(i,a){var o=t.children(i);o&&o.length&&r.forEach(o,(function(t){n(t,a+1)})),e[i]=a}return r.forEach(t.children(),(function(t){n(t,1)})),e}(t),o=r.max(r.values(n))-1,s=2*o+1;t.graph().nestingRoot=e,r.forEach(t.edges(),(function(e){t.edge(e).minlen*=s}));var c=function(t){return r.reduce(t.edges(),(function(e,n){return e+t.edge(n).weight}),0)}(t)+1;r.forEach(t.children(),(function(r){a(t,e,s,c,o,n,r)})),t.graph().nodeRankFactor=s},cleanup:function(t){var e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,r.forEach(t.edges(),(function(e){t.edge(e).nestingEdge&&t.removeEdge(e)}))}}},5995:(t,e,n)=>{"use strict";var r=n(8436),i=n(1138);t.exports={run:function(t){t.graph().dummyChains=[],r.forEach(t.edges(),(function(e){!function(t,e){var n,r,a,o=e.v,s=t.node(o).rank,c=e.w,u=t.node(c).rank,l=e.name,h=t.edge(e),f=h.labelRank;if(u!==s+1){for(t.removeEdge(e),a=0,++s;s<u;++a,++s)h.points=[],r={width:0,height:0,edgeLabel:h,edgeObj:e,rank:s},n=i.addDummyNode(t,"edge",r,"_d"),s===f&&(r.width=h.width,r.height=h.height,r.dummy="edge-label",r.labelpos=h.labelpos),t.setEdge(o,n,{weight:h.weight},l),0===a&&t.graph().dummyChains.push(n),o=n;t.setEdge(o,c,{weight:h.weight},l)}}(t,e)}))},undo:function(t){r.forEach(t.graph().dummyChains,(function(e){var n,r=t.node(e),i=r.edgeLabel;for(t.setEdge(r.edgeObj,i);r.dummy;)n=t.successors(e)[0],t.removeNode(e),i.points.push({x:r.x,y:r.y}),"edge-label"===r.dummy&&(i.x=r.x,i.y=r.y,i.width=r.width,i.height=r.height),e=n,r=t.node(e)}))}}},5093:(t,e,n)=>{var r=n(8436);t.exports=function(t,e,n){var i,a={};r.forEach(n,(function(n){for(var r,o,s=t.parent(n);s;){if((r=t.parent(s))?(o=a[r],a[r]=s):(o=i,i=s),o&&o!==s)return void e.setEdge(o,s);s=r}}))}},5439:(t,e,n)=>{var r=n(8436);t.exports=function(t,e){return r.map(e,(function(e){var n=t.inEdges(e);if(n.length){var i=r.reduce(n,(function(e,n){var r=t.edge(n),i=t.node(n.v);return{sum:e.sum+r.weight*i.order,weight:e.weight+r.weight}}),{sum:0,weight:0});return{v:e,barycenter:i.sum/i.weight,weight:i.weight}}return{v:e}}))}},3128:(t,e,n)=>{var r=n(8436),i=n(574).Graph;t.exports=function(t,e,n){var a=function(t){for(var e;t.hasNode(e=r.uniqueId("_root")););return e}(t),o=new i({compound:!0}).setGraph({root:a}).setDefaultNodeLabel((function(e){return t.node(e)}));return r.forEach(t.nodes(),(function(i){var s=t.node(i),c=t.parent(i);(s.rank===e||s.minRank<=e&&e<=s.maxRank)&&(o.setNode(i),o.setParent(i,c||a),r.forEach(t[n](i),(function(e){var n=e.v===i?e.w:e.v,a=o.edge(n,i),s=r.isUndefined(a)?0:a.weight;o.setEdge(n,i,{weight:t.edge(e).weight+s})})),r.has(s,"minRank")&&o.setNode(i,{borderLeft:s.borderLeft[e],borderRight:s.borderRight[e]}))})),o}},6630:(t,e,n)=>{"use strict";var r=n(8436);function i(t,e,n){for(var i=r.zipObject(n,r.map(n,(function(t,e){return e}))),a=r.flatten(r.map(e,(function(e){return r.sortBy(r.map(t.outEdges(e),(function(e){return{pos:i[e.w],weight:t.edge(e).weight}})),"pos")})),!0),o=1;o<n.length;)o<<=1;var s=2*o-1;o-=1;var c=r.map(new Array(s),(function(){return 0})),u=0;return r.forEach(a.forEach((function(t){var e=t.pos+o;c[e]+=t.weight;for(var n=0;e>0;)e%2&&(n+=c[e+1]),c[e=e-1>>1]+=t.weight;u+=t.weight*n}))),u}t.exports=function(t,e){for(var n=0,r=1;r<e.length;++r)n+=i(t,e[r-1],e[r]);return n}},3408:(t,e,n)=>{"use strict";var r=n(8436),i=n(2588),a=n(6630),o=n(1026),s=n(3128),c=n(5093),u=n(574).Graph,l=n(1138);function h(t,e,n){return r.map(e,(function(e){return s(t,e,n)}))}function f(t,e){var n=new u;r.forEach(t,(function(t){var i=t.graph().root,a=o(t,i,n,e);r.forEach(a.vs,(function(e,n){t.node(e).order=n})),c(t,n,a.vs)}))}function d(t,e){r.forEach(e,(function(e){r.forEach(e,(function(e,n){t.node(e).order=n}))}))}t.exports=function(t){var e=l.maxRank(t),n=h(t,r.range(1,e+1),"inEdges"),o=h(t,r.range(e-1,-1,-1),"outEdges"),s=i(t);d(t,s);for(var c,u=Number.POSITIVE_INFINITY,p=0,y=0;y<4;++p,++y){f(p%2?n:o,p%4>=2),s=l.buildLayerMatrix(t);var g=a(t,s);g<u&&(y=0,c=r.cloneDeep(s),u=g)}d(t,c)}},2588:(t,e,n)=>{"use strict";var r=n(8436);t.exports=function(t){var e={},n=r.filter(t.nodes(),(function(e){return!t.children(e).length})),i=r.max(r.map(n,(function(e){return t.node(e).rank}))),a=r.map(r.range(i+1),(function(){return[]})),o=r.sortBy(n,(function(e){return t.node(e).rank}));return r.forEach(o,(function n(i){if(!r.has(e,i)){e[i]=!0;var o=t.node(i);a[o.rank].push(i),r.forEach(t.successors(i),n)}})),a}},9567:(t,e,n)=>{"use strict";var r=n(8436);t.exports=function(t,e){var n={};return r.forEach(t,(function(t,e){var i=n[t.v]={indegree:0,in:[],out:[],vs:[t.v],i:e};r.isUndefined(t.barycenter)||(i.barycenter=t.barycenter,i.weight=t.weight)})),r.forEach(e.edges(),(function(t){var e=n[t.v],i=n[t.w];r.isUndefined(e)||r.isUndefined(i)||(i.indegree++,e.out.push(n[t.w]))})),function(t){var e=[];function n(t){return function(e){var n,i,a,o;e.merged||(r.isUndefined(e.barycenter)||r.isUndefined(t.barycenter)||e.barycenter>=t.barycenter)&&(i=e,a=0,o=0,(n=t).weight&&(a+=n.barycenter*n.weight,o+=n.weight),i.weight&&(a+=i.barycenter*i.weight,o+=i.weight),n.vs=i.vs.concat(n.vs),n.barycenter=a/o,n.weight=o,n.i=Math.min(i.i,n.i),i.merged=!0)}}function i(e){return function(n){n.in.push(e),0==--n.indegree&&t.push(n)}}for(;t.length;){var a=t.pop();e.push(a),r.forEach(a.in.reverse(),n(a)),r.forEach(a.out,i(a))}return r.map(r.filter(e,(function(t){return!t.merged})),(function(t){return r.pick(t,["vs","i","barycenter","weight"])}))}(r.filter(n,(function(t){return!t.indegree})))}},1026:(t,e,n)=>{var r=n(8436),i=n(5439),a=n(9567),o=n(7304);t.exports=function t(e,n,s,c){var u=e.children(n),l=e.node(n),h=l?l.borderLeft:void 0,f=l?l.borderRight:void 0,d={};h&&(u=r.filter(u,(function(t){return t!==h&&t!==f})));var p=i(e,u);r.forEach(p,(function(n){if(e.children(n.v).length){var i=t(e,n.v,s,c);d[n.v]=i,r.has(i,"barycenter")&&(a=n,o=i,r.isUndefined(a.barycenter)?(a.barycenter=o.barycenter,a.weight=o.weight):(a.barycenter=(a.barycenter*a.weight+o.barycenter*o.weight)/(a.weight+o.weight),a.weight+=o.weight))}var a,o}));var y=a(p,s);!function(t,e){r.forEach(t,(function(t){t.vs=r.flatten(t.vs.map((function(t){return e[t]?e[t].vs:t})),!0)}))}(y,d);var g=o(y,c);if(h&&(g.vs=r.flatten([h,g.vs,f],!0),e.predecessors(h).length)){var m=e.node(e.predecessors(h)[0]),v=e.node(e.predecessors(f)[0]);r.has(g,"barycenter")||(g.barycenter=0,g.weight=0),g.barycenter=(g.barycenter*g.weight+m.order+v.order)/(g.weight+2),g.weight+=2}return g}},7304:(t,e,n)=>{var r=n(8436),i=n(1138);function a(t,e,n){for(var i;e.length&&(i=r.last(e)).i<=n;)e.pop(),t.push(i.vs),n++;return n}t.exports=function(t,e){var n,o=i.partition(t,(function(t){return r.has(t,"barycenter")})),s=o.lhs,c=r.sortBy(o.rhs,(function(t){return-t.i})),u=[],l=0,h=0,f=0;s.sort((n=!!e,function(t,e){return t.barycenter<e.barycenter?-1:t.barycenter>e.barycenter?1:n?e.i-t.i:t.i-e.i})),f=a(u,c,f),r.forEach(s,(function(t){f+=t.vs.length,u.push(t.vs),l+=t.barycenter*t.weight,h+=t.weight,f=a(u,c,f)}));var d={vs:r.flatten(u,!0)};return h&&(d.barycenter=l/h,d.weight=h),d}},4219:(t,e,n)=>{var r=n(8436);t.exports=function(t){var e=function(t){var e={},n=0;return r.forEach(t.children(),(function i(a){var o=n;r.forEach(t.children(a),i),e[a]={low:o,lim:n++}})),e}(t);r.forEach(t.graph().dummyChains,(function(n){for(var r=t.node(n),i=r.edgeObj,a=function(t,e,n,r){var i,a,o=[],s=[],c=Math.min(e[n].low,e[r].low),u=Math.max(e[n].lim,e[r].lim);i=n;do{i=t.parent(i),o.push(i)}while(i&&(e[i].low>c||u>e[i].lim));for(a=i,i=r;(i=t.parent(i))!==a;)s.push(i);return{path:o.concat(s.reverse()),lca:a}}(t,e,i.v,i.w),o=a.path,s=a.lca,c=0,u=o[c],l=!0;n!==i.w;){if(r=t.node(n),l){for(;(u=o[c])!==s&&t.node(u).maxRank<r.rank;)c++;u===s&&(l=!1)}if(!l){for(;c<o.length-1&&t.node(u=o[c+1]).minRank<=r.rank;)c++;u=o[c]}t.setParent(n,u),n=t.successors(n)[0]}}))}},3573:(t,e,n)=>{"use strict";var r=n(8436),i=n(574).Graph,a=n(1138);function o(t,e){var n={};return r.reduce(e,(function(e,i){var a=0,o=0,s=e.length,u=r.last(i);return r.forEach(i,(function(e,l){var h=function(t,e){if(t.node(e).dummy)return r.find(t.predecessors(e),(function(e){return t.node(e).dummy}))}(t,e),f=h?t.node(h).order:s;(h||e===u)&&(r.forEach(i.slice(o,l+1),(function(e){r.forEach(t.predecessors(e),(function(r){var i=t.node(r),o=i.order;!(o<a||f<o)||i.dummy&&t.node(e).dummy||c(n,r,e)}))})),o=l+1,a=f)})),i})),n}function s(t,e){var n={};function i(e,i,a,o,s){var u;r.forEach(r.range(i,a),(function(i){u=e[i],t.node(u).dummy&&r.forEach(t.predecessors(u),(function(e){var r=t.node(e);r.dummy&&(r.order<o||r.order>s)&&c(n,e,u)}))}))}return r.reduce(e,(function(e,n){var a,o=-1,s=0;return r.forEach(n,(function(r,c){if("border"===t.node(r).dummy){var u=t.predecessors(r);u.length&&(a=t.node(u[0]).order,i(n,s,c,o,a),s=c,o=a)}i(n,s,n.length,a,e.length)})),n})),n}function c(t,e,n){if(e>n){var r=e;e=n,n=r}var i=t[e];i||(t[e]=i={}),i[n]=!0}function u(t,e,n){if(e>n){var i=e;e=n,n=i}return r.has(t[e],n)}function l(t,e,n,i){var a={},o={},s={};return r.forEach(e,(function(t){r.forEach(t,(function(t,e){a[t]=t,o[t]=t,s[t]=e}))})),r.forEach(e,(function(t){var e=-1;r.forEach(t,(function(t){var c=i(t);if(c.length){c=r.sortBy(c,(function(t){return s[t]}));for(var l=(c.length-1)/2,h=Math.floor(l),f=Math.ceil(l);h<=f;++h){var d=c[h];o[t]===t&&e<s[d]&&!u(n,t,d)&&(o[d]=t,o[t]=a[t]=a[d],e=s[d])}}}))})),{root:a,align:o}}function h(t,e,n,a,o){var s={},c=function(t,e,n,a){var o=new i,s=t.graph(),c=function(t,e,n){return function(i,a,o){var s,c=i.node(a),u=i.node(o),l=0;if(l+=c.width/2,r.has(c,"labelpos"))switch(c.labelpos.toLowerCase()){case"l":s=-c.width/2;break;case"r":s=c.width/2}if(s&&(l+=n?s:-s),s=0,l+=(c.dummy?e:t)/2,l+=(u.dummy?e:t)/2,l+=u.width/2,r.has(u,"labelpos"))switch(u.labelpos.toLowerCase()){case"l":s=u.width/2;break;case"r":s=-u.width/2}return s&&(l+=n?s:-s),s=0,l}}(s.nodesep,s.edgesep,a);return r.forEach(e,(function(e){var i;r.forEach(e,(function(e){var r=n[e];if(o.setNode(r),i){var a=n[i],s=o.edge(a,r);o.setEdge(a,r,Math.max(c(t,e,i),s||0))}i=e}))})),o}(t,e,n,o),u=o?"borderLeft":"borderRight";function l(t,e){for(var n=c.nodes(),r=n.pop(),i={};r;)i[r]?t(r):(i[r]=!0,n.push(r),n=n.concat(e(r))),r=n.pop()}return l((function(t){s[t]=c.inEdges(t).reduce((function(t,e){return Math.max(t,s[e.v]+c.edge(e))}),0)}),c.predecessors.bind(c)),l((function(e){var n=c.outEdges(e).reduce((function(t,e){return Math.min(t,s[e.w]-c.edge(e))}),Number.POSITIVE_INFINITY),r=t.node(e);n!==Number.POSITIVE_INFINITY&&r.borderType!==u&&(s[e]=Math.max(s[e],n))}),c.successors.bind(c)),r.forEach(a,(function(t){s[t]=s[n[t]]})),s}function f(t,e){return r.minBy(r.values(e),(function(e){var n=Number.NEGATIVE_INFINITY,i=Number.POSITIVE_INFINITY;return r.forIn(e,(function(e,r){var a=function(t,e){return t.node(e).width}(t,r)/2;n=Math.max(e+a,n),i=Math.min(e-a,i)})),n-i}))}function d(t,e){var n=r.values(e),i=r.min(n),a=r.max(n);r.forEach(["u","d"],(function(n){r.forEach(["l","r"],(function(o){var s,c=n+o,u=t[c];if(u!==e){var l=r.values(u);(s="l"===o?i-r.min(l):a-r.max(l))&&(t[c]=r.mapValues(u,(function(t){return t+s})))}}))}))}function p(t,e){return r.mapValues(t.ul,(function(n,i){if(e)return t[e.toLowerCase()][i];var a=r.sortBy(r.map(t,i));return(a[1]+a[2])/2}))}t.exports={positionX:function(t){var e,n=a.buildLayerMatrix(t),i=r.merge(o(t,n),s(t,n)),c={};r.forEach(["u","d"],(function(a){e="u"===a?n:r.values(n).reverse(),r.forEach(["l","r"],(function(n){"r"===n&&(e=r.map(e,(function(t){return r.values(t).reverse()})));var o=("u"===a?t.predecessors:t.successors).bind(t),s=l(0,e,i,o),u=h(t,e,s.root,s.align,"r"===n);"r"===n&&(u=r.mapValues(u,(function(t){return-t}))),c[a+n]=u}))}));var u=f(t,c);return d(c,u),p(c,t.graph().align)},findType1Conflicts:o,findType2Conflicts:s,addConflict:c,hasConflict:u,verticalAlignment:l,horizontalCompaction:h,alignCoordinates:d,findSmallestWidthAlignment:f,balance:p}},7873:(t,e,n)=>{"use strict";var r=n(8436),i=n(1138),a=n(3573).positionX;t.exports=function(t){(function(t){var e=i.buildLayerMatrix(t),n=t.graph().ranksep,a=0;r.forEach(e,(function(e){var i=r.max(r.map(e,(function(e){return t.node(e).height})));r.forEach(e,(function(e){t.node(e).y=a+i/2})),a+=i+n}))})(t=i.asNonCompoundGraph(t)),r.forEach(a(t),(function(e,n){t.node(n).x=e}))}},300:(t,e,n)=>{"use strict";var r=n(8436),i=n(574).Graph,a=n(6681).slack;function o(t,e){return r.forEach(t.nodes(),(function n(i){r.forEach(e.nodeEdges(i),(function(r){var o=r.v,s=i===o?r.w:o;t.hasNode(s)||a(e,r)||(t.setNode(s,{}),t.setEdge(i,s,{}),n(s))}))})),t.nodeCount()}function s(t,e){return r.minBy(e.edges(),(function(n){if(t.hasNode(n.v)!==t.hasNode(n.w))return a(e,n)}))}function c(t,e,n){r.forEach(t.nodes(),(function(t){e.node(t).rank+=n}))}t.exports=function(t){var e,n,r=new i({directed:!1}),u=t.nodes()[0],l=t.nodeCount();for(r.setNode(u,{});o(r,t)<l;)e=s(r,t),n=r.hasNode(e.v)?a(t,e):-a(t,e),c(r,t,n);return r}},8093:(t,e,n)=>{"use strict";var r=n(6681).longestPath,i=n(300),a=n(2472);t.exports=function(t){switch(t.graph().ranker){case"network-simplex":default:!function(t){a(t)}(t);break;case"tight-tree":!function(t){r(t),i(t)}(t);break;case"longest-path":o(t)}};var o=r},2472:(t,e,n)=>{"use strict";var r=n(8436),i=n(300),a=n(6681).slack,o=n(6681).longestPath,s=n(574).alg.preorder,c=n(574).alg.postorder,u=n(1138).simplify;function l(t){t=u(t),o(t);var e,n=i(t);for(d(n),h(n,t);e=y(n);)m(n,t,e,g(n,t,e))}function h(t,e){var n=c(t,t.nodes());n=n.slice(0,n.length-1),r.forEach(n,(function(n){!function(t,e,n){var r=t.node(n).parent;t.edge(n,r).cutvalue=f(t,e,n)}(t,e,n)}))}function f(t,e,n){var i=t.node(n).parent,a=!0,o=e.edge(n,i),s=0;return o||(a=!1,o=e.edge(i,n)),s=o.weight,r.forEach(e.nodeEdges(n),(function(r){var o,c,u=r.v===n,l=u?r.w:r.v;if(l!==i){var h=u===a,f=e.edge(r).weight;if(s+=h?f:-f,o=n,c=l,t.hasEdge(o,c)){var d=t.edge(n,l).cutvalue;s+=h?-d:d}}})),s}function d(t,e){arguments.length<2&&(e=t.nodes()[0]),p(t,{},1,e)}function p(t,e,n,i,a){var o=n,s=t.node(i);return e[i]=!0,r.forEach(t.neighbors(i),(function(a){r.has(e,a)||(n=p(t,e,n,a,i))})),s.low=o,s.lim=n++,a?s.parent=a:delete s.parent,n}function y(t){return r.find(t.edges(),(function(e){return t.edge(e).cutvalue<0}))}function g(t,e,n){var i=n.v,o=n.w;e.hasEdge(i,o)||(i=n.w,o=n.v);var s=t.node(i),c=t.node(o),u=s,l=!1;s.lim>c.lim&&(u=c,l=!0);var h=r.filter(e.edges(),(function(e){return l===v(0,t.node(e.v),u)&&l!==v(0,t.node(e.w),u)}));return r.minBy(h,(function(t){return a(e,t)}))}function m(t,e,n,i){var a=n.v,o=n.w;t.removeEdge(a,o),t.setEdge(i.v,i.w,{}),d(t),h(t,e),function(t,e){var n=r.find(t.nodes(),(function(t){return!e.node(t).parent})),i=s(t,n);i=i.slice(1),r.forEach(i,(function(n){var r=t.node(n).parent,i=e.edge(n,r),a=!1;i||(i=e.edge(r,n),a=!0),e.node(n).rank=e.node(r).rank+(a?i.minlen:-i.minlen)}))}(t,e)}function v(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}t.exports=l,l.initLowLimValues=d,l.initCutValues=h,l.calcCutValue=f,l.leaveEdge=y,l.enterEdge=g,l.exchangeEdges=m},6681:(t,e,n)=>{"use strict";var r=n(8436);t.exports={longestPath:function(t){var e={};r.forEach(t.sources(),(function n(i){var a=t.node(i);if(r.has(e,i))return a.rank;e[i]=!0;var o=r.min(r.map(t.outEdges(i),(function(e){return n(e.w)-t.edge(e).minlen})));return o!==Number.POSITIVE_INFINITY&&null!=o||(o=0),a.rank=o}))},slack:function(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}}},1138:(t,e,n)=>{"use strict";var r=n(8436),i=n(574).Graph;function a(t,e,n,i){var a;do{a=r.uniqueId(i)}while(t.hasNode(a));return n.dummy=e,t.setNode(a,n),a}function o(t){return r.max(r.map(t.nodes(),(function(e){var n=t.node(e).rank;if(!r.isUndefined(n))return n})))}t.exports={addDummyNode:a,simplify:function(t){var e=(new i).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){var r=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:r.weight+i.weight,minlen:Math.max(r.minlen,i.minlen)})})),e},asNonCompoundGraph:function(t){var e=new i({multigraph:t.isMultigraph()}).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){t.children(n).length||e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){e.setEdge(n,t.edge(n))})),e},successorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.outEdges(e),(function(e){n[e.w]=(n[e.w]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},predecessorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.inEdges(e),(function(e){n[e.v]=(n[e.v]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},intersectRect:function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;if(!o&&!s)throw new Error("Not possible to find intersection inside of the rectangle");return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=u*o/s,r=u):(o<0&&(c=-c),n=c,r=c*s/o),{x:i+n,y:a+r}},buildLayerMatrix:function(t){var e=r.map(r.range(o(t)+1),(function(){return[]}));return r.forEach(t.nodes(),(function(n){var i=t.node(n),a=i.rank;r.isUndefined(a)||(e[a][i.order]=n)})),e},normalizeRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank})));r.forEach(t.nodes(),(function(n){var i=t.node(n);r.has(i,"rank")&&(i.rank-=e)}))},removeEmptyRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank}))),n=[];r.forEach(t.nodes(),(function(r){var i=t.node(r).rank-e;n[i]||(n[i]=[]),n[i].push(r)}));var i=0,a=t.graph().nodeRankFactor;r.forEach(n,(function(e,n){r.isUndefined(e)&&n%a!=0?--i:i&&r.forEach(e,(function(e){t.node(e).rank+=i}))}))},addBorderNode:function(t,e,n,r){var i={width:0,height:0};return arguments.length>=4&&(i.rank=n,i.order=r),a(t,"border",i,e)},maxRank:o,partition:function(t,e){var n={lhs:[],rhs:[]};return r.forEach(t,(function(t){e(t)?n.lhs.push(t):n.rhs.push(t)})),n},time:function(t,e){var n=r.now();try{return e()}finally{console.log(t+" time: "+(r.now()-n)+"ms")}},notime:function(t,e){return e()}}},8177:t=>{t.exports="0.8.5"},7856:function(t){t.exports=function(){"use strict";var t=Object.hasOwnProperty,e=Object.setPrototypeOf,n=Object.isFrozen,r=Object.getPrototypeOf,i=Object.getOwnPropertyDescriptor,a=Object.freeze,o=Object.seal,s=Object.create,c="undefined"!=typeof Reflect&&Reflect,u=c.apply,l=c.construct;u||(u=function(t,e,n){return t.apply(e,n)}),a||(a=function(t){return t}),o||(o=function(t){return t}),l||(l=function(t,e){return new(Function.prototype.bind.apply(t,[null].concat(function(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}(e))))});var h,f=w(Array.prototype.forEach),d=w(Array.prototype.pop),p=w(Array.prototype.push),y=w(String.prototype.toLowerCase),g=w(String.prototype.match),m=w(String.prototype.replace),v=w(String.prototype.indexOf),b=w(String.prototype.trim),_=w(RegExp.prototype.test),x=(h=TypeError,function(){for(var t=arguments.length,e=Array(t),n=0;n<t;n++)e[n]=arguments[n];return l(h,e)});function w(t){return function(e){for(var n=arguments.length,r=Array(n>1?n-1:0),i=1;i<n;i++)r[i-1]=arguments[i];return u(t,e,r)}}function k(t,r){e&&e(t,null);for(var i=r.length;i--;){var a=r[i];if("string"==typeof a){var o=y(a);o!==a&&(n(r)||(r[i]=o),a=o)}t[a]=!0}return t}function T(e){var n=s(null),r=void 0;for(r in e)u(t,e,[r])&&(n[r]=e[r]);return n}function E(t,e){for(;null!==t;){var n=i(t,e);if(n){if(n.get)return w(n.get);if("function"==typeof n.value)return w(n.value)}t=r(t)}return function(t){return console.warn("fallback value for",t),null}}var C=a(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),S=a(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),A=a(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),M=a(["animate","color-profile","cursor","discard","fedropshadow","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),N=a(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"]),D=a(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),O=a(["#text"]),B=a(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","xmlns","slot"]),L=a(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),I=a(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),R=a(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),F=o(/\{\{[\s\S]*|[\s\S]*\}\}/gm),P=o(/<%[\s\S]*|[\s\S]*%>/gm),j=o(/^data-[\-\w.\u00B7-\uFFFF]/),Y=o(/^aria-[\-\w]+$/),z=o(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),U=o(/^(?:\w+script|data):/i),q=o(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),H="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};function $(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}var W=function(){return"undefined"==typeof window?null:window},V=function(t,e){if("object"!==(void 0===t?"undefined":H(t))||"function"!=typeof t.createPolicy)return null;var n=null,r="data-tt-policy-suffix";e.currentScript&&e.currentScript.hasAttribute(r)&&(n=e.currentScript.getAttribute(r));var i="dompurify"+(n?"#"+n:"");try{return t.createPolicy(i,{createHTML:function(t){return t}})}catch(t){return console.warn("TrustedTypes policy "+i+" could not be created."),null}};return function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:W(),n=function(e){return t(e)};if(n.version="2.3.4",n.removed=[],!e||!e.document||9!==e.document.nodeType)return n.isSupported=!1,n;var r=e.document,i=e.document,o=e.DocumentFragment,s=e.HTMLTemplateElement,c=e.Node,u=e.Element,l=e.NodeFilter,h=e.NamedNodeMap,w=void 0===h?e.NamedNodeMap||e.MozNamedAttrMap:h,G=e.HTMLFormElement,X=e.DOMParser,Z=e.trustedTypes,Q=u.prototype,K=E(Q,"cloneNode"),J=E(Q,"nextSibling"),tt=E(Q,"childNodes"),et=E(Q,"parentNode");if("function"==typeof s){var nt=i.createElement("template");nt.content&&nt.content.ownerDocument&&(i=nt.content.ownerDocument)}var rt=V(Z,r),it=rt&&Rt?rt.createHTML(""):"",at=i,ot=at.implementation,st=at.createNodeIterator,ct=at.createDocumentFragment,ut=at.getElementsByTagName,lt=r.importNode,ht={};try{ht=T(i).documentMode?i.documentMode:{}}catch(t){}var ft={};n.isSupported="function"==typeof et&&ot&&void 0!==ot.createHTMLDocument&&9!==ht;var dt=F,pt=P,yt=j,gt=Y,mt=U,vt=q,bt=z,_t=null,xt=k({},[].concat($(C),$(S),$(A),$(N),$(O))),wt=null,kt=k({},[].concat($(B),$(L),$(I),$(R))),Tt=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Et=null,Ct=null,St=!0,At=!0,Mt=!1,Nt=!1,Dt=!1,Ot=!1,Bt=!1,Lt=!1,It=!1,Rt=!1,Ft=!0,Pt=!0,jt=!1,Yt={},zt=null,Ut=k({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),qt=null,Ht=k({},["audio","video","img","source","image","track"]),$t=null,Wt=k({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Vt="http://www.w3.org/1998/Math/MathML",Gt="http://www.w3.org/2000/svg",Xt="http://www.w3.org/1999/xhtml",Zt=Xt,Qt=!1,Kt=void 0,Jt=["application/xhtml+xml","text/html"],te="text/html",ee=void 0,ne=null,re=i.createElement("form"),ie=function(t){return t instanceof RegExp||t instanceof Function},ae=function(t){ne&&ne===t||(t&&"object"===(void 0===t?"undefined":H(t))||(t={}),t=T(t),_t="ALLOWED_TAGS"in t?k({},t.ALLOWED_TAGS):xt,wt="ALLOWED_ATTR"in t?k({},t.ALLOWED_ATTR):kt,$t="ADD_URI_SAFE_ATTR"in t?k(T(Wt),t.ADD_URI_SAFE_ATTR):Wt,qt="ADD_DATA_URI_TAGS"in t?k(T(Ht),t.ADD_DATA_URI_TAGS):Ht,zt="FORBID_CONTENTS"in t?k({},t.FORBID_CONTENTS):Ut,Et="FORBID_TAGS"in t?k({},t.FORBID_TAGS):{},Ct="FORBID_ATTR"in t?k({},t.FORBID_ATTR):{},Yt="USE_PROFILES"in t&&t.USE_PROFILES,St=!1!==t.ALLOW_ARIA_ATTR,At=!1!==t.ALLOW_DATA_ATTR,Mt=t.ALLOW_UNKNOWN_PROTOCOLS||!1,Nt=t.SAFE_FOR_TEMPLATES||!1,Dt=t.WHOLE_DOCUMENT||!1,Lt=t.RETURN_DOM||!1,It=t.RETURN_DOM_FRAGMENT||!1,Rt=t.RETURN_TRUSTED_TYPE||!1,Bt=t.FORCE_BODY||!1,Ft=!1!==t.SANITIZE_DOM,Pt=!1!==t.KEEP_CONTENT,jt=t.IN_PLACE||!1,bt=t.ALLOWED_URI_REGEXP||bt,Zt=t.NAMESPACE||Xt,t.CUSTOM_ELEMENT_HANDLING&&ie(t.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(Tt.tagNameCheck=t.CUSTOM_ELEMENT_HANDLING.tagNameCheck),t.CUSTOM_ELEMENT_HANDLING&&ie(t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(Tt.attributeNameCheck=t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),t.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(Tt.allowCustomizedBuiltInElements=t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Kt=Kt=-1===Jt.indexOf(t.PARSER_MEDIA_TYPE)?te:t.PARSER_MEDIA_TYPE,ee="application/xhtml+xml"===Kt?function(t){return t}:y,Nt&&(At=!1),It&&(Lt=!0),Yt&&(_t=k({},[].concat($(O))),wt=[],!0===Yt.html&&(k(_t,C),k(wt,B)),!0===Yt.svg&&(k(_t,S),k(wt,L),k(wt,R)),!0===Yt.svgFilters&&(k(_t,A),k(wt,L),k(wt,R)),!0===Yt.mathMl&&(k(_t,N),k(wt,I),k(wt,R))),t.ADD_TAGS&&(_t===xt&&(_t=T(_t)),k(_t,t.ADD_TAGS)),t.ADD_ATTR&&(wt===kt&&(wt=T(wt)),k(wt,t.ADD_ATTR)),t.ADD_URI_SAFE_ATTR&&k($t,t.ADD_URI_SAFE_ATTR),t.FORBID_CONTENTS&&(zt===Ut&&(zt=T(zt)),k(zt,t.FORBID_CONTENTS)),Pt&&(_t["#text"]=!0),Dt&&k(_t,["html","head","body"]),_t.table&&(k(_t,["tbody"]),delete Et.tbody),a&&a(t),ne=t)},oe=k({},["mi","mo","mn","ms","mtext"]),se=k({},["foreignobject","desc","title","annotation-xml"]),ce=k({},S);k(ce,A),k(ce,M);var ue=k({},N);k(ue,D);var le=function(t){var e=et(t);e&&e.tagName||(e={namespaceURI:Xt,tagName:"template"});var n=y(t.tagName),r=y(e.tagName);if(t.namespaceURI===Gt)return e.namespaceURI===Xt?"svg"===n:e.namespaceURI===Vt?"svg"===n&&("annotation-xml"===r||oe[r]):Boolean(ce[n]);if(t.namespaceURI===Vt)return e.namespaceURI===Xt?"math"===n:e.namespaceURI===Gt?"math"===n&&se[r]:Boolean(ue[n]);if(t.namespaceURI===Xt){if(e.namespaceURI===Gt&&!se[r])return!1;if(e.namespaceURI===Vt&&!oe[r])return!1;var i=k({},["title","style","font","a","script"]);return!ue[n]&&(i[n]||!ce[n])}return!1},he=function(t){p(n.removed,{element:t});try{t.parentNode.removeChild(t)}catch(e){try{t.outerHTML=it}catch(e){t.remove()}}},fe=function(t,e){try{p(n.removed,{attribute:e.getAttributeNode(t),from:e})}catch(t){p(n.removed,{attribute:null,from:e})}if(e.removeAttribute(t),"is"===t&&!wt[t])if(Lt||It)try{he(e)}catch(t){}else try{e.setAttribute(t,"")}catch(t){}},de=function(t){var e=void 0,n=void 0;if(Bt)t="<remove></remove>"+t;else{var r=g(t,/^[\r\n\t ]+/);n=r&&r[0]}"application/xhtml+xml"===Kt&&(t='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+t+"</body></html>");var a=rt?rt.createHTML(t):t;if(Zt===Xt)try{e=(new X).parseFromString(a,Kt)}catch(t){}if(!e||!e.documentElement){e=ot.createDocument(Zt,"template",null);try{e.documentElement.innerHTML=Qt?"":a}catch(t){}}var o=e.body||e.documentElement;return t&&n&&o.insertBefore(i.createTextNode(n),o.childNodes[0]||null),Zt===Xt?ut.call(e,Dt?"html":"body")[0]:Dt?e.documentElement:o},pe=function(t){return st.call(t.ownerDocument||t,t,l.SHOW_ELEMENT|l.SHOW_COMMENT|l.SHOW_TEXT,null,!1)},ye=function(t){return t instanceof G&&("string"!=typeof t.nodeName||"string"!=typeof t.textContent||"function"!=typeof t.removeChild||!(t.attributes instanceof w)||"function"!=typeof t.removeAttribute||"function"!=typeof t.setAttribute||"string"!=typeof t.namespaceURI||"function"!=typeof t.insertBefore)},ge=function(t){return"object"===(void 0===c?"undefined":H(c))?t instanceof c:t&&"object"===(void 0===t?"undefined":H(t))&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName},me=function(t,e,r){ft[t]&&f(ft[t],(function(t){t.call(n,e,r,ne)}))},ve=function(t){var e=void 0;if(me("beforeSanitizeElements",t,null),ye(t))return he(t),!0;if(g(t.nodeName,/[\u0080-\uFFFF]/))return he(t),!0;var r=ee(t.nodeName);if(me("uponSanitizeElement",t,{tagName:r,allowedTags:_t}),!ge(t.firstElementChild)&&(!ge(t.content)||!ge(t.content.firstElementChild))&&_(/<[/\w]/g,t.innerHTML)&&_(/<[/\w]/g,t.textContent))return he(t),!0;if("select"===r&&_(/<template/i,t.innerHTML))return he(t),!0;if(!_t[r]||Et[r]){if(Pt&&!zt[r]){var i=et(t)||t.parentNode,a=tt(t)||t.childNodes;if(a&&i)for(var o=a.length-1;o>=0;--o)i.insertBefore(K(a[o],!0),J(t))}if(!Et[r]&&_e(r)){if(Tt.tagNameCheck instanceof RegExp&&_(Tt.tagNameCheck,r))return!1;if(Tt.tagNameCheck instanceof Function&&Tt.tagNameCheck(r))return!1}return he(t),!0}return t instanceof u&&!le(t)?(he(t),!0):"noscript"!==r&&"noembed"!==r||!_(/<\/no(script|embed)/i,t.innerHTML)?(Nt&&3===t.nodeType&&(e=t.textContent,e=m(e,dt," "),e=m(e,pt," "),t.textContent!==e&&(p(n.removed,{element:t.cloneNode()}),t.textContent=e)),me("afterSanitizeElements",t,null),!1):(he(t),!0)},be=function(t,e,n){if(Ft&&("id"===e||"name"===e)&&(n in i||n in re))return!1;if(At&&!Ct[e]&&_(yt,e));else if(St&&_(gt,e));else if(!wt[e]||Ct[e]){if(!(_e(t)&&(Tt.tagNameCheck instanceof RegExp&&_(Tt.tagNameCheck,t)||Tt.tagNameCheck instanceof Function&&Tt.tagNameCheck(t))&&(Tt.attributeNameCheck instanceof RegExp&&_(Tt.attributeNameCheck,e)||Tt.attributeNameCheck instanceof Function&&Tt.attributeNameCheck(e))||"is"===e&&Tt.allowCustomizedBuiltInElements&&(Tt.tagNameCheck instanceof RegExp&&_(Tt.tagNameCheck,n)||Tt.tagNameCheck instanceof Function&&Tt.tagNameCheck(n))))return!1}else if($t[e]);else if(_(bt,m(n,vt,"")));else if("src"!==e&&"xlink:href"!==e&&"href"!==e||"script"===t||0!==v(n,"data:")||!qt[t])if(Mt&&!_(mt,m(n,vt,"")));else if(n)return!1;return!0},_e=function(t){return t.indexOf("-")>0},xe=function(t){var e=void 0,r=void 0,i=void 0,a=void 0;me("beforeSanitizeAttributes",t,null);var o=t.attributes;if(o){var s={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:wt};for(a=o.length;a--;){var c=e=o[a],u=c.name,l=c.namespaceURI;if(r=b(e.value),i=ee(u),s.attrName=i,s.attrValue=r,s.keepAttr=!0,s.forceKeepAttr=void 0,me("uponSanitizeAttribute",t,s),r=s.attrValue,!s.forceKeepAttr&&(fe(u,t),s.keepAttr))if(_(/\/>/i,r))fe(u,t);else{Nt&&(r=m(r,dt," "),r=m(r,pt," "));var h=ee(t.nodeName);if(be(h,i,r))try{l?t.setAttributeNS(l,u,r):t.setAttribute(u,r),d(n.removed)}catch(t){}}}me("afterSanitizeAttributes",t,null)}},we=function t(e){var n=void 0,r=pe(e);for(me("beforeSanitizeShadowDOM",e,null);n=r.nextNode();)me("uponSanitizeShadowNode",n,null),ve(n)||(n.content instanceof o&&t(n.content),xe(n));me("afterSanitizeShadowDOM",e,null)};return n.sanitize=function(t,i){var a=void 0,s=void 0,u=void 0,l=void 0,h=void 0;if((Qt=!t)&&(t="\x3c!--\x3e"),"string"!=typeof t&&!ge(t)){if("function"!=typeof t.toString)throw x("toString is not a function");if("string"!=typeof(t=t.toString()))throw x("dirty is not a string, aborting")}if(!n.isSupported){if("object"===H(e.toStaticHTML)||"function"==typeof e.toStaticHTML){if("string"==typeof t)return e.toStaticHTML(t);if(ge(t))return e.toStaticHTML(t.outerHTML)}return t}if(Ot||ae(i),n.removed=[],"string"==typeof t&&(jt=!1),jt);else if(t instanceof c)1===(s=(a=de("\x3c!----\x3e")).ownerDocument.importNode(t,!0)).nodeType&&"BODY"===s.nodeName||"HTML"===s.nodeName?a=s:a.appendChild(s);else{if(!Lt&&!Nt&&!Dt&&-1===t.indexOf("<"))return rt&&Rt?rt.createHTML(t):t;if(!(a=de(t)))return Lt?null:it}a&&Bt&&he(a.firstChild);for(var f=pe(jt?t:a);u=f.nextNode();)3===u.nodeType&&u===l||ve(u)||(u.content instanceof o&&we(u.content),xe(u),l=u);if(l=null,jt)return t;if(Lt){if(It)for(h=ct.call(a.ownerDocument);a.firstChild;)h.appendChild(a.firstChild);else h=a;return wt.shadowroot&&(h=lt.call(r,h,!0)),h}var d=Dt?a.outerHTML:a.innerHTML;return Nt&&(d=m(d,dt," "),d=m(d,pt," ")),rt&&Rt?rt.createHTML(d):d},n.setConfig=function(t){ae(t),Ot=!0},n.clearConfig=function(){ne=null,Ot=!1},n.isValidAttribute=function(t,e,n){ne||ae({});var r=ee(t),i=ee(e);return be(r,i,n)},n.addHook=function(t,e){"function"==typeof e&&(ft[t]=ft[t]||[],p(ft[t],e))},n.removeHook=function(t){ft[t]&&d(ft[t])},n.removeHooks=function(t){ft[t]&&(ft[t]=[])},n.removeAllHooks=function(){ft={}},n}()}()},8282:(t,e,n)=>{var r=n(2354);t.exports={Graph:r.Graph,json:n(8974),alg:n(2440),version:r.version}},2842:(t,e,n)=>{var r=n(9126);t.exports=function(t){var e,n={},i=[];function a(i){r.has(n,i)||(n[i]=!0,e.push(i),r.each(t.successors(i),a),r.each(t.predecessors(i),a))}return r.each(t.nodes(),(function(t){e=[],a(t),e.length&&i.push(e)})),i}},3984:(t,e,n)=>{var r=n(9126);function i(t,e,n,a,o,s){r.has(a,e)||(a[e]=!0,n||s.push(e),r.each(o(e),(function(e){i(t,e,n,a,o,s)})),n&&s.push(e))}t.exports=function(t,e,n){r.isArray(e)||(e=[e]);var a=(t.isDirected()?t.successors:t.neighbors).bind(t),o=[],s={};return r.each(e,(function(e){if(!t.hasNode(e))throw new Error("Graph does not have node: "+e);i(t,e,"post"===n,s,a,o)})),o}},4847:(t,e,n)=>{var r=n(3763),i=n(9126);t.exports=function(t,e,n){return i.transform(t.nodes(),(function(i,a){i[a]=r(t,a,e,n)}),{})}},3763:(t,e,n)=>{var r=n(9126),i=n(9675);t.exports=function(t,e,n,r){return function(t,e,n,r){var a,o,s={},c=new i,u=function(t){var e=t.v!==a?t.v:t.w,r=s[e],i=n(t),u=o.distance+i;if(i<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+t+" Weight: "+i);u<r.distance&&(r.distance=u,r.predecessor=a,c.decrease(e,u))};for(t.nodes().forEach((function(t){var n=t===e?0:Number.POSITIVE_INFINITY;s[t]={distance:n},c.add(t,n)}));c.size()>0&&(a=c.removeMin(),(o=s[a]).distance!==Number.POSITIVE_INFINITY);)r(a).forEach(u);return s}(t,String(e),n||a,r||function(e){return t.outEdges(e)})};var a=r.constant(1)},9096:(t,e,n)=>{var r=n(9126),i=n(5023);t.exports=function(t){return r.filter(i(t),(function(e){return e.length>1||1===e.length&&t.hasEdge(e[0],e[0])}))}},8924:(t,e,n)=>{var r=n(9126);t.exports=function(t,e,n){return function(t,e,n){var r={},i=t.nodes();return i.forEach((function(t){r[t]={},r[t][t]={distance:0},i.forEach((function(e){t!==e&&(r[t][e]={distance:Number.POSITIVE_INFINITY})})),n(t).forEach((function(n){var i=n.v===t?n.w:n.v,a=e(n);r[t][i]={distance:a,predecessor:t}}))})),i.forEach((function(t){var e=r[t];i.forEach((function(n){var a=r[n];i.forEach((function(n){var r=a[t],i=e[n],o=a[n],s=r.distance+i.distance;s<o.distance&&(o.distance=s,o.predecessor=i.predecessor)}))}))})),r}(t,e||i,n||function(e){return t.outEdges(e)})};var i=r.constant(1)},2440:(t,e,n)=>{t.exports={components:n(2842),dijkstra:n(3763),dijkstraAll:n(4847),findCycles:n(9096),floydWarshall:n(8924),isAcyclic:n(2707),postorder:n(8828),preorder:n(2648),prim:n(514),tarjan:n(5023),topsort:n(2166)}},2707:(t,e,n)=>{var r=n(2166);t.exports=function(t){try{r(t)}catch(t){if(t instanceof r.CycleException)return!1;throw t}return!0}},8828:(t,e,n)=>{var r=n(3984);t.exports=function(t,e){return r(t,e,"post")}},2648:(t,e,n)=>{var r=n(3984);t.exports=function(t,e){return r(t,e,"pre")}},514:(t,e,n)=>{var r=n(9126),i=n(771),a=n(9675);t.exports=function(t,e){var n,o=new i,s={},c=new a;function u(t){var r=t.v===n?t.w:t.v,i=c.priority(r);if(void 0!==i){var a=e(t);a<i&&(s[r]=n,c.decrease(r,a))}}if(0===t.nodeCount())return o;r.each(t.nodes(),(function(t){c.add(t,Number.POSITIVE_INFINITY),o.setNode(t)})),c.decrease(t.nodes()[0],0);for(var l=!1;c.size()>0;){if(n=c.removeMin(),r.has(s,n))o.setEdge(n,s[n]);else{if(l)throw new Error("Input graph is not connected: "+t);l=!0}t.nodeEdges(n).forEach(u)}return o}},5023:(t,e,n)=>{var r=n(9126);t.exports=function(t){var e=0,n=[],i={},a=[];function o(s){var c=i[s]={onStack:!0,lowlink:e,index:e++};if(n.push(s),t.successors(s).forEach((function(t){r.has(i,t)?i[t].onStack&&(c.lowlink=Math.min(c.lowlink,i[t].index)):(o(t),c.lowlink=Math.min(c.lowlink,i[t].lowlink))})),c.lowlink===c.index){var u,l=[];do{u=n.pop(),i[u].onStack=!1,l.push(u)}while(s!==u);a.push(l)}}return t.nodes().forEach((function(t){r.has(i,t)||o(t)})),a}},2166:(t,e,n)=>{var r=n(9126);function i(t){var e={},n={},i=[];if(r.each(t.sinks(),(function o(s){if(r.has(n,s))throw new a;r.has(e,s)||(n[s]=!0,e[s]=!0,r.each(t.predecessors(s),o),delete n[s],i.push(s))})),r.size(e)!==t.nodeCount())throw new a;return i}function a(){}t.exports=i,i.CycleException=a,a.prototype=new Error},9675:(t,e,n)=>{var r=n(9126);function i(){this._arr=[],this._keyIndices={}}t.exports=i,i.prototype.size=function(){return this._arr.length},i.prototype.keys=function(){return this._arr.map((function(t){return t.key}))},i.prototype.has=function(t){return r.has(this._keyIndices,t)},i.prototype.priority=function(t){var e=this._keyIndices[t];if(void 0!==e)return this._arr[e].priority},i.prototype.min=function(){if(0===this.size())throw new Error("Queue underflow");return this._arr[0].key},i.prototype.add=function(t,e){var n=this._keyIndices;if(t=String(t),!r.has(n,t)){var i=this._arr,a=i.length;return n[t]=a,i.push({key:t,priority:e}),this._decrease(a),!0}return!1},i.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key},i.prototype.decrease=function(t,e){var n=this._keyIndices[t];if(e>this._arr[n].priority)throw new Error("New priority is greater than current priority. Key: "+t+" Old: "+this._arr[n].priority+" New: "+e);this._arr[n].priority=e,this._decrease(n)},i.prototype._heapify=function(t){var e=this._arr,n=2*t,r=n+1,i=t;n<e.length&&(i=e[n].priority<e[i].priority?n:i,r<e.length&&(i=e[r].priority<e[i].priority?r:i),i!==t&&(this._swap(t,i),this._heapify(i)))},i.prototype._decrease=function(t){for(var e,n=this._arr,r=n[t].priority;0!==t&&!(n[e=t>>1].priority<r);)this._swap(t,e),t=e},i.prototype._swap=function(t,e){var n=this._arr,r=this._keyIndices,i=n[t],a=n[e];n[t]=a,n[e]=i,r[a.key]=t,r[i.key]=e}},771:(t,e,n)=>{"use strict";var r=n(9126);t.exports=a;var i="\0";function a(t){this._isDirected=!r.has(t,"directed")||t.directed,this._isMultigraph=!!r.has(t,"multigraph")&&t.multigraph,this._isCompound=!!r.has(t,"compound")&&t.compound,this._label=void 0,this._defaultNodeLabelFn=r.constant(void 0),this._defaultEdgeLabelFn=r.constant(void 0),this._nodes={},this._isCompound&&(this._parent={},this._children={},this._children["\0"]={}),this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={}}function o(t,e){t[e]?t[e]++:t[e]=1}function s(t,e){--t[e]||delete t[e]}function c(t,e,n,i){var a=""+e,o=""+n;if(!t&&a>o){var s=a;a=o,o=s}return a+""+o+""+(r.isUndefined(i)?"\0":i)}function u(t,e,n,r){var i=""+e,a=""+n;if(!t&&i>a){var o=i;i=a,a=o}var s={v:i,w:a};return r&&(s.name=r),s}function l(t,e){return c(t,e.v,e.w,e.name)}a.prototype._nodeCount=0,a.prototype._edgeCount=0,a.prototype.isDirected=function(){return this._isDirected},a.prototype.isMultigraph=function(){return this._isMultigraph},a.prototype.isCompound=function(){return this._isCompound},a.prototype.setGraph=function(t){return this._label=t,this},a.prototype.graph=function(){return this._label},a.prototype.setDefaultNodeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultNodeLabelFn=t,this},a.prototype.nodeCount=function(){return this._nodeCount},a.prototype.nodes=function(){return r.keys(this._nodes)},a.prototype.sources=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._in[e])}))},a.prototype.sinks=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._out[e])}))},a.prototype.setNodes=function(t,e){var n=arguments,i=this;return r.each(t,(function(t){n.length>1?i.setNode(t,e):i.setNode(t)})),this},a.prototype.setNode=function(t,e){return r.has(this._nodes,t)?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]=i,this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)},a.prototype.node=function(t){return this._nodes[t]},a.prototype.hasNode=function(t){return r.has(this._nodes,t)},a.prototype.removeNode=function(t){var e=this;if(r.has(this._nodes,t)){var n=function(t){e.removeEdge(e._edgeObjs[t])};delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],r.each(this.children(t),(function(t){e.setParent(t)})),delete this._children[t]),r.each(r.keys(this._in[t]),n),delete this._in[t],delete this._preds[t],r.each(r.keys(this._out[t]),n),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this},a.prototype.setParent=function(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(r.isUndefined(e))e=i;else{for(var n=e+="";!r.isUndefined(n);n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this},a.prototype._removeFromParentsChildList=function(t){delete this._children[this._parent[t]][t]},a.prototype.parent=function(t){if(this._isCompound){var e=this._parent[t];if(e!==i)return e}},a.prototype.children=function(t){if(r.isUndefined(t)&&(t=i),this._isCompound){var e=this._children[t];if(e)return r.keys(e)}else{if(t===i)return this.nodes();if(this.hasNode(t))return[]}},a.prototype.predecessors=function(t){var e=this._preds[t];if(e)return r.keys(e)},a.prototype.successors=function(t){var e=this._sucs[t];if(e)return r.keys(e)},a.prototype.neighbors=function(t){var e=this.predecessors(t);if(e)return r.union(e,this.successors(t))},a.prototype.isLeaf=function(t){return 0===(this.isDirected()?this.successors(t):this.neighbors(t)).length},a.prototype.filterNodes=function(t){var e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph());var n=this;r.each(this._nodes,(function(n,r){t(r)&&e.setNode(r,n)})),r.each(this._edgeObjs,(function(t){e.hasNode(t.v)&&e.hasNode(t.w)&&e.setEdge(t,n.edge(t))}));var i={};function a(t){var r=n.parent(t);return void 0===r||e.hasNode(r)?(i[t]=r,r):r in i?i[r]:a(r)}return this._isCompound&&r.each(e.nodes(),(function(t){e.setParent(t,a(t))})),e},a.prototype.setDefaultEdgeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultEdgeLabelFn=t,this},a.prototype.edgeCount=function(){return this._edgeCount},a.prototype.edges=function(){return r.values(this._edgeObjs)},a.prototype.setPath=function(t,e){var n=this,i=arguments;return r.reduce(t,(function(t,r){return i.length>1?n.setEdge(t,r,e):n.setEdge(t,r),r})),this},a.prototype.setEdge=function(){var t,e,n,i,a=!1,s=arguments[0];"object"==typeof s&&null!==s&&"v"in s?(t=s.v,e=s.w,n=s.name,2===arguments.length&&(i=arguments[1],a=!0)):(t=s,e=arguments[1],n=arguments[3],arguments.length>2&&(i=arguments[2],a=!0)),t=""+t,e=""+e,r.isUndefined(n)||(n=""+n);var l=c(this._isDirected,t,e,n);if(r.has(this._edgeLabels,l))return a&&(this._edgeLabels[l]=i),this;if(!r.isUndefined(n)&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(t),this.setNode(e),this._edgeLabels[l]=a?i:this._defaultEdgeLabelFn(t,e,n);var h=u(this._isDirected,t,e,n);return t=h.v,e=h.w,Object.freeze(h),this._edgeObjs[l]=h,o(this._preds[e],t),o(this._sucs[t],e),this._in[e][l]=h,this._out[t][l]=h,this._edgeCount++,this},a.prototype.edge=function(t,e,n){var r=1===arguments.length?l(this._isDirected,arguments[0]):c(this._isDirected,t,e,n);return this._edgeLabels[r]},a.prototype.hasEdge=function(t,e,n){var i=1===arguments.length?l(this._isDirected,arguments[0]):c(this._isDirected,t,e,n);return r.has(this._edgeLabels,i)},a.prototype.removeEdge=function(t,e,n){var r=1===arguments.length?l(this._isDirected,arguments[0]):c(this._isDirected,t,e,n),i=this._edgeObjs[r];return i&&(t=i.v,e=i.w,delete this._edgeLabels[r],delete this._edgeObjs[r],s(this._preds[e],t),s(this._sucs[t],e),delete this._in[e][r],delete this._out[t][r],this._edgeCount--),this},a.prototype.inEdges=function(t,e){var n=this._in[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.v===e})):i}},a.prototype.outEdges=function(t,e){var n=this._out[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.w===e})):i}},a.prototype.nodeEdges=function(t,e){var n=this.inEdges(t,e);if(n)return n.concat(this.outEdges(t,e))}},2354:(t,e,n)=>{t.exports={Graph:n(771),version:n(9631)}},8974:(t,e,n)=>{var r=n(9126),i=n(771);function a(t){return r.map(t.nodes(),(function(e){var n=t.node(e),i=t.parent(e),a={v:e};return r.isUndefined(n)||(a.value=n),r.isUndefined(i)||(a.parent=i),a}))}function o(t){return r.map(t.edges(),(function(e){var n=t.edge(e),i={v:e.v,w:e.w};return r.isUndefined(e.name)||(i.name=e.name),r.isUndefined(n)||(i.value=n),i}))}t.exports={write:function(t){var e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:a(t),edges:o(t)};return r.isUndefined(t.graph())||(e.value=r.clone(t.graph())),e},read:function(t){var e=new i(t.options).setGraph(t.value);return r.each(t.nodes,(function(t){e.setNode(t.v,t.value),t.parent&&e.setParent(t.v,t.parent)})),r.each(t.edges,(function(t){e.setEdge({v:t.v,w:t.w,name:t.name},t.value)})),e}}},9126:(t,e,n)=>{var r;try{r={clone:n(6678),constant:n(5703),each:n(6073),filter:n(3105),has:n(8721),isArray:n(1469),isEmpty:n(1609),isFunction:n(3560),isUndefined:n(2353),keys:n(3674),map:n(5161),reduce:n(4061),size:n(4238),transform:n(8718),union:n(3386),values:n(2628)}}catch(t){}r||(r=window._),t.exports=r},9631:t=>{t.exports="2.1.8"},1773:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(7628),a=function(){function t(t,e){this.color=e,this.changed=!1,this.data=t,this.type=new i.default}return t.prototype.set=function(t,e){return this.color=e,this.changed=!1,this.data=t,this.type.type=0,this},t.prototype._ensureHSL=function(){var t=this.data,e=t.h,n=t.s,i=t.l;void 0===e&&(t.h=r.default.channel.rgb2hsl(t,"h")),void 0===n&&(t.s=r.default.channel.rgb2hsl(t,"s")),void 0===i&&(t.l=r.default.channel.rgb2hsl(t,"l"))},t.prototype._ensureRGB=function(){var t=this.data,e=t.r,n=t.g,i=t.b;void 0===e&&(t.r=r.default.channel.hsl2rgb(t,"r")),void 0===n&&(t.g=r.default.channel.hsl2rgb(t,"g")),void 0===i&&(t.b=r.default.channel.hsl2rgb(t,"b"))},Object.defineProperty(t.prototype,"r",{get:function(){var t=this.data,e=t.r;return this.type.is(2)||void 0===e?(this._ensureHSL(),r.default.channel.hsl2rgb(t,"r")):e},set:function(t){this.type.set(1),this.changed=!0,this.data.r=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"g",{get:function(){var t=this.data,e=t.g;return this.type.is(2)||void 0===e?(this._ensureHSL(),r.default.channel.hsl2rgb(t,"g")):e},set:function(t){this.type.set(1),this.changed=!0,this.data.g=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"b",{get:function(){var t=this.data,e=t.b;return this.type.is(2)||void 0===e?(this._ensureHSL(),r.default.channel.hsl2rgb(t,"b")):e},set:function(t){this.type.set(1),this.changed=!0,this.data.b=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"h",{get:function(){var t=this.data,e=t.h;return this.type.is(1)||void 0===e?(this._ensureRGB(),r.default.channel.rgb2hsl(t,"h")):e},set:function(t){this.type.set(2),this.changed=!0,this.data.h=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"s",{get:function(){var t=this.data,e=t.s;return this.type.is(1)||void 0===e?(this._ensureRGB(),r.default.channel.rgb2hsl(t,"s")):e},set:function(t){this.type.set(2),this.changed=!0,this.data.s=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"l",{get:function(){var t=this.data,e=t.l;return this.type.is(1)||void 0===e?(this._ensureRGB(),r.default.channel.rgb2hsl(t,"l")):e},set:function(t){this.type.set(2),this.changed=!0,this.data.l=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"a",{get:function(){return this.data.a},set:function(t){this.changed=!0,this.data.a=t},enumerable:!0,configurable:!0}),t}();e.default=a},8167:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=new(n(1773).default)({r:0,g:0,b:0,a:0},"transparent");e.default=r},7628:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(){this.type=0}return t.prototype.get=function(){return this.type},t.prototype.set=function(t){if(this.type&&this.type!==t)throw new Error("Cannot change both RGB and HSL channels at the same time");this.type=t},t.prototype.reset=function(){this.type=0},t.prototype.is=function(t){return this.type===t},t}();e.default=n},1655:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(8167),i=n(6061),a={re:/^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i,parse:function(t){if(35===t.charCodeAt(0)){var e=t.match(a.re);if(e){var n=e[1],i=parseInt(n,16),o=n.length,s=o%4==0,c=o>4,u=c?1:17,l=c?8:4,h=s?0:-1,f=c?255:15;return r.default.set({r:(i>>l*(h+3)&f)*u,g:(i>>l*(h+2)&f)*u,b:(i>>l*(h+1)&f)*u,a:s?(i&f)*u/255:1},t)}}},stringify:function(t){var e=t.r,n=t.g,r=t.b,a=t.a;return a<1?"#"+i.DEC2HEX[Math.round(e)]+i.DEC2HEX[Math.round(n)]+i.DEC2HEX[Math.round(r)]+i.DEC2HEX[Math.round(255*a)]:"#"+i.DEC2HEX[Math.round(e)]+i.DEC2HEX[Math.round(n)]+i.DEC2HEX[Math.round(r)]}};e.default=a},8589:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a={re:/^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i,hueRe:/^(.+?)(deg|grad|rad|turn)$/i,_hue2deg:function(t){var e=t.match(a.hueRe);if(e){var n=e[1];switch(e[2]){case"grad":return r.default.channel.clamp.h(.9*parseFloat(n));case"rad":return r.default.channel.clamp.h(180*parseFloat(n)/Math.PI);case"turn":return r.default.channel.clamp.h(360*parseFloat(n))}}return r.default.channel.clamp.h(parseFloat(t))},parse:function(t){var e=t.charCodeAt(0);if(104===e||72===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5];return i.default.set({h:a._hue2deg(o),s:r.default.channel.clamp.s(parseFloat(s)),l:r.default.channel.clamp.l(parseFloat(c)),a:u?r.default.channel.clamp.a(l?parseFloat(u)/100:parseFloat(u)):1},t)}}},stringify:function(t){var e=t.h,n=t.s,i=t.l,a=t.a;return a<1?"hsla("+r.default.lang.round(e)+", "+r.default.lang.round(n)+"%, "+r.default.lang.round(i)+"%, "+a+")":"hsl("+r.default.lang.round(e)+", "+r.default.lang.round(n)+"%, "+r.default.lang.round(i)+"%)"}};e.default=a},2191:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1655),i=n(7538),a=n(6762),o=n(8589),s={format:{keyword:i.default,hex:r.default,rgb:a.default,rgba:a.default,hsl:o.default,hsla:o.default},parse:function(t){if("string"!=typeof t)return t;var e=r.default.parse(t)||a.default.parse(t)||o.default.parse(t)||i.default.parse(t);if(e)return e;throw new Error('Unsupported color format: "'+t+'"')},stringify:function(t){return!t.changed&&t.color?t.color:t.type.is(2)||void 0===t.data.r?o.default.stringify(t):t.a<1||!Number.isInteger(t.r)||!Number.isInteger(t.g)||!Number.isInteger(t.b)?a.default.stringify(t):r.default.stringify(t)}};e.default=s},7538:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1655),i={colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyanaqua:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",transparent:"#00000000",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},parse:function(t){t=t.toLowerCase();var e=i.colors[t];if(e)return r.default.parse(e)},stringify:function(t){var e=r.default.stringify(t);for(var n in i.colors)if(i.colors[n]===e)return n}};e.default=i},6762:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a={re:/^rgba?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?)))?\s*?\)$/i,parse:function(t){var e=t.charCodeAt(0);if(114===e||82===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5],h=n[6],f=n[7],d=n[8];return i.default.set({r:r.default.channel.clamp.r(s?2.55*parseFloat(o):parseFloat(o)),g:r.default.channel.clamp.g(u?2.55*parseFloat(c):parseFloat(c)),b:r.default.channel.clamp.b(h?2.55*parseFloat(l):parseFloat(l)),a:f?r.default.channel.clamp.a(d?parseFloat(f)/100:parseFloat(f)):1},t)}}},stringify:function(t){var e=t.r,n=t.g,i=t.b,a=t.a;return a<1?"rgba("+r.default.lang.round(e)+", "+r.default.lang.round(n)+", "+r.default.lang.round(i)+", "+r.default.lang.round(a)+")":"rgb("+r.default.lang.round(e)+", "+r.default.lang.round(n)+", "+r.default.lang.round(i)+")"}};e.default=a},6061:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i={};e.DEC2HEX=i;for(var a=0;a<=255;a++)i[a]=r.default.unit.dec2hex(a)},8613:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),function(t){for(var n in t)e.hasOwnProperty(n)||(e[n]=t[n])}(n(1203))},5371:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191),i=n(418);e.default=function(t,e){var n=r.default.parse(t),a={};for(var o in e)e[o]&&(a[o]=n[o]+e[o]);return i.default(t,a)}},1416:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t,e,n){var a=i.default.parse(t),o=a[e],s=r.default.channel.clamp[e](o+n);return o!==s&&(a[e]=s),i.default.stringify(a)}},9353:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"a")}},3394:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"b")}},418:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t,e){var n=i.default.parse(t);for(var a in e)n[a]=r.default.channel.clamp[a](e[a]);return i.default.stringify(n)}},6197:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t,e){return r.default.lang.round(i.default.parse(t)[e])}},7361:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t){return r.default(t,"h",180)}},3042:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8346);e.default=function(t,e){var n=i.default(t),a=i.default(e),o=Math.max(n,a),s=Math.min(n,a),c=(o+Number.EPSILON)/(s+Number.EPSILON);return r.default.lang.round(r.default.lang.clamp(c,1,10))}},1364:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"l",-e)}},9610:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"s",-e)}},572:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(418);e.default=function(t){return r.default(t,{s:0})}},2299:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"g")}},3116:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a=n(2191);e.default=function(t,e,n,o){void 0===o&&(o=1);var s=i.default.set({h:r.default.channel.clamp.h(t),s:r.default.channel.clamp.s(e),l:r.default.channel.clamp.l(n),a:r.default.channel.clamp.a(o)});return a.default.stringify(s)}},3008:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"h")}},1203:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6538);e.hex=r.default;var i=n(6538);e.rgb=i.default;var a=n(6538);e.rgba=a.default;var o=n(3116);e.hsl=o.default;var s=n(3116);e.hsla=s.default;var c=n(2619);e.toKeyword=c.default;var u=n(4307);e.toHex=u.default;var l=n(4125);e.toRgba=l.default;var h=n(7329);e.toHsla=h.default;var f=n(6197);e.channel=f.default;var d=n(9640);e.red=d.default;var p=n(2299);e.green=p.default;var y=n(3394);e.blue=y.default;var g=n(3008);e.hue=g.default;var m=n(6451);e.saturation=m.default;var v=n(9235);e.lightness=v.default;var b=n(9353);e.alpha=b.default;var _=n(9353);e.opacity=_.default;var x=n(3042);e.contrast=x.default;var w=n(8346);e.luminance=w.default;var k=n(4117);e.isDark=k.default;var T=n(2224);e.isLight=T.default;var E=n(585);e.isValid=E.default;var C=n(1080);e.saturate=C.default;var S=n(9610);e.desaturate=S.default;var A=n(3235);e.lighten=A.default;var M=n(1364);e.darken=M.default;var N=n(7189);e.opacify=N.default;var D=n(7189);e.fadeIn=D.default;var O=n(4989);e.transparentize=O.default;var B=n(4989);e.fadeOut=B.default;var L=n(7361);e.complement=L.default;var I=n(572);e.grayscale=I.default;var R=n(5371);e.adjust=R.default;var F=n(418);e.change=F.default;var P=n(566);e.invert=P.default;var j=n(1861);e.mix=j.default;var Y=n(7081);e.scale=Y.default},566:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191),i=n(1861);e.default=function(t,e){void 0===e&&(e=100);var n=r.default.parse(t);return n.r=255-n.r,n.g=255-n.g,n.b=255-n.b,i.default(n,t,e)}},4117:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2224);e.default=function(t){return!r.default(t)}},2224:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(8346);e.default=function(t){return r.default(t)>=.5}},585:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){try{return r.default.parse(t),!0}catch(t){return!1}}},3235:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"l",e)}},9235:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"l")}},8346:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191);e.default=function(t){var e=i.default.parse(t),n=e.r,a=e.g,o=e.b,s=.2126*r.default.channel.toLinear(n)+.7152*r.default.channel.toLinear(a)+.0722*r.default.channel.toLinear(o);return r.default.lang.round(s)}},1861:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191),i=n(6538);e.default=function(t,e,n){void 0===n&&(n=50);var a=r.default.parse(t),o=a.r,s=a.g,c=a.b,u=a.a,l=r.default.parse(e),h=l.r,f=l.g,d=l.b,p=l.a,y=n/100,g=2*y-1,m=u-p,v=((g*m==-1?g:(g+m)/(1+g*m))+1)/2,b=1-v,_=o*v+h*b,x=s*v+f*b,w=c*v+d*b,k=u*y+p*(1-y);return i.default(_,x,w,k)}},7189:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"a",e)}},9640:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"r")}},6538:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(8167),a=n(2191),o=n(418);e.default=function(t,e,n,s){if(void 0===n&&(n=0),void 0===s&&(s=1),"number"!=typeof t)return o.default(t,{a:e});var c=i.default.set({r:r.default.channel.clamp.r(t),g:r.default.channel.clamp.g(e),b:r.default.channel.clamp.b(n),a:r.default.channel.clamp.a(s)});return a.default.stringify(c)}},1080:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"s",e)}},6451:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(6197);e.default=function(t){return r.default(t,"s")}},7081:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1083),i=n(2191),a=n(5371);e.default=function(t,e){var n,o,s,c=i.default.parse(t),u={};for(var l in e)u[l]=(n=c[l],o=e[l],s=r.default.channel.max[l],o>0?(s-n)*o/100:n*o/100);return a.default(t,u)}},4307:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.hex.stringify(r.default.parse(t))}},7329:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.hsla.stringify(r.default.parse(t))}},2619:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.keyword.stringify(r.default.parse(t))}},4125:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(2191);e.default=function(t){return r.default.format.rgba.stringify(r.default.parse(t))}},4989:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1416);e.default=function(t,e){return r.default(t,"a",-e)}},7994:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:function(t){return t>=255?255:t<0?0:t},g:function(t){return t>=255?255:t<0?0:t},b:function(t){return t>=255?255:t<0?0:t},h:function(t){return t%360},s:function(t){return t>=100?100:t<0?0:t},l:function(t){return t>=100?100:t<0?0:t},a:function(t){return t>=1?1:t<0?0:t}},toLinear:function(t){var e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},hue2rgb:function(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t},hsl2rgb:function(t,e){var r=t.h,i=t.s,a=t.l;if(!i)return 2.55*a;r/=360,i/=100;var o=(a/=100)<.5?a*(1+i):a+i-a*i,s=2*a-o;switch(e){case"r":return 255*n.hue2rgb(s,o,r+1/3);case"g":return 255*n.hue2rgb(s,o,r);case"b":return 255*n.hue2rgb(s,o,r-1/3)}},rgb2hsl:function(t,e){var n=t.r,r=t.g,i=t.b;n/=255,r/=255,i/=255;var a=Math.max(n,r,i),o=Math.min(n,r,i),s=(a+o)/2;if("l"===e)return 100*s;if(a===o)return 0;var c=a-o;if("s"===e)return 100*(s>.5?c/(2-a-o):c/(a+o));switch(a){case n:return 60*((r-i)/c+(r<i?6:0));case r:return 60*((i-n)/c+2);case i:return 60*((n-r)/c+4);default:return-1}}};e.default=n},1083:(t,e,n)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(7994),i=n(4027),a=n(318),o={channel:r.default,lang:i.default,unit:a.default};e.default=o},4027:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n={clamp:function(t,e,n){return e>n?Math.min(e,Math.max(n,t)):Math.min(n,Math.max(e,t))},round:function(t){return Math.round(1e10*t)/1e10}};e.default=n},318:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});var n={dec2hex:function(t){var e=Math.round(t).toString(16);return e.length>1?e:"0"+e}};e.default=n},8552:(t,e,n)=>{var r=n(852)(n(5639),"DataView");t.exports=r},1989:(t,e,n)=>{var r=n(1789),i=n(401),a=n(7667),o=n(1327),s=n(1866);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},8407:(t,e,n)=>{var r=n(7040),i=n(2188),a=n(2117),o=n(7518),s=n(4705);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},7071:(t,e,n)=>{var r=n(852)(n(5639),"Map");t.exports=r},3369:(t,e,n)=>{var r=n(4785),i=n(1285),a=n(6e3),o=n(9916),s=n(5265);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},3818:(t,e,n)=>{var r=n(852)(n(5639),"Promise");t.exports=r},8525:(t,e,n)=>{var r=n(852)(n(5639),"Set");t.exports=r},8668:(t,e,n)=>{var r=n(3369),i=n(619),a=n(2385);function o(t){var e=-1,n=null==t?0:t.length;for(this.__data__=new r;++e<n;)this.add(t[e])}o.prototype.add=o.prototype.push=i,o.prototype.has=a,t.exports=o},6384:(t,e,n)=>{var r=n(8407),i=n(7465),a=n(3779),o=n(7599),s=n(4758),c=n(4309);function u(t){var e=this.__data__=new r(t);this.size=e.size}u.prototype.clear=i,u.prototype.delete=a,u.prototype.get=o,u.prototype.has=s,u.prototype.set=c,t.exports=u},2705:(t,e,n)=>{var r=n(5639).Symbol;t.exports=r},1149:(t,e,n)=>{var r=n(5639).Uint8Array;t.exports=r},577:(t,e,n)=>{var r=n(852)(n(5639),"WeakMap");t.exports=r},6874:t=>{t.exports=function(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}},7412:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r&&!1!==e(t[n],n,t););return t}},4963:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=0,a=[];++n<r;){var o=t[n];e(o,n,t)&&(a[i++]=o)}return a}},7443:(t,e,n)=>{var r=n(2118);t.exports=function(t,e){return!(null==t||!t.length)&&r(t,e,0)>-1}},1196:t=>{t.exports=function(t,e,n){for(var r=-1,i=null==t?0:t.length;++r<i;)if(n(e,t[r]))return!0;return!1}},4636:(t,e,n)=>{var r=n(2545),i=n(5694),a=n(1469),o=n(4144),s=n(5776),c=n(6719),u=Object.prototype.hasOwnProperty;t.exports=function(t,e){var n=a(t),l=!n&&i(t),h=!n&&!l&&o(t),f=!n&&!l&&!h&&c(t),d=n||l||h||f,p=d?r(t.length,String):[],y=p.length;for(var g in t)!e&&!u.call(t,g)||d&&("length"==g||h&&("offset"==g||"parent"==g)||f&&("buffer"==g||"byteLength"==g||"byteOffset"==g)||s(g,y))||p.push(g);return p}},9932:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=Array(r);++n<r;)i[n]=e(t[n],n,t);return i}},2488:t=>{t.exports=function(t,e){for(var n=-1,r=e.length,i=t.length;++n<r;)t[i+n]=e[n];return t}},2663:t=>{t.exports=function(t,e,n,r){var i=-1,a=null==t?0:t.length;for(r&&a&&(n=t[++i]);++i<a;)n=e(n,t[i],i,t);return n}},2908:t=>{t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r;)if(e(t[n],n,t))return!0;return!1}},8983:(t,e,n)=>{var r=n(371)("length");t.exports=r},6556:(t,e,n)=>{var r=n(9465),i=n(7813);t.exports=function(t,e,n){(void 0!==n&&!i(t[e],n)||void 0===n&&!(e in t))&&r(t,e,n)}},4865:(t,e,n)=>{var r=n(9465),i=n(7813),a=Object.prototype.hasOwnProperty;t.exports=function(t,e,n){var o=t[e];a.call(t,e)&&i(o,n)&&(void 0!==n||e in t)||r(t,e,n)}},8470:(t,e,n)=>{var r=n(7813);t.exports=function(t,e){for(var n=t.length;n--;)if(r(t[n][0],e))return n;return-1}},4037:(t,e,n)=>{var r=n(8363),i=n(3674);t.exports=function(t,e){return t&&r(e,i(e),t)}},3886:(t,e,n)=>{var r=n(8363),i=n(1704);t.exports=function(t,e){return t&&r(e,i(e),t)}},9465:(t,e,n)=>{var r=n(8777);t.exports=function(t,e,n){"__proto__"==e&&r?r(t,e,{configurable:!0,enumerable:!0,value:n,writable:!0}):t[e]=n}},5990:(t,e,n)=>{var r=n(6384),i=n(7412),a=n(4865),o=n(4037),s=n(3886),c=n(4626),u=n(278),l=n(8805),h=n(1911),f=n(8234),d=n(6904),p=n(4160),y=n(3824),g=n(9148),m=n(8517),v=n(1469),b=n(4144),_=n(6688),x=n(3218),w=n(2928),k=n(3674),T=n(1704),E="[object Arguments]",C="[object Function]",S="[object Object]",A={};A[E]=A["[object Array]"]=A["[object ArrayBuffer]"]=A["[object DataView]"]=A["[object Boolean]"]=A["[object Date]"]=A["[object Float32Array]"]=A["[object Float64Array]"]=A["[object Int8Array]"]=A["[object Int16Array]"]=A["[object Int32Array]"]=A["[object Map]"]=A["[object Number]"]=A[S]=A["[object RegExp]"]=A["[object Set]"]=A["[object String]"]=A["[object Symbol]"]=A["[object Uint8Array]"]=A["[object Uint8ClampedArray]"]=A["[object Uint16Array]"]=A["[object Uint32Array]"]=!0,A["[object Error]"]=A[C]=A["[object WeakMap]"]=!1,t.exports=function t(e,n,M,N,D,O){var B,L=1&n,I=2&n,R=4&n;if(M&&(B=D?M(e,N,D,O):M(e)),void 0!==B)return B;if(!x(e))return e;var F=v(e);if(F){if(B=y(e),!L)return u(e,B)}else{var P=p(e),j=P==C||"[object GeneratorFunction]"==P;if(b(e))return c(e,L);if(P==S||P==E||j&&!D){if(B=I||j?{}:m(e),!L)return I?h(e,s(B,e)):l(e,o(B,e))}else{if(!A[P])return D?e:{};B=g(e,P,L)}}O||(O=new r);var Y=O.get(e);if(Y)return Y;O.set(e,B),w(e)?e.forEach((function(r){B.add(t(r,n,M,r,e,O))})):_(e)&&e.forEach((function(r,i){B.set(i,t(r,n,M,i,e,O))}));var z=F?void 0:(R?I?d:f:I?T:k)(e);return i(z||e,(function(r,i){z&&(r=e[i=r]),a(B,i,t(r,n,M,i,e,O))})),B}},3118:(t,e,n)=>{var r=n(3218),i=Object.create,a=function(){function t(){}return function(e){if(!r(e))return{};if(i)return i(e);t.prototype=e;var n=new t;return t.prototype=void 0,n}}();t.exports=a},9881:(t,e,n)=>{var r=n(7816),i=n(9291)(r);t.exports=i},6029:(t,e,n)=>{var r=n(3448);t.exports=function(t,e,n){for(var i=-1,a=t.length;++i<a;){var o=t[i],s=e(o);if(null!=s&&(void 0===c?s==s&&!r(s):n(s,c)))var c=s,u=o}return u}},760:(t,e,n)=>{var r=n(9881);t.exports=function(t,e){var n=[];return r(t,(function(t,r,i){e(t,r,i)&&n.push(t)})),n}},1848:t=>{t.exports=function(t,e,n,r){for(var i=t.length,a=n+(r?1:-1);r?a--:++a<i;)if(e(t[a],a,t))return a;return-1}},1078:(t,e,n)=>{var r=n(2488),i=n(7285);t.exports=function t(e,n,a,o,s){var c=-1,u=e.length;for(a||(a=i),s||(s=[]);++c<u;){var l=e[c];n>0&&a(l)?n>1?t(l,n-1,a,o,s):r(s,l):o||(s[s.length]=l)}return s}},8483:(t,e,n)=>{var r=n(5063)();t.exports=r},7816:(t,e,n)=>{var r=n(8483),i=n(3674);t.exports=function(t,e){return t&&r(t,e,i)}},7786:(t,e,n)=>{var r=n(1811),i=n(327);t.exports=function(t,e){for(var n=0,a=(e=r(e,t)).length;null!=t&&n<a;)t=t[i(e[n++])];return n&&n==a?t:void 0}},8866:(t,e,n)=>{var r=n(2488),i=n(1469);t.exports=function(t,e,n){var a=e(t);return i(t)?a:r(a,n(t))}},4239:(t,e,n)=>{var r=n(2705),i=n(9607),a=n(2333),o=r?r.toStringTag:void 0;t.exports=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":o&&o in Object(t)?i(t):a(t)}},3325:t=>{t.exports=function(t,e){return t>e}},8565:t=>{var e=Object.prototype.hasOwnProperty;t.exports=function(t,n){return null!=t&&e.call(t,n)}},13:t=>{t.exports=function(t,e){return null!=t&&e in Object(t)}},2118:(t,e,n)=>{var r=n(1848),i=n(2722),a=n(2351);t.exports=function(t,e,n){return e==e?a(t,e,n):r(t,i,n)}},9454:(t,e,n)=>{var r=n(4239),i=n(7005);t.exports=function(t){return i(t)&&"[object Arguments]"==r(t)}},939:(t,e,n)=>{var r=n(2492),i=n(7005);t.exports=function t(e,n,a,o,s){return e===n||(null==e||null==n||!i(e)&&!i(n)?e!=e&&n!=n:r(e,n,a,o,t,s))}},2492:(t,e,n)=>{var r=n(6384),i=n(7114),a=n(8351),o=n(6096),s=n(4160),c=n(1469),u=n(4144),l=n(6719),h="[object Arguments]",f="[object Array]",d="[object Object]",p=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,y,g,m){var v=c(t),b=c(e),_=v?f:s(t),x=b?f:s(e),w=(_=_==h?d:_)==d,k=(x=x==h?d:x)==d,T=_==x;if(T&&u(t)){if(!u(e))return!1;v=!0,w=!1}if(T&&!w)return m||(m=new r),v||l(t)?i(t,e,n,y,g,m):a(t,e,_,n,y,g,m);if(!(1&n)){var E=w&&p.call(t,"__wrapped__"),C=k&&p.call(e,"__wrapped__");if(E||C){var S=E?t.value():t,A=C?e.value():e;return m||(m=new r),g(S,A,n,y,m)}}return!!T&&(m||(m=new r),o(t,e,n,y,g,m))}},5588:(t,e,n)=>{var r=n(4160),i=n(7005);t.exports=function(t){return i(t)&&"[object Map]"==r(t)}},2958:(t,e,n)=>{var r=n(6384),i=n(939);t.exports=function(t,e,n,a){var o=n.length,s=o,c=!a;if(null==t)return!s;for(t=Object(t);o--;){var u=n[o];if(c&&u[2]?u[1]!==t[u[0]]:!(u[0]in t))return!1}for(;++o<s;){var l=(u=n[o])[0],h=t[l],f=u[1];if(c&&u[2]){if(void 0===h&&!(l in t))return!1}else{var d=new r;if(a)var p=a(h,f,l,t,e,d);if(!(void 0===p?i(f,h,3,a,d):p))return!1}}return!0}},2722:t=>{t.exports=function(t){return t!=t}},8458:(t,e,n)=>{var r=n(3560),i=n(5346),a=n(3218),o=n(346),s=/^\[object .+?Constructor\]$/,c=Function.prototype,u=Object.prototype,l=c.toString,h=u.hasOwnProperty,f=RegExp("^"+l.call(h).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=function(t){return!(!a(t)||i(t))&&(r(t)?f:s).test(o(t))}},9221:(t,e,n)=>{var r=n(4160),i=n(7005);t.exports=function(t){return i(t)&&"[object Set]"==r(t)}},8749:(t,e,n)=>{var r=n(4239),i=n(1780),a=n(7005),o={};o["[object Float32Array]"]=o["[object Float64Array]"]=o["[object Int8Array]"]=o["[object Int16Array]"]=o["[object Int32Array]"]=o["[object Uint8Array]"]=o["[object Uint8ClampedArray]"]=o["[object Uint16Array]"]=o["[object Uint32Array]"]=!0,o["[object Arguments]"]=o["[object Array]"]=o["[object ArrayBuffer]"]=o["[object Boolean]"]=o["[object DataView]"]=o["[object Date]"]=o["[object Error]"]=o["[object Function]"]=o["[object Map]"]=o["[object Number]"]=o["[object Object]"]=o["[object RegExp]"]=o["[object Set]"]=o["[object String]"]=o["[object WeakMap]"]=!1,t.exports=function(t){return a(t)&&i(t.length)&&!!o[r(t)]}},7206:(t,e,n)=>{var r=n(1573),i=n(6432),a=n(6557),o=n(1469),s=n(9601);t.exports=function(t){return"function"==typeof t?t:null==t?a:"object"==typeof t?o(t)?i(t[0],t[1]):r(t):s(t)}},280:(t,e,n)=>{var r=n(5726),i=n(6916),a=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return i(t);var e=[];for(var n in Object(t))a.call(t,n)&&"constructor"!=n&&e.push(n);return e}},313:(t,e,n)=>{var r=n(3218),i=n(5726),a=n(3498),o=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return a(t);var e=i(t),n=[];for(var s in t)("constructor"!=s||!e&&o.call(t,s))&&n.push(s);return n}},433:t=>{t.exports=function(t,e){return t<e}},9199:(t,e,n)=>{var r=n(9881),i=n(8612);t.exports=function(t,e){var n=-1,a=i(t)?Array(t.length):[];return r(t,(function(t,r,i){a[++n]=e(t,r,i)})),a}},1573:(t,e,n)=>{var r=n(2958),i=n(1499),a=n(2634);t.exports=function(t){var e=i(t);return 1==e.length&&e[0][2]?a(e[0][0],e[0][1]):function(n){return n===t||r(n,t,e)}}},6432:(t,e,n)=>{var r=n(939),i=n(1917),a=n(9095),o=n(5403),s=n(9162),c=n(2634),u=n(327);t.exports=function(t,e){return o(t)&&s(e)?c(u(t),e):function(n){var o=i(n,t);return void 0===o&&o===e?a(n,t):r(e,o,3)}}},2980:(t,e,n)=>{var r=n(6384),i=n(6556),a=n(8483),o=n(9783),s=n(3218),c=n(1704),u=n(6390);t.exports=function t(e,n,l,h,f){e!==n&&a(n,(function(a,c){if(f||(f=new r),s(a))o(e,n,c,l,t,h,f);else{var d=h?h(u(e,c),a,c+"",e,n,f):void 0;void 0===d&&(d=a),i(e,c,d)}}),c)}},9783:(t,e,n)=>{var r=n(6556),i=n(4626),a=n(7133),o=n(278),s=n(8517),c=n(5694),u=n(1469),l=n(9246),h=n(4144),f=n(3560),d=n(3218),p=n(8630),y=n(6719),g=n(6390),m=n(3678);t.exports=function(t,e,n,v,b,_,x){var w=g(t,n),k=g(e,n),T=x.get(k);if(T)r(t,n,T);else{var E=_?_(w,k,n+"",t,e,x):void 0,C=void 0===E;if(C){var S=u(k),A=!S&&h(k),M=!S&&!A&&y(k);E=k,S||A||M?u(w)?E=w:l(w)?E=o(w):A?(C=!1,E=i(k,!0)):M?(C=!1,E=a(k,!0)):E=[]:p(k)||c(k)?(E=w,c(w)?E=m(w):d(w)&&!f(w)||(E=s(k))):C=!1}C&&(x.set(k,E),b(E,k,v,_,x),x.delete(k)),r(t,n,E)}}},9556:(t,e,n)=>{var r=n(9932),i=n(7786),a=n(7206),o=n(9199),s=n(1131),c=n(1717),u=n(5022),l=n(6557),h=n(1469);t.exports=function(t,e,n){e=e.length?r(e,(function(t){return h(t)?function(e){return i(e,1===t.length?t[0]:t)}:t})):[l];var f=-1;e=r(e,c(a));var d=o(t,(function(t,n,i){return{criteria:r(e,(function(e){return e(t)})),index:++f,value:t}}));return s(d,(function(t,e){return u(t,e,n)}))}},5970:(t,e,n)=>{var r=n(3012),i=n(9095);t.exports=function(t,e){return r(t,e,(function(e,n){return i(t,n)}))}},3012:(t,e,n)=>{var r=n(7786),i=n(611),a=n(1811);t.exports=function(t,e,n){for(var o=-1,s=e.length,c={};++o<s;){var u=e[o],l=r(t,u);n(l,u)&&i(c,a(u,t),l)}return c}},371:t=>{t.exports=function(t){return function(e){return null==e?void 0:e[t]}}},9152:(t,e,n)=>{var r=n(7786);t.exports=function(t){return function(e){return r(e,t)}}},98:t=>{var e=Math.ceil,n=Math.max;t.exports=function(t,r,i,a){for(var o=-1,s=n(e((r-t)/(i||1)),0),c=Array(s);s--;)c[a?s:++o]=t,t+=i;return c}},107:t=>{t.exports=function(t,e,n,r,i){return i(t,(function(t,i,a){n=r?(r=!1,t):e(n,t,i,a)})),n}},5976:(t,e,n)=>{var r=n(6557),i=n(5357),a=n(61);t.exports=function(t,e){return a(i(t,e,r),t+"")}},611:(t,e,n)=>{var r=n(4865),i=n(1811),a=n(5776),o=n(3218),s=n(327);t.exports=function(t,e,n,c){if(!o(t))return t;for(var u=-1,l=(e=i(e,t)).length,h=l-1,f=t;null!=f&&++u<l;){var d=s(e[u]),p=n;if("__proto__"===d||"constructor"===d||"prototype"===d)return t;if(u!=h){var y=f[d];void 0===(p=c?c(y,d,f):void 0)&&(p=o(y)?y:a(e[u+1])?[]:{})}r(f,d,p),f=f[d]}return t}},6560:(t,e,n)=>{var r=n(5703),i=n(8777),a=n(6557),o=i?function(t,e){return i(t,"toString",{configurable:!0,enumerable:!1,value:r(e),writable:!0})}:a;t.exports=o},1131:t=>{t.exports=function(t,e){var n=t.length;for(t.sort(e);n--;)t[n]=t[n].value;return t}},2545:t=>{t.exports=function(t,e){for(var n=-1,r=Array(t);++n<t;)r[n]=e(n);return r}},531:(t,e,n)=>{var r=n(2705),i=n(9932),a=n(1469),o=n(3448),s=r?r.prototype:void 0,c=s?s.toString:void 0;t.exports=function t(e){if("string"==typeof e)return e;if(a(e))return i(e,t)+"";if(o(e))return c?c.call(e):"";var n=e+"";return"0"==n&&1/e==-1/0?"-0":n}},7561:(t,e,n)=>{var r=n(7990),i=/^\s+/;t.exports=function(t){return t?t.slice(0,r(t)+1).replace(i,""):t}},1717:t=>{t.exports=function(t){return function(e){return t(e)}}},5652:(t,e,n)=>{var r=n(8668),i=n(7443),a=n(1196),o=n(4757),s=n(3593),c=n(1814);t.exports=function(t,e,n){var u=-1,l=i,h=t.length,f=!0,d=[],p=d;if(n)f=!1,l=a;else if(h>=200){var y=e?null:s(t);if(y)return c(y);f=!1,l=o,p=new r}else p=e?[]:d;t:for(;++u<h;){var g=t[u],m=e?e(g):g;if(g=n||0!==g?g:0,f&&m==m){for(var v=p.length;v--;)if(p[v]===m)continue t;e&&p.push(m),d.push(g)}else l(p,m,n)||(p!==d&&p.push(m),d.push(g))}return d}},7415:(t,e,n)=>{var r=n(9932);t.exports=function(t,e){return r(e,(function(e){return t[e]}))}},1757:t=>{t.exports=function(t,e,n){for(var r=-1,i=t.length,a=e.length,o={};++r<i;){var s=r<a?e[r]:void 0;n(o,t[r],s)}return o}},4757:t=>{t.exports=function(t,e){return t.has(e)}},4290:(t,e,n)=>{var r=n(6557);t.exports=function(t){return"function"==typeof t?t:r}},1811:(t,e,n)=>{var r=n(1469),i=n(5403),a=n(5514),o=n(9833);t.exports=function(t,e){return r(t)?t:i(t,e)?[t]:a(o(t))}},4318:(t,e,n)=>{var r=n(1149);t.exports=function(t){var e=new t.constructor(t.byteLength);return new r(e).set(new r(t)),e}},4626:(t,e,n)=>{t=n.nmd(t);var r=n(5639),i=e&&!e.nodeType&&e,a=i&&t&&!t.nodeType&&t,o=a&&a.exports===i?r.Buffer:void 0,s=o?o.allocUnsafe:void 0;t.exports=function(t,e){if(e)return t.slice();var n=t.length,r=s?s(n):new t.constructor(n);return t.copy(r),r}},7157:(t,e,n)=>{var r=n(4318);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}},3147:t=>{var e=/\w*$/;t.exports=function(t){var n=new t.constructor(t.source,e.exec(t));return n.lastIndex=t.lastIndex,n}},419:(t,e,n)=>{var r=n(2705),i=r?r.prototype:void 0,a=i?i.valueOf:void 0;t.exports=function(t){return a?Object(a.call(t)):{}}},7133:(t,e,n)=>{var r=n(4318);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.length)}},6393:(t,e,n)=>{var r=n(3448);t.exports=function(t,e){if(t!==e){var n=void 0!==t,i=null===t,a=t==t,o=r(t),s=void 0!==e,c=null===e,u=e==e,l=r(e);if(!c&&!l&&!o&&t>e||o&&s&&u&&!c&&!l||i&&s&&u||!n&&u||!a)return 1;if(!i&&!o&&!l&&t<e||l&&n&&a&&!i&&!o||c&&n&&a||!s&&a||!u)return-1}return 0}},5022:(t,e,n)=>{var r=n(6393);t.exports=function(t,e,n){for(var i=-1,a=t.criteria,o=e.criteria,s=a.length,c=n.length;++i<s;){var u=r(a[i],o[i]);if(u)return i>=c?u:u*("desc"==n[i]?-1:1)}return t.index-e.index}},278:t=>{t.exports=function(t,e){var n=-1,r=t.length;for(e||(e=Array(r));++n<r;)e[n]=t[n];return e}},8363:(t,e,n)=>{var r=n(4865),i=n(9465);t.exports=function(t,e,n,a){var o=!n;n||(n={});for(var s=-1,c=e.length;++s<c;){var u=e[s],l=a?a(n[u],t[u],u,n,t):void 0;void 0===l&&(l=t[u]),o?i(n,u,l):r(n,u,l)}return n}},8805:(t,e,n)=>{var r=n(8363),i=n(9551);t.exports=function(t,e){return r(t,i(t),e)}},1911:(t,e,n)=>{var r=n(8363),i=n(1442);t.exports=function(t,e){return r(t,i(t),e)}},4429:(t,e,n)=>{var r=n(5639)["__core-js_shared__"];t.exports=r},1750:(t,e,n)=>{var r=n(5976),i=n(6612);t.exports=function(t){return r((function(e,n){var r=-1,a=n.length,o=a>1?n[a-1]:void 0,s=a>2?n[2]:void 0;for(o=t.length>3&&"function"==typeof o?(a--,o):void 0,s&&i(n[0],n[1],s)&&(o=a<3?void 0:o,a=1),e=Object(e);++r<a;){var c=n[r];c&&t(e,c,r,o)}return e}))}},9291:(t,e,n)=>{var r=n(8612);t.exports=function(t,e){return function(n,i){if(null==n)return n;if(!r(n))return t(n,i);for(var a=n.length,o=e?a:-1,s=Object(n);(e?o--:++o<a)&&!1!==i(s[o],o,s););return n}}},5063:t=>{t.exports=function(t){return function(e,n,r){for(var i=-1,a=Object(e),o=r(e),s=o.length;s--;){var c=o[t?s:++i];if(!1===n(a[c],c,a))break}return e}}},7740:(t,e,n)=>{var r=n(7206),i=n(8612),a=n(3674);t.exports=function(t){return function(e,n,o){var s=Object(e);if(!i(e)){var c=r(n,3);e=a(e),n=function(t){return c(s[t],t,s)}}var u=t(e,n,o);return u>-1?s[c?e[u]:u]:void 0}}},7445:(t,e,n)=>{var r=n(98),i=n(6612),a=n(8601);t.exports=function(t){return function(e,n,o){return o&&"number"!=typeof o&&i(e,n,o)&&(n=o=void 0),e=a(e),void 0===n?(n=e,e=0):n=a(n),o=void 0===o?e<n?1:-1:a(o),r(e,n,o,t)}}},3593:(t,e,n)=>{var r=n(8525),i=n(308),a=n(1814),o=r&&1/a(new r([,-0]))[1]==1/0?function(t){return new r(t)}:i;t.exports=o},8777:(t,e,n)=>{var r=n(852),i=function(){try{var t=r(Object,"defineProperty");return t({},"",{}),t}catch(t){}}();t.exports=i},7114:(t,e,n)=>{var r=n(8668),i=n(2908),a=n(4757);t.exports=function(t,e,n,o,s,c){var u=1&n,l=t.length,h=e.length;if(l!=h&&!(u&&h>l))return!1;var f=c.get(t),d=c.get(e);if(f&&d)return f==e&&d==t;var p=-1,y=!0,g=2&n?new r:void 0;for(c.set(t,e),c.set(e,t);++p<l;){var m=t[p],v=e[p];if(o)var b=u?o(v,m,p,e,t,c):o(m,v,p,t,e,c);if(void 0!==b){if(b)continue;y=!1;break}if(g){if(!i(e,(function(t,e){if(!a(g,e)&&(m===t||s(m,t,n,o,c)))return g.push(e)}))){y=!1;break}}else if(m!==v&&!s(m,v,n,o,c)){y=!1;break}}return c.delete(t),c.delete(e),y}},8351:(t,e,n)=>{var r=n(2705),i=n(1149),a=n(7813),o=n(7114),s=n(8776),c=n(1814),u=r?r.prototype:void 0,l=u?u.valueOf:void 0;t.exports=function(t,e,n,r,u,h,f){switch(n){case"[object DataView]":if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case"[object ArrayBuffer]":return!(t.byteLength!=e.byteLength||!h(new i(t),new i(e)));case"[object Boolean]":case"[object Date]":case"[object Number]":return a(+t,+e);case"[object Error]":return t.name==e.name&&t.message==e.message;case"[object RegExp]":case"[object String]":return t==e+"";case"[object Map]":var d=s;case"[object Set]":var p=1&r;if(d||(d=c),t.size!=e.size&&!p)return!1;var y=f.get(t);if(y)return y==e;r|=2,f.set(t,e);var g=o(d(t),d(e),r,u,h,f);return f.delete(t),g;case"[object Symbol]":if(l)return l.call(t)==l.call(e)}return!1}},6096:(t,e,n)=>{var r=n(8234),i=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,a,o,s){var c=1&n,u=r(t),l=u.length;if(l!=r(e).length&&!c)return!1;for(var h=l;h--;){var f=u[h];if(!(c?f in e:i.call(e,f)))return!1}var d=s.get(t),p=s.get(e);if(d&&p)return d==e&&p==t;var y=!0;s.set(t,e),s.set(e,t);for(var g=c;++h<l;){var m=t[f=u[h]],v=e[f];if(a)var b=c?a(v,m,f,e,t,s):a(m,v,f,t,e,s);if(!(void 0===b?m===v||o(m,v,n,a,s):b)){y=!1;break}g||(g="constructor"==f)}if(y&&!g){var _=t.constructor,x=e.constructor;_==x||!("constructor"in t)||!("constructor"in e)||"function"==typeof _&&_ instanceof _&&"function"==typeof x&&x instanceof x||(y=!1)}return s.delete(t),s.delete(e),y}},9021:(t,e,n)=>{var r=n(5564),i=n(5357),a=n(61);t.exports=function(t){return a(i(t,void 0,r),t+"")}},1957:(t,e,n)=>{var r="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;t.exports=r},8234:(t,e,n)=>{var r=n(8866),i=n(9551),a=n(3674);t.exports=function(t){return r(t,a,i)}},6904:(t,e,n)=>{var r=n(8866),i=n(1442),a=n(1704);t.exports=function(t){return r(t,a,i)}},5050:(t,e,n)=>{var r=n(7019);t.exports=function(t,e){var n=t.__data__;return r(e)?n["string"==typeof e?"string":"hash"]:n.map}},1499:(t,e,n)=>{var r=n(9162),i=n(3674);t.exports=function(t){for(var e=i(t),n=e.length;n--;){var a=e[n],o=t[a];e[n]=[a,o,r(o)]}return e}},852:(t,e,n)=>{var r=n(8458),i=n(7801);t.exports=function(t,e){var n=i(t,e);return r(n)?n:void 0}},5924:(t,e,n)=>{var r=n(5569)(Object.getPrototypeOf,Object);t.exports=r},9607:(t,e,n)=>{var r=n(2705),i=Object.prototype,a=i.hasOwnProperty,o=i.toString,s=r?r.toStringTag:void 0;t.exports=function(t){var e=a.call(t,s),n=t[s];try{t[s]=void 0;var r=!0}catch(t){}var i=o.call(t);return r&&(e?t[s]=n:delete t[s]),i}},9551:(t,e,n)=>{var r=n(4963),i=n(479),a=Object.prototype.propertyIsEnumerable,o=Object.getOwnPropertySymbols,s=o?function(t){return null==t?[]:(t=Object(t),r(o(t),(function(e){return a.call(t,e)})))}:i;t.exports=s},1442:(t,e,n)=>{var r=n(2488),i=n(5924),a=n(9551),o=n(479),s=Object.getOwnPropertySymbols?function(t){for(var e=[];t;)r(e,a(t)),t=i(t);return e}:o;t.exports=s},4160:(t,e,n)=>{var r=n(8552),i=n(7071),a=n(3818),o=n(8525),s=n(577),c=n(4239),u=n(346),l="[object Map]",h="[object Promise]",f="[object Set]",d="[object WeakMap]",p="[object DataView]",y=u(r),g=u(i),m=u(a),v=u(o),b=u(s),_=c;(r&&_(new r(new ArrayBuffer(1)))!=p||i&&_(new i)!=l||a&&_(a.resolve())!=h||o&&_(new o)!=f||s&&_(new s)!=d)&&(_=function(t){var e=c(t),n="[object Object]"==e?t.constructor:void 0,r=n?u(n):"";if(r)switch(r){case y:return p;case g:return l;case m:return h;case v:return f;case b:return d}return e}),t.exports=_},7801:t=>{t.exports=function(t,e){return null==t?void 0:t[e]}},222:(t,e,n)=>{var r=n(1811),i=n(5694),a=n(1469),o=n(5776),s=n(1780),c=n(327);t.exports=function(t,e,n){for(var u=-1,l=(e=r(e,t)).length,h=!1;++u<l;){var f=c(e[u]);if(!(h=null!=t&&n(t,f)))break;t=t[f]}return h||++u!=l?h:!!(l=null==t?0:t.length)&&s(l)&&o(f,l)&&(a(t)||i(t))}},2689:t=>{var e=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");t.exports=function(t){return e.test(t)}},1789:(t,e,n)=>{var r=n(4536);t.exports=function(){this.__data__=r?r(null):{},this.size=0}},401:t=>{t.exports=function(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}},7667:(t,e,n)=>{var r=n(4536),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;if(r){var n=e[t];return"__lodash_hash_undefined__"===n?void 0:n}return i.call(e,t)?e[t]:void 0}},1327:(t,e,n)=>{var r=n(4536),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;return r?void 0!==e[t]:i.call(e,t)}},1866:(t,e,n)=>{var r=n(4536);t.exports=function(t,e){var n=this.__data__;return this.size+=this.has(t)?0:1,n[t]=r&&void 0===e?"__lodash_hash_undefined__":e,this}},3824:t=>{var e=Object.prototype.hasOwnProperty;t.exports=function(t){var n=t.length,r=new t.constructor(n);return n&&"string"==typeof t[0]&&e.call(t,"index")&&(r.index=t.index,r.input=t.input),r}},9148:(t,e,n)=>{var r=n(4318),i=n(7157),a=n(3147),o=n(419),s=n(7133);t.exports=function(t,e,n){var c=t.constructor;switch(e){case"[object ArrayBuffer]":return r(t);case"[object Boolean]":case"[object Date]":return new c(+t);case"[object DataView]":return i(t,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return s(t,n);case"[object Map]":case"[object Set]":return new c;case"[object Number]":case"[object String]":return new c(t);case"[object RegExp]":return a(t);case"[object Symbol]":return o(t)}}},8517:(t,e,n)=>{var r=n(3118),i=n(5924),a=n(5726);t.exports=function(t){return"function"!=typeof t.constructor||a(t)?{}:r(i(t))}},7285:(t,e,n)=>{var r=n(2705),i=n(5694),a=n(1469),o=r?r.isConcatSpreadable:void 0;t.exports=function(t){return a(t)||i(t)||!!(o&&t&&t[o])}},5776:t=>{var e=/^(?:0|[1-9]\d*)$/;t.exports=function(t,n){var r=typeof t;return!!(n=null==n?9007199254740991:n)&&("number"==r||"symbol"!=r&&e.test(t))&&t>-1&&t%1==0&&t<n}},6612:(t,e,n)=>{var r=n(7813),i=n(8612),a=n(5776),o=n(3218);t.exports=function(t,e,n){if(!o(n))return!1;var s=typeof e;return!!("number"==s?i(n)&&a(e,n.length):"string"==s&&e in n)&&r(n[e],t)}},5403:(t,e,n)=>{var r=n(1469),i=n(3448),a=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,o=/^\w*$/;t.exports=function(t,e){if(r(t))return!1;var n=typeof t;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=t&&!i(t))||o.test(t)||!a.test(t)||null!=e&&t in Object(e)}},7019:t=>{t.exports=function(t){var e=typeof t;return"string"==e||"number"==e||"symbol"==e||"boolean"==e?"__proto__"!==t:null===t}},5346:(t,e,n)=>{var r,i=n(4429),a=(r=/[^.]+$/.exec(i&&i.keys&&i.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";t.exports=function(t){return!!a&&a in t}},5726:t=>{var e=Object.prototype;t.exports=function(t){var n=t&&t.constructor;return t===("function"==typeof n&&n.prototype||e)}},9162:(t,e,n)=>{var r=n(3218);t.exports=function(t){return t==t&&!r(t)}},7040:t=>{t.exports=function(){this.__data__=[],this.size=0}},2188:(t,e,n)=>{var r=n(8470),i=Array.prototype.splice;t.exports=function(t){var e=this.__data__,n=r(e,t);return!(n<0||(n==e.length-1?e.pop():i.call(e,n,1),--this.size,0))}},2117:(t,e,n)=>{var r=n(8470);t.exports=function(t){var e=this.__data__,n=r(e,t);return n<0?void 0:e[n][1]}},7518:(t,e,n)=>{var r=n(8470);t.exports=function(t){return r(this.__data__,t)>-1}},4705:(t,e,n)=>{var r=n(8470);t.exports=function(t,e){var n=this.__data__,i=r(n,t);return i<0?(++this.size,n.push([t,e])):n[i][1]=e,this}},4785:(t,e,n)=>{var r=n(1989),i=n(8407),a=n(7071);t.exports=function(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}},1285:(t,e,n)=>{var r=n(5050);t.exports=function(t){var e=r(this,t).delete(t);return this.size-=e?1:0,e}},6e3:(t,e,n)=>{var r=n(5050);t.exports=function(t){return r(this,t).get(t)}},9916:(t,e,n)=>{var r=n(5050);t.exports=function(t){return r(this,t).has(t)}},5265:(t,e,n)=>{var r=n(5050);t.exports=function(t,e){var n=r(this,t),i=n.size;return n.set(t,e),this.size+=n.size==i?0:1,this}},8776:t=>{t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t,r){n[++e]=[r,t]})),n}},2634:t=>{t.exports=function(t,e){return function(n){return null!=n&&n[t]===e&&(void 0!==e||t in Object(n))}}},4523:(t,e,n)=>{var r=n(8306);t.exports=function(t){var e=r(t,(function(t){return 500===n.size&&n.clear(),t})),n=e.cache;return e}},4536:(t,e,n)=>{var r=n(852)(Object,"create");t.exports=r},6916:(t,e,n)=>{var r=n(5569)(Object.keys,Object);t.exports=r},3498:t=>{t.exports=function(t){var e=[];if(null!=t)for(var n in Object(t))e.push(n);return e}},1167:(t,e,n)=>{t=n.nmd(t);var r=n(1957),i=e&&!e.nodeType&&e,a=i&&t&&!t.nodeType&&t,o=a&&a.exports===i&&r.process,s=function(){try{return a&&a.require&&a.require("util").types||o&&o.binding&&o.binding("util")}catch(t){}}();t.exports=s},2333:t=>{var e=Object.prototype.toString;t.exports=function(t){return e.call(t)}},5569:t=>{t.exports=function(t,e){return function(n){return t(e(n))}}},5357:(t,e,n)=>{var r=n(6874),i=Math.max;t.exports=function(t,e,n){return e=i(void 0===e?t.length-1:e,0),function(){for(var a=arguments,o=-1,s=i(a.length-e,0),c=Array(s);++o<s;)c[o]=a[e+o];o=-1;for(var u=Array(e+1);++o<e;)u[o]=a[o];return u[e]=n(c),r(t,this,u)}}},5639:(t,e,n)=>{var r=n(1957),i="object"==typeof self&&self&&self.Object===Object&&self,a=r||i||Function("return this")();t.exports=a},6390:t=>{t.exports=function(t,e){if(("constructor"!==e||"function"!=typeof t[e])&&"__proto__"!=e)return t[e]}},619:t=>{t.exports=function(t){return this.__data__.set(t,"__lodash_hash_undefined__"),this}},2385:t=>{t.exports=function(t){return this.__data__.has(t)}},1814:t=>{t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t){n[++e]=t})),n}},61:(t,e,n)=>{var r=n(6560),i=n(1275)(r);t.exports=i},1275:t=>{var e=Date.now;t.exports=function(t){var n=0,r=0;return function(){var i=e(),a=16-(i-r);if(r=i,a>0){if(++n>=800)return arguments[0]}else n=0;return t.apply(void 0,arguments)}}},7465:(t,e,n)=>{var r=n(8407);t.exports=function(){this.__data__=new r,this.size=0}},3779:t=>{t.exports=function(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}},7599:t=>{t.exports=function(t){return this.__data__.get(t)}},4758:t=>{t.exports=function(t){return this.__data__.has(t)}},4309:(t,e,n)=>{var r=n(8407),i=n(7071),a=n(3369);t.exports=function(t,e){var n=this.__data__;if(n instanceof r){var o=n.__data__;if(!i||o.length<199)return o.push([t,e]),this.size=++n.size,this;n=this.__data__=new a(o)}return n.set(t,e),this.size=n.size,this}},2351:t=>{t.exports=function(t,e,n){for(var r=n-1,i=t.length;++r<i;)if(t[r]===e)return r;return-1}},8016:(t,e,n)=>{var r=n(8983),i=n(2689),a=n(1903);t.exports=function(t){return i(t)?a(t):r(t)}},5514:(t,e,n)=>{var r=n(4523),i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,a=/\\(\\)?/g,o=r((function(t){var e=[];return 46===t.charCodeAt(0)&&e.push(""),t.replace(i,(function(t,n,r,i){e.push(r?i.replace(a,"$1"):n||t)})),e}));t.exports=o},327:(t,e,n)=>{var r=n(3448);t.exports=function(t){if("string"==typeof t||r(t))return t;var e=t+"";return"0"==e&&1/t==-1/0?"-0":e}},346:t=>{var e=Function.prototype.toString;t.exports=function(t){if(null!=t){try{return e.call(t)}catch(t){}try{return t+""}catch(t){}}return""}},7990:t=>{var e=/\s/;t.exports=function(t){for(var n=t.length;n--&&e.test(t.charAt(n)););return n}},1903:t=>{var e="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",n="\\ud83c[\\udffb-\\udfff]",r="[^\\ud800-\\udfff]",i="(?:\\ud83c[\\udde6-\\uddff]){2}",a="[\\ud800-\\udbff][\\udc00-\\udfff]",o="(?:"+e+"|"+n+")?",s="[\\ufe0e\\ufe0f]?",c=s+o+"(?:\\u200d(?:"+[r,i,a].join("|")+")"+s+o+")*",u="(?:"+[r+e+"?",e,i,a,"[\\ud800-\\udfff]"].join("|")+")",l=RegExp(n+"(?="+n+")|"+u+c,"g");t.exports=function(t){for(var e=l.lastIndex=0;l.test(t);)++e;return e}},6678:(t,e,n)=>{var r=n(5990);t.exports=function(t){return r(t,4)}},361:(t,e,n)=>{var r=n(5990);t.exports=function(t){return r(t,5)}},5703:t=>{t.exports=function(t){return function(){return t}}},1747:(t,e,n)=>{var r=n(5976),i=n(7813),a=n(6612),o=n(1704),s=Object.prototype,c=s.hasOwnProperty,u=r((function(t,e){t=Object(t);var n=-1,r=e.length,u=r>2?e[2]:void 0;for(u&&a(e[0],e[1],u)&&(r=1);++n<r;)for(var l=e[n],h=o(l),f=-1,d=h.length;++f<d;){var p=h[f],y=t[p];(void 0===y||i(y,s[p])&&!c.call(t,p))&&(t[p]=l[p])}return t}));t.exports=u},6073:(t,e,n)=>{t.exports=n(4486)},7813:t=>{t.exports=function(t,e){return t===e||t!=t&&e!=e}},3105:(t,e,n)=>{var r=n(4963),i=n(760),a=n(7206),o=n(1469);t.exports=function(t,e){return(o(t)?r:i)(t,a(e,3))}},3311:(t,e,n)=>{var r=n(7740)(n(998));t.exports=r},998:(t,e,n)=>{var r=n(1848),i=n(7206),a=n(554),o=Math.max;t.exports=function(t,e,n){var s=null==t?0:t.length;if(!s)return-1;var c=null==n?0:a(n);return c<0&&(c=o(s+c,0)),r(t,i(e,3),c)}},5564:(t,e,n)=>{var r=n(1078);t.exports=function(t){return null!=t&&t.length?r(t,1):[]}},4486:(t,e,n)=>{var r=n(7412),i=n(9881),a=n(4290),o=n(1469);t.exports=function(t,e){return(o(t)?r:i)(t,a(e))}},2620:(t,e,n)=>{var r=n(8483),i=n(4290),a=n(1704);t.exports=function(t,e){return null==t?t:r(t,i(e),a)}},1917:(t,e,n)=>{var r=n(7786);t.exports=function(t,e,n){var i=null==t?void 0:r(t,e);return void 0===i?n:i}},8721:(t,e,n)=>{var r=n(8565),i=n(222);t.exports=function(t,e){return null!=t&&i(t,e,r)}},9095:(t,e,n)=>{var r=n(13),i=n(222);t.exports=function(t,e){return null!=t&&i(t,e,r)}},6557:t=>{t.exports=function(t){return t}},5694:(t,e,n)=>{var r=n(9454),i=n(7005),a=Object.prototype,o=a.hasOwnProperty,s=a.propertyIsEnumerable,c=r(function(){return arguments}())?r:function(t){return i(t)&&o.call(t,"callee")&&!s.call(t,"callee")};t.exports=c},1469:t=>{var e=Array.isArray;t.exports=e},8612:(t,e,n)=>{var r=n(3560),i=n(1780);t.exports=function(t){return null!=t&&i(t.length)&&!r(t)}},9246:(t,e,n)=>{var r=n(8612),i=n(7005);t.exports=function(t){return i(t)&&r(t)}},4144:(t,e,n)=>{t=n.nmd(t);var r=n(5639),i=n(5062),a=e&&!e.nodeType&&e,o=a&&t&&!t.nodeType&&t,s=o&&o.exports===a?r.Buffer:void 0,c=(s?s.isBuffer:void 0)||i;t.exports=c},1609:(t,e,n)=>{var r=n(280),i=n(4160),a=n(5694),o=n(1469),s=n(8612),c=n(4144),u=n(5726),l=n(6719),h=Object.prototype.hasOwnProperty;t.exports=function(t){if(null==t)return!0;if(s(t)&&(o(t)||"string"==typeof t||"function"==typeof t.splice||c(t)||l(t)||a(t)))return!t.length;var e=i(t);if("[object Map]"==e||"[object Set]"==e)return!t.size;if(u(t))return!r(t).length;for(var n in t)if(h.call(t,n))return!1;return!0}},3560:(t,e,n)=>{var r=n(4239),i=n(3218);t.exports=function(t){if(!i(t))return!1;var e=r(t);return"[object Function]"==e||"[object GeneratorFunction]"==e||"[object AsyncFunction]"==e||"[object Proxy]"==e}},1780:t=>{t.exports=function(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=9007199254740991}},6688:(t,e,n)=>{var r=n(5588),i=n(1717),a=n(1167),o=a&&a.isMap,s=o?i(o):r;t.exports=s},3218:t=>{t.exports=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}},7005:t=>{t.exports=function(t){return null!=t&&"object"==typeof t}},8630:(t,e,n)=>{var r=n(4239),i=n(5924),a=n(7005),o=Function.prototype,s=Object.prototype,c=o.toString,u=s.hasOwnProperty,l=c.call(Object);t.exports=function(t){if(!a(t)||"[object Object]"!=r(t))return!1;var e=i(t);if(null===e)return!0;var n=u.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&c.call(n)==l}},2928:(t,e,n)=>{var r=n(9221),i=n(1717),a=n(1167),o=a&&a.isSet,s=o?i(o):r;t.exports=s},7037:(t,e,n)=>{var r=n(4239),i=n(1469),a=n(7005);t.exports=function(t){return"string"==typeof t||!i(t)&&a(t)&&"[object String]"==r(t)}},3448:(t,e,n)=>{var r=n(4239),i=n(7005);t.exports=function(t){return"symbol"==typeof t||i(t)&&"[object Symbol]"==r(t)}},6719:(t,e,n)=>{var r=n(8749),i=n(1717),a=n(1167),o=a&&a.isTypedArray,s=o?i(o):r;t.exports=s},2353:t=>{t.exports=function(t){return void 0===t}},3674:(t,e,n)=>{var r=n(4636),i=n(280),a=n(8612);t.exports=function(t){return a(t)?r(t):i(t)}},1704:(t,e,n)=>{var r=n(4636),i=n(313),a=n(8612);t.exports=function(t){return a(t)?r(t,!0):i(t)}},928:t=>{t.exports=function(t){var e=null==t?0:t.length;return e?t[e-1]:void 0}},5161:(t,e,n)=>{var r=n(9932),i=n(7206),a=n(9199),o=n(1469);t.exports=function(t,e){return(o(t)?r:a)(t,i(e,3))}},6604:(t,e,n)=>{var r=n(9465),i=n(7816),a=n(7206);t.exports=function(t,e){var n={};return e=a(e,3),i(t,(function(t,i,a){r(n,i,e(t,i,a))})),n}},6162:(t,e,n)=>{var r=n(6029),i=n(3325),a=n(6557);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},8306:(t,e,n)=>{var r=n(3369);function i(t,e){if("function"!=typeof t||null!=e&&"function"!=typeof e)throw new TypeError("Expected a function");var n=function(){var r=arguments,i=e?e.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=t.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(i.Cache||r),n}i.Cache=r,t.exports=i},3857:(t,e,n)=>{var r=n(2980),i=n(1750)((function(t,e,n){r(t,e,n)}));t.exports=i},3632:(t,e,n)=>{var r=n(6029),i=n(433),a=n(6557);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},2762:(t,e,n)=>{var r=n(6029),i=n(7206),a=n(433);t.exports=function(t,e){return t&&t.length?r(t,i(e,2),a):void 0}},308:t=>{t.exports=function(){}},7771:(t,e,n)=>{var r=n(5639);t.exports=function(){return r.Date.now()}},9722:(t,e,n)=>{var r=n(5970),i=n(9021)((function(t,e){return null==t?{}:r(t,e)}));t.exports=i},9601:(t,e,n)=>{var r=n(371),i=n(9152),a=n(5403),o=n(327);t.exports=function(t){return a(t)?r(o(t)):i(t)}},6026:(t,e,n)=>{var r=n(7445)();t.exports=r},4061:(t,e,n)=>{var r=n(2663),i=n(9881),a=n(7206),o=n(107),s=n(1469);t.exports=function(t,e,n){var c=s(t)?r:o,u=arguments.length<3;return c(t,a(e,4),n,u,i)}},4238:(t,e,n)=>{var r=n(280),i=n(4160),a=n(8612),o=n(7037),s=n(8016);t.exports=function(t){if(null==t)return 0;if(a(t))return o(t)?s(t):t.length;var e=i(t);return"[object Map]"==e||"[object Set]"==e?t.size:r(t).length}},9734:(t,e,n)=>{var r=n(1078),i=n(9556),a=n(5976),o=n(6612),s=a((function(t,e){if(null==t)return[];var n=e.length;return n>1&&o(t,e[0],e[1])?e=[]:n>2&&o(e[0],e[1],e[2])&&(e=[e[0]]),i(t,r(e,1),[])}));t.exports=s},479:t=>{t.exports=function(){return[]}},5062:t=>{t.exports=function(){return!1}},8601:(t,e,n)=>{var r=n(4841);t.exports=function(t){return t?Infinity===(t=r(t))||t===-1/0?17976931348623157e292*(t<0?-1:1):t==t?t:0:0===t?t:0}},554:(t,e,n)=>{var r=n(8601);t.exports=function(t){var e=r(t),n=e%1;return e==e?n?e-n:e:0}},4841:(t,e,n)=>{var r=n(7561),i=n(3218),a=n(3448),o=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,c=/^0o[0-7]+$/i,u=parseInt;t.exports=function(t){if("number"==typeof t)return t;if(a(t))return NaN;if(i(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=i(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=r(t);var n=s.test(t);return n||c.test(t)?u(t.slice(2),n?2:8):o.test(t)?NaN:+t}},3678:(t,e,n)=>{var r=n(8363),i=n(1704);t.exports=function(t){return r(t,i(t))}},9833:(t,e,n)=>{var r=n(531);t.exports=function(t){return null==t?"":r(t)}},8718:(t,e,n)=>{var r=n(7412),i=n(3118),a=n(7816),o=n(7206),s=n(5924),c=n(1469),u=n(4144),l=n(3560),h=n(3218),f=n(6719);t.exports=function(t,e,n){var d=c(t),p=d||u(t)||f(t);if(e=o(e,4),null==n){var y=t&&t.constructor;n=p?d?new y:[]:h(t)&&l(y)?i(s(t)):{}}return(p?r:a)(t,(function(t,r,i){return e(n,t,r,i)})),n}},3386:(t,e,n)=>{var r=n(1078),i=n(5976),a=n(5652),o=n(9246),s=i((function(t){return a(r(t,1,o,!0))}));t.exports=s},3955:(t,e,n)=>{var r=n(9833),i=0;t.exports=function(t){var e=++i;return r(t)+e}},2628:(t,e,n)=>{var r=n(7415),i=n(3674);t.exports=function(t){return null==t?[]:r(t,i(t))}},7287:(t,e,n)=>{var r=n(4865),i=n(1757);t.exports=function(t,e){return i(t||[],e||[],r)}},9234:()=>{},1748:(t,e,n)=>{var r={"./locale":9234,"./locale.js":9234};function i(t){var e=a(t);return n(e)}function a(t){if(!n.o(r,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}return r[t]}i.keys=function(){return Object.keys(r)},i.resolve=a,t.exports=i,i.id=1748},1941:function(t,e,n){(t=n.nmd(t)).exports=function(){"use strict";var e,r;function i(){return e.apply(null,arguments)}function a(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function o(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function s(t){return void 0===t}function c(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function u(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function l(t,e){var n,r=[];for(n=0;n<t.length;++n)r.push(e(t[n],n));return r}function h(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function f(t,e){for(var n in e)h(e,n)&&(t[n]=e[n]);return h(e,"toString")&&(t.toString=e.toString),h(e,"valueOf")&&(t.valueOf=e.valueOf),t}function d(t,e,n,r){return be(t,e,n,r,!0).utc()}function p(t){return null==t._pf&&(t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),t._pf}function y(t){if(null==t._isValid){var e=p(t),n=r.call(e.parsedDateParts,(function(t){return null!=t})),i=!isNaN(t._d.getTime())&&e.overflow<0&&!e.empty&&!e.invalidMonth&&!e.invalidWeekday&&!e.weekdayMismatch&&!e.nullInput&&!e.invalidFormat&&!e.userInvalidated&&(!e.meridiem||e.meridiem&&n);if(t._strict&&(i=i&&0===e.charsLeftOver&&0===e.unusedTokens.length&&void 0===e.bigHour),null!=Object.isFrozen&&Object.isFrozen(t))return i;t._isValid=i}return t._isValid}function g(t){var e=d(NaN);return null!=t?f(p(e),t):p(e).userInvalidated=!0,e}r=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,r=0;r<n;r++)if(r in e&&t.call(this,e[r],r,e))return!0;return!1};var m=i.momentProperties=[];function v(t,e){var n,r,i;if(s(e._isAMomentObject)||(t._isAMomentObject=e._isAMomentObject),s(e._i)||(t._i=e._i),s(e._f)||(t._f=e._f),s(e._l)||(t._l=e._l),s(e._strict)||(t._strict=e._strict),s(e._tzm)||(t._tzm=e._tzm),s(e._isUTC)||(t._isUTC=e._isUTC),s(e._offset)||(t._offset=e._offset),s(e._pf)||(t._pf=p(e)),s(e._locale)||(t._locale=e._locale),0<m.length)for(n=0;n<m.length;n++)s(i=e[r=m[n]])||(t[r]=i);return t}var b=!1;function _(t){v(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===b&&(b=!0,i.updateOffset(this),b=!1)}function x(t){return t instanceof _||null!=t&&null!=t._isAMomentObject}function w(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function k(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=w(e)),n}function T(t,e,n){var r,i=Math.min(t.length,e.length),a=Math.abs(t.length-e.length),o=0;for(r=0;r<i;r++)(n&&t[r]!==e[r]||!n&&k(t[r])!==k(e[r]))&&o++;return o+a}function E(t){!1===i.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function C(t,e){var n=!0;return f((function(){if(null!=i.deprecationHandler&&i.deprecationHandler(null,t),n){for(var r,a=[],o=0;o<arguments.length;o++){if(r="","object"==typeof arguments[o]){for(var s in r+="\n["+o+"] ",arguments[0])r+=s+": "+arguments[0][s]+", ";r=r.slice(0,-2)}else r=arguments[o];a.push(r)}E(t+"\nArguments: "+Array.prototype.slice.call(a).join("")+"\n"+(new Error).stack),n=!1}return e.apply(this,arguments)}),e)}var S,A={};function M(t,e){null!=i.deprecationHandler&&i.deprecationHandler(t,e),A[t]||(E(e),A[t]=!0)}function N(t){return t instanceof Function||"[object Function]"===Object.prototype.toString.call(t)}function D(t,e){var n,r=f({},t);for(n in e)h(e,n)&&(o(t[n])&&o(e[n])?(r[n]={},f(r[n],t[n]),f(r[n],e[n])):null!=e[n]?r[n]=e[n]:delete r[n]);for(n in t)h(t,n)&&!h(e,n)&&o(t[n])&&(r[n]=f({},r[n]));return r}function O(t){null!=t&&this.set(t)}i.suppressDeprecationWarnings=!1,i.deprecationHandler=null,S=Object.keys?Object.keys:function(t){var e,n=[];for(e in t)h(t,e)&&n.push(e);return n};var B={};function L(t,e){var n=t.toLowerCase();B[n]=B[n+"s"]=B[e]=t}function I(t){return"string"==typeof t?B[t]||B[t.toLowerCase()]:void 0}function R(t){var e,n,r={};for(n in t)h(t,n)&&(e=I(n))&&(r[e]=t[n]);return r}var F={};function P(t,e){F[t]=e}function j(t,e,n){var r=""+Math.abs(t),i=e-r.length;return(0<=t?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var Y=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,z=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,U={},q={};function H(t,e,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),t&&(q[t]=i),e&&(q[e[0]]=function(){return j(i.apply(this,arguments),e[1],e[2])}),n&&(q[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),t)})}function $(t,e){return t.isValid()?(e=W(e,t.localeData()),U[e]=U[e]||function(t){var e,n,r,i=t.match(Y);for(e=0,n=i.length;e<n;e++)q[i[e]]?i[e]=q[i[e]]:i[e]=(r=i[e]).match(/\[[\s\S]/)?r.replace(/^\[|\]$/g,""):r.replace(/\\/g,"");return function(e){var r,a="";for(r=0;r<n;r++)a+=N(i[r])?i[r].call(e,t):i[r];return a}}(e),U[e](t)):t.localeData().invalidDate()}function W(t,e){var n=5;function r(t){return e.longDateFormat(t)||t}for(z.lastIndex=0;0<=n&&z.test(t);)t=t.replace(z,r),z.lastIndex=0,n-=1;return t}var V=/\d/,G=/\d\d/,X=/\d{3}/,Z=/\d{4}/,Q=/[+-]?\d{6}/,K=/\d\d?/,J=/\d\d\d\d?/,tt=/\d\d\d\d\d\d?/,et=/\d{1,3}/,nt=/\d{1,4}/,rt=/[+-]?\d{1,6}/,it=/\d+/,at=/[+-]?\d+/,ot=/Z|[+-]\d\d:?\d\d/gi,st=/Z|[+-]\d\d(?::?\d\d)?/gi,ct=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ut={};function lt(t,e,n){ut[t]=N(e)?e:function(t,r){return t&&n?n:e}}function ht(t,e){return h(ut,t)?ut[t](e._strict,e._locale):new RegExp(ft(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(t,e,n,r,i){return e||n||r||i}))))}function ft(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var dt={};function pt(t,e){var n,r=e;for("string"==typeof t&&(t=[t]),c(e)&&(r=function(t,n){n[e]=k(t)}),n=0;n<t.length;n++)dt[t[n]]=r}function yt(t,e){pt(t,(function(t,n,r,i){r._w=r._w||{},e(t,r._w,r,i)}))}function gt(t){return mt(t)?366:365}function mt(t){return t%4==0&&t%100!=0||t%400==0}H("Y",0,0,(function(){var t=this.year();return t<=9999?""+t:"+"+t})),H(0,["YY",2],0,(function(){return this.year()%100})),H(0,["YYYY",4],0,"year"),H(0,["YYYYY",5],0,"year"),H(0,["YYYYYY",6,!0],0,"year"),L("year","y"),P("year",1),lt("Y",at),lt("YY",K,G),lt("YYYY",nt,Z),lt("YYYYY",rt,Q),lt("YYYYYY",rt,Q),pt(["YYYYY","YYYYYY"],0),pt("YYYY",(function(t,e){e[0]=2===t.length?i.parseTwoDigitYear(t):k(t)})),pt("YY",(function(t,e){e[0]=i.parseTwoDigitYear(t)})),pt("Y",(function(t,e){e[0]=parseInt(t,10)})),i.parseTwoDigitYear=function(t){return k(t)+(68<k(t)?1900:2e3)};var vt,bt=_t("FullYear",!0);function _t(t,e){return function(n){return null!=n?(wt(this,t,n),i.updateOffset(this,e),this):xt(this,t)}}function xt(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function wt(t,e,n){t.isValid()&&!isNaN(n)&&("FullYear"===e&&mt(t.year())&&1===t.month()&&29===t.date()?t._d["set"+(t._isUTC?"UTC":"")+e](n,t.month(),kt(n,t.month())):t._d["set"+(t._isUTC?"UTC":"")+e](n))}function kt(t,e){if(isNaN(t)||isNaN(e))return NaN;var n=(e%12+12)%12;return t+=(e-n)/12,1===n?mt(t)?29:28:31-n%7%2}vt=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var e;for(e=0;e<this.length;++e)if(this[e]===t)return e;return-1},H("M",["MM",2],"Mo",(function(){return this.month()+1})),H("MMM",0,0,(function(t){return this.localeData().monthsShort(this,t)})),H("MMMM",0,0,(function(t){return this.localeData().months(this,t)})),L("month","M"),P("month",8),lt("M",K),lt("MM",K,G),lt("MMM",(function(t,e){return e.monthsShortRegex(t)})),lt("MMMM",(function(t,e){return e.monthsRegex(t)})),pt(["M","MM"],(function(t,e){e[1]=k(t)-1})),pt(["MMM","MMMM"],(function(t,e,n,r){var i=n._locale.monthsParse(t,r,n._strict);null!=i?e[1]=i:p(n).invalidMonth=t}));var Tt=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Et="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Ct="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function St(t,e){var n;if(!t.isValid())return t;if("string"==typeof e)if(/^\d+$/.test(e))e=k(e);else if(!c(e=t.localeData().monthsParse(e)))return t;return n=Math.min(t.date(),kt(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function At(t){return null!=t?(St(this,t),i.updateOffset(this,!0),this):xt(this,"Month")}var Mt=ct,Nt=ct;function Dt(){function t(t,e){return e.length-t.length}var e,n,r=[],i=[],a=[];for(e=0;e<12;e++)n=d([2e3,e]),r.push(this.monthsShort(n,"")),i.push(this.months(n,"")),a.push(this.months(n,"")),a.push(this.monthsShort(n,""));for(r.sort(t),i.sort(t),a.sort(t),e=0;e<12;e++)r[e]=ft(r[e]),i[e]=ft(i[e]);for(e=0;e<24;e++)a[e]=ft(a[e]);this._monthsRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+r.join("|")+")","i")}function Ot(t){var e;if(t<100&&0<=t){var n=Array.prototype.slice.call(arguments);n[0]=t+400,e=new Date(Date.UTC.apply(null,n)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)}else e=new Date(Date.UTC.apply(null,arguments));return e}function Bt(t,e,n){var r=7+e-n;return-(7+Ot(t,0,r).getUTCDay()-e)%7+r-1}function Lt(t,e,n,r,i){var a,o,s=1+7*(e-1)+(7+n-r)%7+Bt(t,r,i);return o=s<=0?gt(a=t-1)+s:s>gt(t)?(a=t+1,s-gt(t)):(a=t,s),{year:a,dayOfYear:o}}function It(t,e,n){var r,i,a=Bt(t.year(),e,n),o=Math.floor((t.dayOfYear()-a-1)/7)+1;return o<1?r=o+Rt(i=t.year()-1,e,n):o>Rt(t.year(),e,n)?(r=o-Rt(t.year(),e,n),i=t.year()+1):(i=t.year(),r=o),{week:r,year:i}}function Rt(t,e,n){var r=Bt(t,e,n),i=Bt(t+1,e,n);return(gt(t)-r+i)/7}function Ft(t,e){return t.slice(e,7).concat(t.slice(0,e))}H("w",["ww",2],"wo","week"),H("W",["WW",2],"Wo","isoWeek"),L("week","w"),L("isoWeek","W"),P("week",5),P("isoWeek",5),lt("w",K),lt("ww",K,G),lt("W",K),lt("WW",K,G),yt(["w","ww","W","WW"],(function(t,e,n,r){e[r.substr(0,1)]=k(t)})),H("d",0,"do","day"),H("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),H("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),H("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),H("e",0,0,"weekday"),H("E",0,0,"isoWeekday"),L("day","d"),L("weekday","e"),L("isoWeekday","E"),P("day",11),P("weekday",11),P("isoWeekday",11),lt("d",K),lt("e",K),lt("E",K),lt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),lt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),lt("dddd",(function(t,e){return e.weekdaysRegex(t)})),yt(["dd","ddd","dddd"],(function(t,e,n,r){var i=n._locale.weekdaysParse(t,r,n._strict);null!=i?e.d=i:p(n).invalidWeekday=t})),yt(["d","e","E"],(function(t,e,n,r){e[r]=k(t)}));var Pt="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),jt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Yt="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),zt=ct,Ut=ct,qt=ct;function Ht(){function t(t,e){return e.length-t.length}var e,n,r,i,a,o=[],s=[],c=[],u=[];for(e=0;e<7;e++)n=d([2e3,1]).day(e),r=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),a=this.weekdays(n,""),o.push(r),s.push(i),c.push(a),u.push(r),u.push(i),u.push(a);for(o.sort(t),s.sort(t),c.sort(t),u.sort(t),e=0;e<7;e++)s[e]=ft(s[e]),c[e]=ft(c[e]),u[e]=ft(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+c.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function $t(){return this.hours()%12||12}function Wt(t,e){H(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function Vt(t,e){return e._meridiemParse}H("H",["HH",2],0,"hour"),H("h",["hh",2],0,$t),H("k",["kk",2],0,(function(){return this.hours()||24})),H("hmm",0,0,(function(){return""+$t.apply(this)+j(this.minutes(),2)})),H("hmmss",0,0,(function(){return""+$t.apply(this)+j(this.minutes(),2)+j(this.seconds(),2)})),H("Hmm",0,0,(function(){return""+this.hours()+j(this.minutes(),2)})),H("Hmmss",0,0,(function(){return""+this.hours()+j(this.minutes(),2)+j(this.seconds(),2)})),Wt("a",!0),Wt("A",!1),L("hour","h"),P("hour",13),lt("a",Vt),lt("A",Vt),lt("H",K),lt("h",K),lt("k",K),lt("HH",K,G),lt("hh",K,G),lt("kk",K,G),lt("hmm",J),lt("hmmss",tt),lt("Hmm",J),lt("Hmmss",tt),pt(["H","HH"],3),pt(["k","kk"],(function(t,e,n){var r=k(t);e[3]=24===r?0:r})),pt(["a","A"],(function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t})),pt(["h","hh"],(function(t,e,n){e[3]=k(t),p(n).bigHour=!0})),pt("hmm",(function(t,e,n){var r=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r)),p(n).bigHour=!0})),pt("hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r,2)),e[5]=k(t.substr(i)),p(n).bigHour=!0})),pt("Hmm",(function(t,e,n){var r=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r))})),pt("Hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=k(t.substr(0,r)),e[4]=k(t.substr(r,2)),e[5]=k(t.substr(i))}));var Gt,Xt=_t("Hours",!0),Zt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Et,monthsShort:Ct,week:{dow:0,doy:6},weekdays:Pt,weekdaysMin:Yt,weekdaysShort:jt,meridiemParse:/[ap]\.?m?\.?/i},Qt={},Kt={};function Jt(t){return t?t.toLowerCase().replace("_","-"):t}function te(e){var r=null;if(!Qt[e]&&t&&t.exports)try{r=Gt._abbr,n(1748)("./"+e),ee(r)}catch(e){}return Qt[e]}function ee(t,e){var n;return t&&((n=s(e)?re(t):ne(t,e))?Gt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),Gt._abbr}function ne(t,e){if(null===e)return delete Qt[t],null;var n,r=Zt;if(e.abbr=t,null!=Qt[t])M("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=Qt[t]._config;else if(null!=e.parentLocale)if(null!=Qt[e.parentLocale])r=Qt[e.parentLocale]._config;else{if(null==(n=te(e.parentLocale)))return Kt[e.parentLocale]||(Kt[e.parentLocale]=[]),Kt[e.parentLocale].push({name:t,config:e}),null;r=n._config}return Qt[t]=new O(D(r,e)),Kt[t]&&Kt[t].forEach((function(t){ne(t.name,t.config)})),ee(t),Qt[t]}function re(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Gt;if(!a(t)){if(e=te(t))return e;t=[t]}return function(t){for(var e,n,r,i,a=0;a<t.length;){for(e=(i=Jt(t[a]).split("-")).length,n=(n=Jt(t[a+1]))?n.split("-"):null;0<e;){if(r=te(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&T(i,n,!0)>=e-1)break;e--}a++}return Gt}(t)}function ie(t){var e,n=t._a;return n&&-2===p(t).overflow&&(e=n[1]<0||11<n[1]?1:n[2]<1||n[2]>kt(n[0],n[1])?2:n[3]<0||24<n[3]||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||59<n[4]?4:n[5]<0||59<n[5]?5:n[6]<0||999<n[6]?6:-1,p(t)._overflowDayOfYear&&(e<0||2<e)&&(e=2),p(t)._overflowWeeks&&-1===e&&(e=7),p(t)._overflowWeekday&&-1===e&&(e=8),p(t).overflow=e),t}function ae(t,e,n){return null!=t?t:null!=e?e:n}function oe(t){var e,n,r,a,o,s=[];if(!t._d){var c,u;for(c=t,u=new Date(i.now()),r=c._useUTC?[u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()]:[u.getFullYear(),u.getMonth(),u.getDate()],t._w&&null==t._a[2]&&null==t._a[1]&&function(t){var e,n,r,i,a,o,s,c;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)a=1,o=4,n=ae(e.GG,t._a[0],It(_e(),1,4).year),r=ae(e.W,1),((i=ae(e.E,1))<1||7<i)&&(c=!0);else{a=t._locale._week.dow,o=t._locale._week.doy;var u=It(_e(),a,o);n=ae(e.gg,t._a[0],u.year),r=ae(e.w,u.week),null!=e.d?((i=e.d)<0||6<i)&&(c=!0):null!=e.e?(i=e.e+a,(e.e<0||6<e.e)&&(c=!0)):i=a}r<1||r>Rt(n,a,o)?p(t)._overflowWeeks=!0:null!=c?p(t)._overflowWeekday=!0:(s=Lt(n,r,i,a,o),t._a[0]=s.year,t._dayOfYear=s.dayOfYear)}(t),null!=t._dayOfYear&&(o=ae(t._a[0],r[0]),(t._dayOfYear>gt(o)||0===t._dayOfYear)&&(p(t)._overflowDayOfYear=!0),n=Ot(o,0,t._dayOfYear),t._a[1]=n.getUTCMonth(),t._a[2]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=r[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[3]&&0===t._a[4]&&0===t._a[5]&&0===t._a[6]&&(t._nextDay=!0,t._a[3]=0),t._d=(t._useUTC?Ot:function(t,e,n,r,i,a,o){var s;return t<100&&0<=t?(s=new Date(t+400,e,n,r,i,a,o),isFinite(s.getFullYear())&&s.setFullYear(t)):s=new Date(t,e,n,r,i,a,o),s}).apply(null,s),a=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[3]=24),t._w&&void 0!==t._w.d&&t._w.d!==a&&(p(t).weekdayMismatch=!0)}}var se=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ce=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ue=/Z|[+-]\d\d(?::?\d\d)?/,le=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],he=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],fe=/^\/?Date\((\-?\d+)/i;function de(t){var e,n,r,i,a,o,s=t._i,c=se.exec(s)||ce.exec(s);if(c){for(p(t).iso=!0,e=0,n=le.length;e<n;e++)if(le[e][1].exec(c[1])){i=le[e][0],r=!1!==le[e][2];break}if(null==i)return void(t._isValid=!1);if(c[3]){for(e=0,n=he.length;e<n;e++)if(he[e][1].exec(c[3])){a=(c[2]||" ")+he[e][0];break}if(null==a)return void(t._isValid=!1)}if(!r&&null!=a)return void(t._isValid=!1);if(c[4]){if(!ue.exec(c[4]))return void(t._isValid=!1);o="Z"}t._f=i+(a||"")+(o||""),me(t)}else t._isValid=!1}var pe=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;var ye={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function ge(t){var e,n,r,i=pe.exec(t._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(i){var a=function(t,e,n,r,i,a){var o=[function(t){var e=parseInt(t,10);return e<=49?2e3+e:e<=999?1900+e:e}(t),Ct.indexOf(e),parseInt(n,10),parseInt(r,10),parseInt(i,10)];return a&&o.push(parseInt(a,10)),o}(i[4],i[3],i[2],i[5],i[6],i[7]);if(n=a,r=t,(e=i[1])&&jt.indexOf(e)!==new Date(n[0],n[1],n[2]).getDay()&&(p(r).weekdayMismatch=!0,!(r._isValid=!1)))return;t._a=a,t._tzm=function(t,e,n){if(t)return ye[t];if(e)return 0;var r=parseInt(n,10),i=r%100;return(r-i)/100*60+i}(i[8],i[9],i[10]),t._d=Ot.apply(null,t._a),t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),p(t).rfc2822=!0}else t._isValid=!1}function me(t){if(t._f!==i.ISO_8601)if(t._f!==i.RFC_2822){t._a=[],p(t).empty=!0;var e,n,r,a,o,s,c,u,l=""+t._i,f=l.length,d=0;for(r=W(t._f,t._locale).match(Y)||[],e=0;e<r.length;e++)a=r[e],(n=(l.match(ht(a,t))||[])[0])&&(0<(o=l.substr(0,l.indexOf(n))).length&&p(t).unusedInput.push(o),l=l.slice(l.indexOf(n)+n.length),d+=n.length),q[a]?(n?p(t).empty=!1:p(t).unusedTokens.push(a),s=a,u=t,null!=(c=n)&&h(dt,s)&&dt[s](c,u._a,u,s)):t._strict&&!n&&p(t).unusedTokens.push(a);p(t).charsLeftOver=f-d,0<l.length&&p(t).unusedInput.push(l),t._a[3]<=12&&!0===p(t).bigHour&&0<t._a[3]&&(p(t).bigHour=void 0),p(t).parsedDateParts=t._a.slice(0),p(t).meridiem=t._meridiem,t._a[3]=function(t,e,n){var r;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):(null!=t.isPM&&((r=t.isPM(n))&&e<12&&(e+=12),r||12!==e||(e=0)),e)}(t._locale,t._a[3],t._meridiem),oe(t),ie(t)}else ge(t);else de(t)}function ve(t){var e,n,r,h,d=t._i,m=t._f;return t._locale=t._locale||re(t._l),null===d||void 0===m&&""===d?g({nullInput:!0}):("string"==typeof d&&(t._i=d=t._locale.preparse(d)),x(d)?new _(ie(d)):(u(d)?t._d=d:a(m)?function(t){var e,n,r,i,a;if(0===t._f.length)return p(t).invalidFormat=!0,t._d=new Date(NaN);for(i=0;i<t._f.length;i++)a=0,e=v({},t),null!=t._useUTC&&(e._useUTC=t._useUTC),e._f=t._f[i],me(e),y(e)&&(a+=p(e).charsLeftOver,a+=10*p(e).unusedTokens.length,p(e).score=a,(null==r||a<r)&&(r=a,n=e));f(t,n||e)}(t):m?me(t):s(n=(e=t)._i)?e._d=new Date(i.now()):u(n)?e._d=new Date(n.valueOf()):"string"==typeof n?(r=e,null===(h=fe.exec(r._i))?(de(r),!1===r._isValid&&(delete r._isValid,ge(r),!1===r._isValid&&(delete r._isValid,i.createFromInputFallback(r)))):r._d=new Date(+h[1])):a(n)?(e._a=l(n.slice(0),(function(t){return parseInt(t,10)})),oe(e)):o(n)?function(t){if(!t._d){var e=R(t._i);t._a=l([e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],(function(t){return t&&parseInt(t,10)})),oe(t)}}(e):c(n)?e._d=new Date(n):i.createFromInputFallback(e),y(t)||(t._d=null),t))}function be(t,e,n,r,i){var s,c={};return!0!==n&&!1!==n||(r=n,n=void 0),(o(t)&&function(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}(t)||a(t)&&0===t.length)&&(t=void 0),c._isAMomentObject=!0,c._useUTC=c._isUTC=i,c._l=n,c._i=t,c._f=e,c._strict=r,(s=new _(ie(ve(c))))._nextDay&&(s.add(1,"d"),s._nextDay=void 0),s}function _e(t,e,n,r){return be(t,e,n,r,!1)}i.createFromInputFallback=C("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",(function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))})),i.ISO_8601=function(){},i.RFC_2822=function(){};var xe=C("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=_e.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:g()})),we=C("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=_e.apply(null,arguments);return this.isValid()&&t.isValid()?this<t?this:t:g()}));function ke(t,e){var n,r;if(1===e.length&&a(e[0])&&(e=e[0]),!e.length)return _e();for(n=e[0],r=1;r<e.length;++r)e[r].isValid()&&!e[r][t](n)||(n=e[r]);return n}var Te=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Ee(t){var e=R(t),n=e.year||0,r=e.quarter||0,i=e.month||0,a=e.week||e.isoWeek||0,o=e.day||0,s=e.hour||0,c=e.minute||0,u=e.second||0,l=e.millisecond||0;this._isValid=function(t){for(var e in t)if(-1===vt.call(Te,e)||null!=t[e]&&isNaN(t[e]))return!1;for(var n=!1,r=0;r<Te.length;++r)if(t[Te[r]]){if(n)return!1;parseFloat(t[Te[r]])!==k(t[Te[r]])&&(n=!0)}return!0}(e),this._milliseconds=+l+1e3*u+6e4*c+1e3*s*60*60,this._days=+o+7*a,this._months=+i+3*r+12*n,this._data={},this._locale=re(),this._bubble()}function Ce(t){return t instanceof Ee}function Se(t){return t<0?-1*Math.round(-1*t):Math.round(t)}function Ae(t,e){H(t,0,0,(function(){var t=this.utcOffset(),n="+";return t<0&&(t=-t,n="-"),n+j(~~(t/60),2)+e+j(~~t%60,2)}))}Ae("Z",":"),Ae("ZZ",""),lt("Z",st),lt("ZZ",st),pt(["Z","ZZ"],(function(t,e,n){n._useUTC=!0,n._tzm=Ne(st,t)}));var Me=/([\+\-]|\d\d)/gi;function Ne(t,e){var n=(e||"").match(t);if(null===n)return null;var r=((n[n.length-1]||[])+"").match(Me)||["-",0,0],i=60*r[1]+k(r[2]);return 0===i?0:"+"===r[0]?i:-i}function De(t,e){var n,r;return e._isUTC?(n=e.clone(),r=(x(t)||u(t)?t.valueOf():_e(t).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+r),i.updateOffset(n,!1),n):_e(t).local()}function Oe(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function Be(){return!!this.isValid()&&this._isUTC&&0===this._offset}i.updateOffset=function(){};var Le=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Ie=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Re(t,e){var n,r,i,a=t,o=null;return Ce(t)?a={ms:t._milliseconds,d:t._days,M:t._months}:c(t)?(a={},e?a[e]=t:a.milliseconds=t):(o=Le.exec(t))?(n="-"===o[1]?-1:1,a={y:0,d:k(o[2])*n,h:k(o[3])*n,m:k(o[4])*n,s:k(o[5])*n,ms:k(Se(1e3*o[6]))*n}):(o=Ie.exec(t))?(n="-"===o[1]?-1:1,a={y:Fe(o[2],n),M:Fe(o[3],n),w:Fe(o[4],n),d:Fe(o[5],n),h:Fe(o[6],n),m:Fe(o[7],n),s:Fe(o[8],n)}):null==a?a={}:"object"==typeof a&&("from"in a||"to"in a)&&(i=function(t,e){var n;return t.isValid()&&e.isValid()?(e=De(e,t),t.isBefore(e)?n=Pe(t,e):((n=Pe(e,t)).milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}(_e(a.from),_e(a.to)),(a={}).ms=i.milliseconds,a.M=i.months),r=new Ee(a),Ce(t)&&h(t,"_locale")&&(r._locale=t._locale),r}function Fe(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function Pe(t,e){var n={};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function je(t,e){return function(n,r){var i;return null===r||isNaN(+r)||(M(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),i=n,n=r,r=i),Ye(this,Re(n="string"==typeof n?+n:n,r),t),this}}function Ye(t,e,n,r){var a=e._milliseconds,o=Se(e._days),s=Se(e._months);t.isValid()&&(r=null==r||r,s&&St(t,xt(t,"Month")+s*n),o&&wt(t,"Date",xt(t,"Date")+o*n),a&&t._d.setTime(t._d.valueOf()+a*n),r&&i.updateOffset(t,o||s))}Re.fn=Ee.prototype,Re.invalid=function(){return Re(NaN)};var ze=je(1,"add"),Ue=je(-1,"subtract");function qe(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),r=t.clone().add(n,"months");return-(n+(e-r<0?(e-r)/(r-t.clone().add(n-1,"months")):(e-r)/(t.clone().add(n+1,"months")-r)))||0}function He(t){var e;return void 0===t?this._locale._abbr:(null!=(e=re(t))&&(this._locale=e),this)}i.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",i.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var $e=C("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function We(){return this._locale}var Ve=126227808e5;function Ge(t,e){return(t%e+e)%e}function Xe(t,e,n){return t<100&&0<=t?new Date(t+400,e,n)-Ve:new Date(t,e,n).valueOf()}function Ze(t,e,n){return t<100&&0<=t?Date.UTC(t+400,e,n)-Ve:Date.UTC(t,e,n)}function Qe(t,e){H(0,[t,t.length],0,e)}function Ke(t,e,n,r,i){var a;return null==t?It(this,r,i).year:((a=Rt(t,r,i))<e&&(e=a),function(t,e,n,r,i){var a=Lt(t,e,n,r,i),o=Ot(a.year,0,a.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}.call(this,t,e,n,r,i))}H(0,["gg",2],0,(function(){return this.weekYear()%100})),H(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),Qe("gggg","weekYear"),Qe("ggggg","weekYear"),Qe("GGGG","isoWeekYear"),Qe("GGGGG","isoWeekYear"),L("weekYear","gg"),L("isoWeekYear","GG"),P("weekYear",1),P("isoWeekYear",1),lt("G",at),lt("g",at),lt("GG",K,G),lt("gg",K,G),lt("GGGG",nt,Z),lt("gggg",nt,Z),lt("GGGGG",rt,Q),lt("ggggg",rt,Q),yt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,n,r){e[r.substr(0,2)]=k(t)})),yt(["gg","GG"],(function(t,e,n,r){e[r]=i.parseTwoDigitYear(t)})),H("Q",0,"Qo","quarter"),L("quarter","Q"),P("quarter",7),lt("Q",V),pt("Q",(function(t,e){e[1]=3*(k(t)-1)})),H("D",["DD",2],"Do","date"),L("date","D"),P("date",9),lt("D",K),lt("DD",K,G),lt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),pt(["D","DD"],2),pt("Do",(function(t,e){e[2]=k(t.match(K)[0])}));var Je=_t("Date",!0);H("DDD",["DDDD",3],"DDDo","dayOfYear"),L("dayOfYear","DDD"),P("dayOfYear",4),lt("DDD",et),lt("DDDD",X),pt(["DDD","DDDD"],(function(t,e,n){n._dayOfYear=k(t)})),H("m",["mm",2],0,"minute"),L("minute","m"),P("minute",14),lt("m",K),lt("mm",K,G),pt(["m","mm"],4);var tn=_t("Minutes",!1);H("s",["ss",2],0,"second"),L("second","s"),P("second",15),lt("s",K),lt("ss",K,G),pt(["s","ss"],5);var en,nn=_t("Seconds",!1);for(H("S",0,0,(function(){return~~(this.millisecond()/100)})),H(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),H(0,["SSS",3],0,"millisecond"),H(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),H(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),H(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),H(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),H(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),H(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),L("millisecond","ms"),P("millisecond",16),lt("S",et,V),lt("SS",et,G),lt("SSS",et,X),en="SSSS";en.length<=9;en+="S")lt(en,it);function rn(t,e){e[6]=k(1e3*("0."+t))}for(en="S";en.length<=9;en+="S")pt(en,rn);var an=_t("Milliseconds",!1);H("z",0,0,"zoneAbbr"),H("zz",0,0,"zoneName");var on=_.prototype;function sn(t){return t}on.add=ze,on.calendar=function(t,e){var n=t||_e(),r=De(n,this).startOf("day"),a=i.calendarFormat(this,r)||"sameElse",o=e&&(N(e[a])?e[a].call(this,n):e[a]);return this.format(o||this.localeData().calendar(a,this,_e(n)))},on.clone=function(){return new _(this)},on.diff=function(t,e,n){var r,i,a;if(!this.isValid())return NaN;if(!(r=De(t,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),e=I(e)){case"year":a=qe(this,r)/12;break;case"month":a=qe(this,r);break;case"quarter":a=qe(this,r)/3;break;case"second":a=(this-r)/1e3;break;case"minute":a=(this-r)/6e4;break;case"hour":a=(this-r)/36e5;break;case"day":a=(this-r-i)/864e5;break;case"week":a=(this-r-i)/6048e5;break;default:a=this-r}return n?a:w(a)},on.endOf=function(t){var e;if(void 0===(t=I(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year()+1,0,1)-1;break;case"quarter":e=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=n(this.year(),this.month()+1,1)-1;break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=36e5-Ge(e+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":e=this._d.valueOf(),e+=6e4-Ge(e,6e4)-1;break;case"second":e=this._d.valueOf(),e+=1e3-Ge(e,1e3)-1}return this._d.setTime(e),i.updateOffset(this,!0),this},on.format=function(t){t||(t=this.isUtc()?i.defaultFormatUtc:i.defaultFormat);var e=$(this,t);return this.localeData().postformat(e)},on.from=function(t,e){return this.isValid()&&(x(t)&&t.isValid()||_e(t).isValid())?Re({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.fromNow=function(t){return this.from(_e(),t)},on.to=function(t,e){return this.isValid()&&(x(t)&&t.isValid()||_e(t).isValid())?Re({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.toNow=function(t){return this.to(_e(),t)},on.get=function(t){return N(this[t=I(t)])?this[t]():this},on.invalidAt=function(){return p(this).overflow},on.isAfter=function(t,e){var n=x(t)?t:_e(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=I(e)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(e).valueOf())},on.isBefore=function(t,e){var n=x(t)?t:_e(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=I(e)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(e).valueOf()<n.valueOf())},on.isBetween=function(t,e,n,r){var i=x(t)?t:_e(t),a=x(e)?e:_e(e);return!!(this.isValid()&&i.isValid()&&a.isValid())&&("("===(r=r||"()")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(")"===r[1]?this.isBefore(a,n):!this.isAfter(a,n))},on.isSame=function(t,e){var n,r=x(t)?t:_e(t);return!(!this.isValid()||!r.isValid())&&("millisecond"===(e=I(e)||"millisecond")?this.valueOf()===r.valueOf():(n=r.valueOf(),this.clone().startOf(e).valueOf()<=n&&n<=this.clone().endOf(e).valueOf()))},on.isSameOrAfter=function(t,e){return this.isSame(t,e)||this.isAfter(t,e)},on.isSameOrBefore=function(t,e){return this.isSame(t,e)||this.isBefore(t,e)},on.isValid=function(){return y(this)},on.lang=$e,on.locale=He,on.localeData=We,on.max=we,on.min=xe,on.parsingFlags=function(){return f({},p(this))},on.set=function(t,e){if("object"==typeof t)for(var n=function(t){var e=[];for(var n in t)e.push({unit:n,priority:F[n]});return e.sort((function(t,e){return t.priority-e.priority})),e}(t=R(t)),r=0;r<n.length;r++)this[n[r].unit](t[n[r].unit]);else if(N(this[t=I(t)]))return this[t](e);return this},on.startOf=function(t){var e;if(void 0===(t=I(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year(),0,1);break;case"quarter":e=n(this.year(),this.month()-this.month()%3,1);break;case"month":e=n(this.year(),this.month(),1);break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=n(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=Ge(e+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":e=this._d.valueOf(),e-=Ge(e,6e4);break;case"second":e=this._d.valueOf(),e-=Ge(e,1e3)}return this._d.setTime(e),i.updateOffset(this,!0),this},on.subtract=Ue,on.toArray=function(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]},on.toObject=function(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}},on.toDate=function(){return new Date(this.valueOf())},on.toISOString=function(t){if(!this.isValid())return null;var e=!0!==t,n=e?this.clone().utc():this;return n.year()<0||9999<n.year()?$(n,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):N(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",$(n,"Z")):$(n,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},on.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',r=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=e+'[")]';return this.format(n+r+"-MM-DD[T]HH:mm:ss.SSS"+i)},on.toJSON=function(){return this.isValid()?this.toISOString():null},on.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},on.unix=function(){return Math.floor(this.valueOf()/1e3)},on.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},on.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},on.year=bt,on.isLeapYear=function(){return mt(this.year())},on.weekYear=function(t){return Ke.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},on.isoWeekYear=function(t){return Ke.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},on.quarter=on.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},on.month=At,on.daysInMonth=function(){return kt(this.year(),this.month())},on.week=on.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},on.isoWeek=on.isoWeeks=function(t){var e=It(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},on.weeksInYear=function(){var t=this.localeData()._week;return Rt(this.year(),t.dow,t.doy)},on.isoWeeksInYear=function(){return Rt(this.year(),1,4)},on.date=Je,on.day=on.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e,n,r=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(e=t,n=this.localeData(),t="string"!=typeof e?e:isNaN(e)?"number"==typeof(e=n.weekdaysParse(e))?e:null:parseInt(e,10),this.add(t-r,"d")):r},on.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},on.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null==t)return this.day()||7;var e,n,r=(e=t,n=this.localeData(),"string"==typeof e?n.weekdaysParse(e)%7||7:isNaN(e)?null:e);return this.day(this.day()%7?r:r-7)},on.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},on.hour=on.hours=Xt,on.minute=on.minutes=tn,on.second=on.seconds=nn,on.millisecond=on.milliseconds=an,on.utcOffset=function(t,e,n){var r,a=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null==t)return this._isUTC?a:Oe(this);if("string"==typeof t){if(null===(t=Ne(st,t)))return this}else Math.abs(t)<16&&!n&&(t*=60);return!this._isUTC&&e&&(r=Oe(this)),this._offset=t,this._isUTC=!0,null!=r&&this.add(r,"m"),a!==t&&(!e||this._changeInProgress?Ye(this,Re(t-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,i.updateOffset(this,!0),this._changeInProgress=null)),this},on.utc=function(t){return this.utcOffset(0,t)},on.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Oe(this),"m")),this},on.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=Ne(ot,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},on.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?_e(t).utcOffset():0,(this.utcOffset()-t)%60==0)},on.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},on.isLocal=function(){return!!this.isValid()&&!this._isUTC},on.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},on.isUtc=Be,on.isUTC=Be,on.zoneAbbr=function(){return this._isUTC?"UTC":""},on.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},on.dates=C("dates accessor is deprecated. Use date instead.",Je),on.months=C("months accessor is deprecated. Use month instead",At),on.years=C("years accessor is deprecated. Use year instead",bt),on.zone=C("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()})),on.isDSTShifted=C("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var t={};if(v(t,this),(t=ve(t))._a){var e=t._isUTC?d(t._a):_e(t._a);this._isDSTShifted=this.isValid()&&0<T(t._a,e.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted}));var cn=O.prototype;function un(t,e,n,r){var i=re(),a=d().set(r,e);return i[n](a,t)}function ln(t,e,n){if(c(t)&&(e=t,t=void 0),t=t||"",null!=e)return un(t,e,n,"month");var r,i=[];for(r=0;r<12;r++)i[r]=un(t,r,n,"month");return i}function hn(t,e,n,r){"boolean"==typeof t?c(e)&&(n=e,e=void 0):(e=t,t=!1,c(n=e)&&(n=e,e=void 0)),e=e||"";var i,a=re(),o=t?a._week.dow:0;if(null!=n)return un(e,(n+o)%7,r,"day");var s=[];for(i=0;i<7;i++)s[i]=un(e,(i+o)%7,r,"day");return s}cn.calendar=function(t,e,n){var r=this._calendar[t]||this._calendar.sameElse;return N(r)?r.call(e,n):r},cn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,(function(t){return t.slice(1)})),this._longDateFormat[t])},cn.invalidDate=function(){return this._invalidDate},cn.ordinal=function(t){return this._ordinal.replace("%d",t)},cn.preparse=sn,cn.postformat=sn,cn.relativeTime=function(t,e,n,r){var i=this._relativeTime[n];return N(i)?i(t,e,n,r):i.replace(/%d/i,t)},cn.pastFuture=function(t,e){var n=this._relativeTime[0<t?"future":"past"];return N(n)?n(e):n.replace(/%s/i,e)},cn.set=function(t){var e,n;for(n in t)N(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},cn.months=function(t,e){return t?a(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Tt).test(e)?"format":"standalone"][t.month()]:a(this._months)?this._months:this._months.standalone},cn.monthsShort=function(t,e){return t?a(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Tt.test(e)?"format":"standalone"][t.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},cn.monthsParse=function(t,e,n){var r,i,a;if(this._monthsParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],r=0;r<12;++r)a=d([2e3,r]),this._shortMonthsParse[r]=this.monthsShort(a,"").toLocaleLowerCase(),this._longMonthsParse[r]=this.months(a,"").toLocaleLowerCase();return n?"MMM"===e?-1!==(i=vt.call(this._shortMonthsParse,o))?i:null:-1!==(i=vt.call(this._longMonthsParse,o))?i:null:"MMM"===e?-1!==(i=vt.call(this._shortMonthsParse,o))||-1!==(i=vt.call(this._longMonthsParse,o))?i:null:-1!==(i=vt.call(this._longMonthsParse,o))||-1!==(i=vt.call(this._shortMonthsParse,o))?i:null}.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=d([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(a="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(a.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[r].test(t))return r;if(n&&"MMM"===e&&this._shortMonthsParse[r].test(t))return r;if(!n&&this._monthsParse[r].test(t))return r}},cn.monthsRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=Nt),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},cn.monthsShortRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=Mt),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},cn.week=function(t){return It(t,this._week.dow,this._week.doy).week},cn.firstDayOfYear=function(){return this._week.doy},cn.firstDayOfWeek=function(){return this._week.dow},cn.weekdays=function(t,e){var n=a(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?Ft(n,this._week.dow):t?n[t.day()]:n},cn.weekdaysMin=function(t){return!0===t?Ft(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin},cn.weekdaysShort=function(t){return!0===t?Ft(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort},cn.weekdaysParse=function(t,e,n){var r,i,a;if(this._weekdaysParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)a=d([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(a,"").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(a,"").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(a,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(i=vt.call(this._weekdaysParse,o))?i:null:"ddd"===e?-1!==(i=vt.call(this._shortWeekdaysParse,o))?i:null:-1!==(i=vt.call(this._minWeekdaysParse,o))?i:null:"dddd"===e?-1!==(i=vt.call(this._weekdaysParse,o))||-1!==(i=vt.call(this._shortWeekdaysParse,o))||-1!==(i=vt.call(this._minWeekdaysParse,o))?i:null:"ddd"===e?-1!==(i=vt.call(this._shortWeekdaysParse,o))||-1!==(i=vt.call(this._weekdaysParse,o))||-1!==(i=vt.call(this._minWeekdaysParse,o))?i:null:-1!==(i=vt.call(this._minWeekdaysParse,o))||-1!==(i=vt.call(this._weekdaysParse,o))||-1!==(i=vt.call(this._shortWeekdaysParse,o))?i:null}.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=d([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[r]||(a="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[r]=new RegExp(a.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[r].test(t))return r;if(n&&"ddd"===e&&this._shortWeekdaysParse[r].test(t))return r;if(n&&"dd"===e&&this._minWeekdaysParse[r].test(t))return r;if(!n&&this._weekdaysParse[r].test(t))return r}},cn.weekdaysRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Ht.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=zt),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},cn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Ht.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Ut),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},cn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Ht.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=qt),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},cn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},cn.meridiem=function(t,e,n){return 11<t?n?"pm":"PM":n?"am":"AM"},ee("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===k(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),i.lang=C("moment.lang is deprecated. Use moment.locale instead.",ee),i.langData=C("moment.langData is deprecated. Use moment.localeData instead.",re);var fn=Math.abs;function dn(t,e,n,r){var i=Re(e,n);return t._milliseconds+=r*i._milliseconds,t._days+=r*i._days,t._months+=r*i._months,t._bubble()}function pn(t){return t<0?Math.floor(t):Math.ceil(t)}function yn(t){return 4800*t/146097}function gn(t){return 146097*t/4800}function mn(t){return function(){return this.as(t)}}var vn=mn("ms"),bn=mn("s"),_n=mn("m"),xn=mn("h"),wn=mn("d"),kn=mn("w"),Tn=mn("M"),En=mn("Q"),Cn=mn("y");function Sn(t){return function(){return this.isValid()?this._data[t]:NaN}}var An=Sn("milliseconds"),Mn=Sn("seconds"),Nn=Sn("minutes"),Dn=Sn("hours"),On=Sn("days"),Bn=Sn("months"),Ln=Sn("years"),In=Math.round,Rn={ss:44,s:45,m:45,h:22,d:26,M:11},Fn=Math.abs;function Pn(t){return(0<t)-(t<0)||+t}function jn(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n=Fn(this._milliseconds)/1e3,r=Fn(this._days),i=Fn(this._months);e=w((t=w(n/60))/60),n%=60,t%=60;var a=w(i/12),o=i%=12,s=r,c=e,u=t,l=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var f=h<0?"-":"",d=Pn(this._months)!==Pn(h)?"-":"",p=Pn(this._days)!==Pn(h)?"-":"",y=Pn(this._milliseconds)!==Pn(h)?"-":"";return f+"P"+(a?d+a+"Y":"")+(o?d+o+"M":"")+(s?p+s+"D":"")+(c||u||l?"T":"")+(c?y+c+"H":"")+(u?y+u+"M":"")+(l?y+l+"S":"")}var Yn=Ee.prototype;return Yn.isValid=function(){return this._isValid},Yn.abs=function(){var t=this._data;return this._milliseconds=fn(this._milliseconds),this._days=fn(this._days),this._months=fn(this._months),t.milliseconds=fn(t.milliseconds),t.seconds=fn(t.seconds),t.minutes=fn(t.minutes),t.hours=fn(t.hours),t.months=fn(t.months),t.years=fn(t.years),this},Yn.add=function(t,e){return dn(this,t,e,1)},Yn.subtract=function(t,e){return dn(this,t,e,-1)},Yn.as=function(t){if(!this.isValid())return NaN;var e,n,r=this._milliseconds;if("month"===(t=I(t))||"quarter"===t||"year"===t)switch(e=this._days+r/864e5,n=this._months+yn(e),t){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(e=this._days+Math.round(gn(this._months)),t){case"week":return e/7+r/6048e5;case"day":return e+r/864e5;case"hour":return 24*e+r/36e5;case"minute":return 1440*e+r/6e4;case"second":return 86400*e+r/1e3;case"millisecond":return Math.floor(864e5*e)+r;default:throw new Error("Unknown unit "+t)}},Yn.asMilliseconds=vn,Yn.asSeconds=bn,Yn.asMinutes=_n,Yn.asHours=xn,Yn.asDays=wn,Yn.asWeeks=kn,Yn.asMonths=Tn,Yn.asQuarters=En,Yn.asYears=Cn,Yn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*k(this._months/12):NaN},Yn._bubble=function(){var t,e,n,r,i,a=this._milliseconds,o=this._days,s=this._months,c=this._data;return 0<=a&&0<=o&&0<=s||a<=0&&o<=0&&s<=0||(a+=864e5*pn(gn(s)+o),s=o=0),c.milliseconds=a%1e3,t=w(a/1e3),c.seconds=t%60,e=w(t/60),c.minutes=e%60,n=w(e/60),c.hours=n%24,s+=i=w(yn(o+=w(n/24))),o-=pn(gn(i)),r=w(s/12),s%=12,c.days=o,c.months=s,c.years=r,this},Yn.clone=function(){return Re(this)},Yn.get=function(t){return t=I(t),this.isValid()?this[t+"s"]():NaN},Yn.milliseconds=An,Yn.seconds=Mn,Yn.minutes=Nn,Yn.hours=Dn,Yn.days=On,Yn.weeks=function(){return w(this.days()/7)},Yn.months=Bn,Yn.years=Ln,Yn.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e,n,r,i,a,o,s,c,u,l,h=this.localeData(),f=(e=!t,n=h,r=Re(this).abs(),i=In(r.as("s")),a=In(r.as("m")),o=In(r.as("h")),s=In(r.as("d")),c=In(r.as("M")),u=In(r.as("y")),(l=i<=Rn.ss&&["s",i]||i<Rn.s&&["ss",i]||a<=1&&["m"]||a<Rn.m&&["mm",a]||o<=1&&["h"]||o<Rn.h&&["hh",o]||s<=1&&["d"]||s<Rn.d&&["dd",s]||c<=1&&["M"]||c<Rn.M&&["MM",c]||u<=1&&["y"]||["yy",u])[2]=e,l[3]=0<+this,l[4]=n,function(t,e,n,r,i){return i.relativeTime(e||1,!!n,t,r)}.apply(null,l));return t&&(f=h.pastFuture(+this,f)),h.postformat(f)},Yn.toISOString=jn,Yn.toString=jn,Yn.toJSON=jn,Yn.locale=He,Yn.localeData=We,Yn.toIsoString=C("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",jn),Yn.lang=$e,H("X",0,0,"unix"),H("x",0,0,"valueOf"),lt("x",at),lt("X",/[+-]?\d+(\.\d{1,3})?/),pt("X",(function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))})),pt("x",(function(t,e,n){n._d=new Date(k(t))})),i.version="2.24.0",e=_e,i.fn=on,i.min=function(){return ke("isBefore",[].slice.call(arguments,0))},i.max=function(){return ke("isAfter",[].slice.call(arguments,0))},i.now=function(){return Date.now?Date.now():+new Date},i.utc=d,i.unix=function(t){return _e(1e3*t)},i.months=function(t,e){return ln(t,e,"months")},i.isDate=u,i.locale=ee,i.invalid=g,i.duration=Re,i.isMoment=x,i.weekdays=function(t,e,n){return hn(t,e,n,"weekdays")},i.parseZone=function(){return _e.apply(null,arguments).parseZone()},i.localeData=re,i.isDuration=Ce,i.monthsShort=function(t,e){return ln(t,e,"monthsShort")},i.weekdaysMin=function(t,e,n){return hn(t,e,n,"weekdaysMin")},i.defineLocale=ne,i.updateLocale=function(t,e){if(null!=e){var n,r,i=Zt;null!=(r=te(t))&&(i=r._config),(n=new O(e=D(i,e))).parentLocale=Qt[t],Qt[t]=n,ee(t)}else null!=Qt[t]&&(null!=Qt[t].parentLocale?Qt[t]=Qt[t].parentLocale:null!=Qt[t]&&delete Qt[t]);return Qt[t]},i.locales=function(){return S(Qt)},i.weekdaysShort=function(t,e,n){return hn(t,e,n,"weekdaysShort")},i.normalizeUnits=I,i.relativeTimeRounding=function(t){return void 0===t?In:"function"==typeof t&&(In=t,!0)},i.relativeTimeThreshold=function(t,e){return void 0!==Rn[t]&&(void 0===e?Rn[t]:(Rn[t]=e,"s"===t&&(Rn.ss=e-1),!0))},i.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},i.prototype=on,i.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},i}()},6470:t=>{"use strict";function e(t){if("string"!=typeof t)throw new TypeError("Path must be a string. Received "+JSON.stringify(t))}function n(t,e){for(var n,r="",i=0,a=-1,o=0,s=0;s<=t.length;++s){if(s<t.length)n=t.charCodeAt(s);else{if(47===n)break;n=47}if(47===n){if(a===s-1||1===o);else if(a!==s-1&&2===o){if(r.length<2||2!==i||46!==r.charCodeAt(r.length-1)||46!==r.charCodeAt(r.length-2))if(r.length>2){var c=r.lastIndexOf("/");if(c!==r.length-1){-1===c?(r="",i=0):i=(r=r.slice(0,c)).length-1-r.lastIndexOf("/"),a=s,o=0;continue}}else if(2===r.length||1===r.length){r="",i=0,a=s,o=0;continue}e&&(r.length>0?r+="/..":r="..",i=2)}else r.length>0?r+="/"+t.slice(a+1,s):r=t.slice(a+1,s),i=s-a-1;a=s,o=0}else 46===n&&-1!==o?++o:o=-1}return r}var r={resolve:function(){for(var t,r="",i=!1,a=arguments.length-1;a>=-1&&!i;a--){var o;a>=0?o=arguments[a]:(void 0===t&&(t=process.cwd()),o=t),e(o),0!==o.length&&(r=o+"/"+r,i=47===o.charCodeAt(0))}return r=n(r,!i),i?r.length>0?"/"+r:"/":r.length>0?r:"."},normalize:function(t){if(e(t),0===t.length)return".";var r=47===t.charCodeAt(0),i=47===t.charCodeAt(t.length-1);return 0!==(t=n(t,!r)).length||r||(t="."),t.length>0&&i&&(t+="/"),r?"/"+t:t},isAbsolute:function(t){return e(t),t.length>0&&47===t.charCodeAt(0)},join:function(){if(0===arguments.length)return".";for(var t,n=0;n<arguments.length;++n){var i=arguments[n];e(i),i.length>0&&(void 0===t?t=i:t+="/"+i)}return void 0===t?".":r.normalize(t)},relative:function(t,n){if(e(t),e(n),t===n)return"";if((t=r.resolve(t))===(n=r.resolve(n)))return"";for(var i=1;i<t.length&&47===t.charCodeAt(i);++i);for(var a=t.length,o=a-i,s=1;s<n.length&&47===n.charCodeAt(s);++s);for(var c=n.length-s,u=o<c?o:c,l=-1,h=0;h<=u;++h){if(h===u){if(c>u){if(47===n.charCodeAt(s+h))return n.slice(s+h+1);if(0===h)return n.slice(s+h)}else o>u&&(47===t.charCodeAt(i+h)?l=h:0===h&&(l=0));break}var f=t.charCodeAt(i+h);if(f!==n.charCodeAt(s+h))break;47===f&&(l=h)}var d="";for(h=i+l+1;h<=a;++h)h!==a&&47!==t.charCodeAt(h)||(0===d.length?d+="..":d+="/..");return d.length>0?d+n.slice(s+l):(s+=l,47===n.charCodeAt(s)&&++s,n.slice(s))},_makeLong:function(t){return t},dirname:function(t){if(e(t),0===t.length)return".";for(var n=t.charCodeAt(0),r=47===n,i=-1,a=!0,o=t.length-1;o>=1;--o)if(47===(n=t.charCodeAt(o))){if(!a){i=o;break}}else a=!1;return-1===i?r?"/":".":r&&1===i?"//":t.slice(0,i)},basename:function(t,n){if(void 0!==n&&"string"!=typeof n)throw new TypeError('"ext" argument must be a string');e(t);var r,i=0,a=-1,o=!0;if(void 0!==n&&n.length>0&&n.length<=t.length){if(n.length===t.length&&n===t)return"";var s=n.length-1,c=-1;for(r=t.length-1;r>=0;--r){var u=t.charCodeAt(r);if(47===u){if(!o){i=r+1;break}}else-1===c&&(o=!1,c=r+1),s>=0&&(u===n.charCodeAt(s)?-1==--s&&(a=r):(s=-1,a=c))}return i===a?a=c:-1===a&&(a=t.length),t.slice(i,a)}for(r=t.length-1;r>=0;--r)if(47===t.charCodeAt(r)){if(!o){i=r+1;break}}else-1===a&&(o=!1,a=r+1);return-1===a?"":t.slice(i,a)},extname:function(t){e(t);for(var n=-1,r=0,i=-1,a=!0,o=0,s=t.length-1;s>=0;--s){var c=t.charCodeAt(s);if(47!==c)-1===i&&(a=!1,i=s+1),46===c?-1===n?n=s:1!==o&&(o=1):-1!==n&&(o=-1);else if(!a){r=s+1;break}}return-1===n||-1===i||0===o||1===o&&n===i-1&&n===r+1?"":t.slice(n,i)},format:function(t){if(null===t||"object"!=typeof t)throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof t);return function(t,e){var n=e.dir||e.root,r=e.base||(e.name||"")+(e.ext||"");return n?n===e.root?n+r:n+"/"+r:r}(0,t)},parse:function(t){e(t);var n={root:"",dir:"",base:"",ext:"",name:""};if(0===t.length)return n;var r,i=t.charCodeAt(0),a=47===i;a?(n.root="/",r=1):r=0;for(var o=-1,s=0,c=-1,u=!0,l=t.length-1,h=0;l>=r;--l)if(47!==(i=t.charCodeAt(l)))-1===c&&(u=!1,c=l+1),46===i?-1===o?o=l:1!==h&&(h=1):-1!==o&&(h=-1);else if(!u){s=l+1;break}return-1===o||-1===c||0===h||1===h&&o===c-1&&o===s+1?-1!==c&&(n.base=n.name=0===s&&a?t.slice(1,c):t.slice(s,c)):(0===s&&a?(n.name=t.slice(1,o),n.base=t.slice(1,c)):(n.name=t.slice(s,o),n.base=t.slice(s,c)),n.ext=t.slice(o,c)),s>0?n.dir=t.slice(0,s-1):a&&(n.dir="/"),n},sep:"/",delimiter:":",win32:null,posix:null};r.posix=r,t.exports=r},8218:()=>{},8009:()=>{},5354:()=>{},6878:()=>{},8183:()=>{},1428:()=>{},4551:()=>{},8800:()=>{},1993:()=>{},3069:()=>{},9143:()=>{}},e={};function n(r){var i=e[r];if(void 0!==i)return i.exports;var a=e[r]={id:r,loaded:!1,exports:{}};return t[r].call(a.exports,a,a.exports,n),a.loaded=!0,a.exports}n.c=e,n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var r in e)n.o(e,r)&&!n.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.nmd=t=>(t.paths=[],t.children||(t.children=[]),t);var r=n(n.s=7458);return r.default})()}));
+//# sourceMappingURL=mermaid.min.js.map
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/SUMMARY.md b/third_party/rust/autocxx/v0_17/crate/book/src/SUMMARY.md
new file mode 100644
index 0000000..59f23620
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/SUMMARY.md
@@ -0,0 +1,22 @@
+# Summary
+
+- [Rust ❤️  pre-existing C++](index.md)
+- [Tutorial](tutorial.md)
+- [Workflow](workflow.md)
+- [Allowlist and syntax](allowlist.md)
+- [Building](building.md)
+- [Storage - stack and heaps](storage.md)
+- [Pointers, references, values](references_etc.md)
+- [Built-in types](primitives.md)
+- [C++ type and function names](naming.md)
+- [C++ structs, enums and classes](cpp_types.md)
+- [C++ functions](cpp_functions.md)
+- [Callbacks into Rust](rust_calls.md)
+- [Other C++ features](other_features.md)
+- [Safety](safety.md)
+- [Rustic bindings](rustic.md)
+- [Examples](examples.md)
+- [Credits](credits.md)
+- [Contributing](contributing.md)
+- [Code of Conduct](code-of-conduct.md)
+
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/allowlist.md b/third_party/rust/autocxx/v0_17/crate/book/src/allowlist.md
new file mode 100644
index 0000000..2834ee6
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/allowlist.md
@@ -0,0 +1,31 @@
+# The allowlist and `include_cpp` syntax
+
+To include C++ in your Rust codebase using `autocxx`, you will need
+at least one [`include_cpp` macro](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html).
+
+The simplest is:
+
+```rust,ignore
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "my_header.h"
+    generate!("MyAPIFunction")
+}
+```
+
+You need to include [`generate!` directives](https://docs.rs/autocxx/latest/autocxx/macro.generate.html)
+for every *type* or *function* you wish to access from Rust. You don't need to specify this for member functions
+of types that you've added - they'll be generated automatically. (If a particular member function can't
+be generated, some placeholder item with explanatory documentation [will be generated instead](workflow.md)).
+
+Various other directives are possible inside this macro, most notably:
+
+* You can ask to generate all the items in a namespace using
+  [`generate_ns!`](https://docs.rs/autocxx/latest/autocxx/macro.generate_ns.html)
+* You might sometimes want to ask that a type is generated as 'plain old data' using
+  [`generate_pod!`](https://docs.rs/autocxx/latest/autocxx/macro.generate_pod.html) instead of `generate!` -
+  see the chapter on [C++ types](cpp_types.md).
+* You'll probaly want to specify a [`safety!` policy](safety.md)
+
+See [the docs.rs documentation for the full list](https://docs.rs/autocxx/latest/autocxx/).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/building.md b/third_party/rust/autocxx/v0_17/crate/book/src/building.md
new file mode 100644
index 0000000..4d5d9191
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/building.md
@@ -0,0 +1,60 @@
+# Building
+
+## Building if you're using cargo
+
+The basics of building in a `cargo` environment are explained in [the tutorial](tutorial.md).
+
+If your build depends on later editions of the C++ standard library, you will need to ensure that both `libclang` and the compiler are sent the appropriate flag, like this:
+
+```rust,ignore
+fn main() {
+    let path = std::path::PathBuf::from("src"); // include path
+    let mut b = autocxx_build::Builder::new("src/main.rs", &[&path])
+        .extra_clang_args(&["-std=c++17"])
+        .expect_build();
+    b.flag_if_supported("-std=c++17")
+        .compile("autocxx-demo"); // arbitrary library name, pick anything
+    println!("cargo:rerun-if-changed=src/main.rs");
+    // Add instructions to link to any C++ libraries you need.
+}
+```
+
+## Building - if you're not using cargo
+
+See the `autocxx-gen` crate. You'll need to:
+
+* Run the `codegen` phase. You'll need to use the [`autocxx-gen`](https://crates.io/crates/autocxx-gen)
+  tool to process the .rs code into C++ header and
+  implementation files. This will also generate `.rs` side bindings.
+* Educate the procedural macro about where to find the generated `.rs` bindings. Set the
+  `AUTOCXX_RS` environment variable to a list of directories to search.
+  If you use `autocxx-build`, this happens automatically. (You can alternatively
+  specify `AUTOCXX_RS_FILE` to give a precise filename as opposed to a directory to search,
+  though this isn't recommended unless your build system specifically requires it
+  because it allows only a single `include_cpp!` block per `.rs` file.) See `gen --help`
+  for details on the naming of the generated files.
+
+```mermaid
+flowchart TB
+    s(Rust source with include_cpp!)
+    c(Existing C++ headers)
+    cg(autocxx-gen or autocxx-build)
+    genrs(Generated .rs file)
+    gencpp(Generated .cpp and .h files)
+    rsb(Rust/Cargo build)
+    cppb(C++ build)
+    l(Linker)
+    s --> cg
+    c --> cg
+    cg --> genrs
+    cg --> gencpp
+    m(autocxx-macro)
+    s --> m
+    genrs-. included .->m
+    m --> rsb
+    gencpp --> cppb
+    cppb --> l
+    rsb --> l
+```
+
+This interop inevitably involves lots of fiddly small functions. It's likely to perform far better if you can achieve cross-language link-time-optimization (LTO). [This issue](https://github.com/dtolnay/cxx/issues/371) may give some useful hints - see also all the build-related help in [the cxx manual](https://cxx.rs/) which all applies here too.
diff --git a/third_party/rust/autocxx/v0_16/crate/docs/code-of-conduct.md b/third_party/rust/autocxx/v0_17/crate/book/src/code-of-conduct.md
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/docs/code-of-conduct.md
rename to third_party/rust/autocxx/v0_17/crate/book/src/code-of-conduct.md
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/contributing.md b/third_party/rust/autocxx/v0_17/crate/book/src/contributing.md
new file mode 100644
index 0000000..9942445
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/contributing.md
@@ -0,0 +1,106 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows [Google's Open Source Community
+Guidelines](https://opensource.google/conduct/).
+
+## Directory structure
+
+* `book` - you're reading it!
+* `demo` - a very simple demo example
+* `examples` - will gradually fill with more complex examples
+* `parser` - code which parses a single `include_cpp!` macro. Used by both the macro
+  (which doesn't do much) and the code generator (which does much more, by means of
+  `engine` below)
+* `engine` - all the core code for actual code generation.
+* `macro` - the procedural macro which expands the Rust code.
+* `gen/build` - a library to be used from `build.rs` scripts to generate .cc and .h
+  files from an `include_cxx` section.
+* `gen/cmd` - a command-line tool which does the same.
+* `src` (outermost project) - a wrapper crate which imports the procedural macro and
+  a few other things.
+
+## Where to start reading
+
+The main algorithm is in `engine/src/lib.rs`, in the function `generate()`. This asks
+`bindgen` to generate a heap of Rust code and then passes it into
+`engine/src/conversion` to convert it to be a format suitable for input
+to `cxx`.
+
+However, most of the actual code is in `engine/src/conversion/mod.rs`.
+
+At the moment we're using a slightly branched version of `bindgen` called `autocxx-bindgen`.
+It's hoped this is temporary; some of our changes are sufficiently weird that it would be
+presumptious to try to get them accepted upstream until we're sure `autocxx` has roughly the right approach.
+
+## How to develop
+
+If you're making a change, here's what you need to do to get useful diagnostics etc.
+First of all, `cargo run` in the `demo` directory. If it breaks, you don't get much
+in the way of useful diagnostics, because `stdout` is swallowed by cargo build scripts.
+So, practically speaking, you would almost always move onto running one of the tests
+in the test suite. With suitable options, you can get plenty of output. For instance:
+
+```ignore
+RUST_BACKTRACE=1 RUST_LOG=autocxx_engine=info cargo test --all test_cycle_string_full_pipeline -- --nocapture
+```
+
+This is especially valuable to see the `bindgen` output Rust code, and then the converted Rust code which we pass into cxx. Usually, most problems are due to some mis-conversion somewhere
+in `engine/src/conversion`. See [here](https://docs.rs/autocxx-engine/latest/autocxx_engine/struct.IncludeCppEngine.html) for documentation and diagrams on how the engine works.
+
+## Reporting bugs
+
+If you've found a problem, and you're reading this, *thank you*! Your diligence
+in reporting the bug is much appreciated and will make `autocxx` better. In
+order of preference here's how we would like to hear about your problem:
+
+* Raise a pull request adding a new failing integration test to
+  `engine/src/integration_tests.rs`.
+* Minimize the test using `tools/reduce`, something like this:
+  `target/debug/autocxx-reduce file -d "safety!(unsafe_ffi)" -d
+  'generate_pod!("A")' -I ~/my-include-dir -h my-header.h -p
+  problem-error-message -- --remove-pass pass_line_markers`
+  This is a wrapper for the amazing `creduce` which will take thousands of lines
+  of C++, preprocess it, and then identify the minimum required lines to
+  reproduce the same problem.
+* Use the C++ preprocessor to give a single complete C++ file which demonstrates
+  the problem, along with the `include_cpp!` directive you use.
+  Alternatively, run your build using `AUTOCXX_REPRO_CASE=repro.json` which should
+  put everything we need into `output.h`. If necessary, you can use the `CLANG_PATH`
+  or `CXX` environment variables to specify the path to the Clang compiler to use.
+* Failing all else, build using
+  `cargo clean -p <your package name> && RUST_LOG=autocxx_engine=info cargo build -vvv`
+  and send the _entire_ log to us. This will include two key bits of logging:
+  the C++ bindings as distilled by `bindgen`, and then the version which
+  we've converted and moulded to be suitable for use by `cxx`.
+
+## How to contribute to this manual
+
+More examples in this manual are _very_ welcome!
+
+Because `autocxx` examples require both Rust and C++ code to be linked together,
+a custom preprocessor is used for this manual. See one of the existing examples
+such as in `index.md` to see how to do this.
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/cpp_functions.md b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_functions.md
new file mode 100644
index 0000000..c34a4e8
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_functions.md
@@ -0,0 +1,228 @@
+# C++ functions
+
+Calling C++ functions is largly as you might expect.
+
+## Value and rvalue parameters
+
+Functions taking [non-POD](cpp_types.md) value parameters can take a `cxx::UniquePtr<T>`
+or a `&T`. This gives you the choice of Rust semantics - where a parameter
+is absorbed and destroyed - or C++ semantics where the parameter is copied.
+
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"
+#include <strstream>
+Goat::Goat() {}
+void feed_goat(Goat g) {}
+",
+"#include <cstdint>
+
+struct Goat {
+    Goat();
+    uint32_t horn_count;
+};
+
+void feed_goat(Goat g); // takes goat by value
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("Goat")
+    generate!("feed_goat")
+}
+
+fn main() {
+    let goat = ffi::Goat::make_unique(); // returns a cxx::UniquePtr, i.e. a std::unique_ptr
+    // C++-like semantics...
+    ffi::feed_goat(&goat);
+    // ... you've still got the goat!
+    ffi::feed_goat(&goat);
+    // Or, Rust-like semantics, where the goat is consumed.
+    ffi::feed_goat(goat);
+    // No goat any more...
+    // ffi::feed_goat(&goat); // doesn't compile
+}
+}
+)
+```
+
+Specifically, you can pass anything which implements [`ValueParam<T>`](https://docs.rs/autocxx/latest/autocxx/trait.ValueParam.html).
+
+If you're keeping non-POD values on the Rust stack, you need to explicitly use [`as_mov`](https://docs.rs/autocxx/latest/autocxx/prelude/fn.as_mov.html) to indicate that you want to
+consume the object using move semantics:
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"
+#include <strstream>
+Blimp::Blimp() {}
+void burst(Blimp g) {}
+",
+"#include <cstdint>
+
+struct Blimp {
+    Blimp();
+    uint32_t tons_of_helium;
+};
+
+void burst(Blimp b); // consumes blimp,
+    // but because C++ is amazing, may copy the blimp first.
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("Blimp")
+    generate!("burst")
+}
+
+fn main() {
+    moveit! {
+        let mut blimp = ffi::Blimp::new();
+    }
+    ffi::burst(&*blimp); // pass by copy
+    ffi::burst(as_copy(blimp.as_ref())); // explicitly say you want to pass by copy
+    ffi::burst(as_mov(blimp)); // consume, using move constructor
+}
+}
+)
+```
+
+Rvalue parameters are not yet supported.
+
+## Default parameters
+
+Are not yet supported[^default].
+
+[^default]: the work is [planned here](https://github.com/google/autocxx/issues/563).
+
+## Return values
+
+At present, return values for [non-POD](cpp_types.md) types are always
+a `cxx::UniquePtr<T>`. This is likely to change in future, at least to a type
+which is guaranteed not to be null[^not-null.]
+
+[^not-null]: [plans here](https://github.com/google/autocxx/issues/845)
+
+## Overloads - and identifiers ending in digits
+
+C++ allows function overloads; Rust doesn't. `autocxx` follows the lead
+of `bindgen` here and generating overloads as `func`, `func1`, `func2` etc.
+This is essentially awful without `rust-analyzer` IDE support - see the
+[workflows chapter](workflow.md) for why you should be using an IDE.
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"
+void saw(const View&) {}
+void saw(const Tree&) {}",
+"
+#include <string>
+struct View {
+    std::string of_what; // go and watch In Bruges, it's great
+};
+
+struct Tree {
+    int dendrochronologically_determined_age;
+};
+
+void saw(const View&);
+void saw(const Tree&);
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("Tree")
+    generate!("View")
+    generate!("saw")
+    generate!("saw1")
+}
+
+fn main() {
+    let view = ffi::View::make_unique();
+    ffi::saw(&view);
+    let tree = ffi::Tree::make_unique();
+    ffi::saw1(&tree); // yuck, overload
+}
+}
+)
+```
+
+`autocxx` doesn't yet support default parameters.
+
+It's fairly likely we'll change the model here in the future, such that
+we can pass tuples of different parameter types into a single function
+implementation.
+
+## Methods
+
+Calling a *const* method is simple:
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"",
+"
+class Sloth {
+public:
+    void sleep() const {} // sloths unchanged by sleep
+};
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("Sloth")
+}
+
+fn main() {
+    let sloth = ffi::Sloth::make_unique();
+    sloth.sleep();
+    sloth.sleep();
+}
+}
+)
+```
+
+Calling a non-const method is a bit more of a pain. Per `cxx` norms, all mutable
+references to C++ objects must be [pinned](https://doc.rust-lang.org/std/pin/).
+In practice, this means you must call [`.pin_mut()`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html#method.pin_mut)
+every time you call a method:
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"",
+"
+class Sloth {
+public:
+    void unpeel_from_tree() {} // sloths get agitated when removed from
+        // trees, probably shouldn't be const
+};
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("Sloth")
+}
+
+fn main() {
+    let mut sloth = ffi::Sloth::make_unique();
+    sloth.pin_mut().unpeel_from_tree();
+    sloth.pin_mut().unpeel_from_tree();
+}
+}
+)
+```
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/cpp_types.md b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_types.md
new file mode 100644
index 0000000..3a6f3ea
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/cpp_types.md
@@ -0,0 +1,177 @@
+# C++ structs, enums and classes
+
+If you add a C++ struct, class or enum to the [allowlist](allowlist.md), Rust bindings will be generated to that type and to any methods it has.
+Even if you don't add it to the allowlist, the type may be generated if it's required by some other function - but in this case
+all its methods won't be generated.
+
+Rust and C++ differ in an important way:
+
+* In Rust, the compiler is free to pick up some data and move it to somewhere else (in a `memcpy` sense). The object is none the wiser.
+* In C++, once created, an object stays where it is, until or unless it has its "move constructor" invoked.
+
+This makes a big difference: C++ objects can have self-referential pointers, and any such pointer would be invalidated by Rust doing
+a memcpy. Such self-referential pointers are common - even some implementations of `std::string` do it.
+
+## POD and non-POD
+
+When asking `autocxx` to generate bindings for a type, then, you have to make a choice.
+
+* *This C++ type is trivial*. It has no destructor or move constructor (or they're trivial), and thus Rust is free to move it around the stack as it wishes. `autocxx` calls these types POD ("plain old data"). Alternatively,
+* *This C++ type has a non-trivial destructor or move constructor, so we can't allow Rust to move this around*. `autocxx` calls these types non-POD.
+
+POD types are nicer:
+
+* You can just use them as regular Rust types.
+* You get direct field access.
+* No funny business.
+
+Non-POD types are awkward:
+
+* You can't just _have_ one as a Rust variable. Normally you hold them in a [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html), though there are other options.
+* There is no access to fields (yet).
+* You can't even have a `&mut` reference to one, because then you might be able to use [`std::mem::swap`](https://doc.rust-lang.org/stable/std/mem/fn.swap.html) or similar. You can have a `Pin<&mut>` reference, which is more fiddly.
+
+By default, `autocxx` generates non-POD types. You can request a POD type using [`generate_pod!`](https://docs.rs/autocxx/latest/autocxx/macro.generate_pod.html). Don't worry: you can't mess this up. If the C++ type doesn't in fact comply with the requirements for a POD type, your build will fail thanks to some static assertions generated in the C++. (If you're _really_ sure your type is freely relocatable, because you implemented the move constructor and destructor and you promise they're trivial, you can override these assertions using the C++ trait `IsRelocatable` per the instructions in [cxx.h](https://github.com/dtolnay/cxx/blob/master/include/cxx.h)).
+
+See [the chapter on storage](storage.md) for lots more detail on how you can hold onto non-POD types.
+
+## Construction
+
+Each constructor results in _two_ Rust functions.
+
+* A `new` function exists, which
+  offers a constructor per the standards of the `moveit` crate, and thus can be used
+  to place the object on the Rust stack (as well as in various containers such as `Box`
+  and `UniquePtr`)
+* A `make_unique` function is also created, which constructs the item directly into
+  a `cxx::UniquePtr`. This is more commonly what you want.
+
+Multiple constructors (aka constructor overloading) follows the same [rules as other functions](cpp_functions.html#overloads---and-identifiers-ending-in-digits).
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"void A::set(uint32_t val) { a = val; }
+uint32_t A::get() const { return a; }",
+"#include <stdint.h>
+#include <string>
+struct A {
+    A() {}
+    void set(uint32_t val);
+    uint32_t get() const;
+    uint32_t a;
+};
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("A")
+}
+
+fn main() {
+    moveit! {
+        let mut stack_obj = ffi::A::new();
+    }
+    stack_obj.as_mut().set(42);
+    assert_eq!(stack_obj.get(), 42);
+
+    let mut heap_obj = ffi::A::make_unique();
+    heap_obj.pin_mut().set(42);
+    assert_eq!(heap_obj.get(), 42);
+}
+}
+)
+```
+
+## Forward declarations
+
+A type which is incomplete in the C++ headers (i.e. represented only by a forward
+declaration) can't be held in a `UniquePtr` within Rust (because Rust can't know
+if it has a destructor that will need to be called if the object is dropped.)
+Naturally, such an object can't be passed by value either; it can still be
+referenced in Rust references.
+
+## Generic (templated) types
+
+If you're using one of the generic types which is supported natively by cxx,
+e.g. `std::unique_ptr`, it should work as you expect. For other generic types,
+we synthesize a concrete Rust type, corresponding to a C++ typedef, for each
+concrete instantiation of the type. Such generated types are always opaque,
+and never have methods attached. That's therefore enough to pass them
+between return types and parameters of other functions within [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html)s
+but not really enough to do anything else with these types yet[^templated].
+
+[^templated]: Future improvements tracked [here](https://github.com/google/autocxx/issues/349)
+
+To make them more useful, you might have to add extra C++ functions to extract
+data or otherwise deal with them.
+
+## Implicit member functions
+
+Most of the API of a C++ type is contained within the type, so `autocxx` can
+understand what is available for Rust to call when that type is analyzed.
+However, there is an important exception for the so-called special
+member functions, which will be implicitly generated by the C++ compiler for
+some types. `autocxx` makes use of these types of special members:
+* Default constructor
+* Destructor
+* Copy constructor
+* Move constructor
+
+Explicitly declared versions of these special members are easy: `autocxx` knows
+they exist and uses them.
+
+`autocxx` currently uses its own analysis to determine when implicit versions of
+these exist. This analysis tries to be conservative (avoid generating wrappers
+that require the existence of C++ functions that don't exist), but sometimes
+this goes wrong and understanding the details is necessary to get the correct
+Rust wrappers generated.
+
+In particular, determing whether an implicit version of any of these exists
+requires analyzing the types of all bases and members. `autocxx` only analyzes
+types when requested, because some may be un-analyzable. If the types of any
+bases or members are not analyzed, `autocxx` will assume a public destructor
+exists (in the absence of any other destructors), and avoid using any other
+implicit special member functions. Notably this includes the default
+constructor, so types with un-analyzed bases or members and no explicit
+constructors will not get a `make_unique` or `new` generated. If `autocxx` isn't
+generating a `make_unique` or `CopyNew` or `MoveNew` for a type which permits
+the corresponding operations in C++, make sure the types of all bases and
+members are analyzed or implement it explicitly.
+
+`autocxx` currently does not take member initializers (`const int x = 5`) into
+account when determining whether a default constructor
+exists[^member-initializers]. Explicitly declared default destructors still
+work though.
+
+Currently, `autocxx` assumes that an explicitly defaulted (`= default`) member
+function exists, although it is valid C++ for that to be
+deleted[^explicitly-defaulted]. Clang's
+[-Wdefaulted-function-deleted](https://clang.llvm.org/docs/DiagnosticsReference.html#wdefaulted-function-deleted)
+flag (enabled by default) will warn about types like this.
+
+A C++ type which can be instantiated but has an inaccessible constructor will
+be leaked by Rust[^inaccessible-destructor]. The object's memory itself will be
+freed without calling any C++ destructor, which will leak any resources tracked
+by the C++ implementation.
+
+Many of the special members may be overloaded in C++. This generally means
+adding `const` or `volatile` qualifiers or extra arguments with defaults.
+`autocxx` avoids using any overloaded special members because choosing which
+one to call from Rust gets tricky.
+
+[^member-initializers]: Handling of member initializers is tracked
+[here](https://github.com/google/autocxx/issues/816).
+[^explicitly-defaulted]: Fix for explicitly defaulted special member functions
+that are deleted is tracked [here](https://github.com/google/autocxx/issues/815).
+[^inaccessible-destructor]: Discussion around what to do about inaccessible or
+deleted destructors [here](https://github.com/google/autocxx/issues/829).
+
+## Abstract types
+
+`autocxx` does not allow instantiation of abstract types[^abstract] (aka types with pure virtual methods).
+
+[^abstract]: `autocxx`'s determination of abstract types is a bit approximate and
+[could be improved](https://github.com/google/autocxx/issues/774).
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/credits.md b/third_party/rust/autocxx/v0_17/crate/book/src/credits.md
new file mode 100644
index 0000000..04b2501e
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/credits.md
@@ -0,0 +1,7 @@
+# Credits
+
+David Tolnay did much of the hard work here, by inventing the underlying cxx crate, and in fact nearly all of the parsing infrastructure on which this crate depends. `bindgen` is also awesome. This crate stands on the shoulders of giants!
+
+Thanks to all the other contributors to cxx, bindgen and autocxx.
+
+And thanks to [all in the Chromium community for inspiring this tool](https://www.chromium.org/Home/chromium-security/memory-safety/rust-and-c-interoperability/).
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/examples.md b/third_party/rust/autocxx/v0_17/crate/book/src/examples.md
new file mode 100644
index 0000000..d0246f5
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/examples.md
@@ -0,0 +1,10 @@
+# Examples
+
+* [Demo](https://github.com/google/autocxx/tree/main/demo) - simplest possible demo
+* [S2 example](https://github.com/google/autocxx/tree/main/examples/s2) - example using S2 geometry library
+* [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini) - example using (something like) the Steam client library
+* [Subclass example](https://github.com/google/autocxx/tree/main/examples/subclass) - example using subclasses
+* [Integration tests](https://github.com/google/autocxx/blob/main/integration-tests/src/tests.rs)
+  - hundreds of small snippets
+
+Contributions of more examples to the `examples` directory are much appreciated!
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/index.md b/third_party/rust/autocxx/v0_17/crate/book/src/index.md
new file mode 100644
index 0000000..59832bb
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/index.md
@@ -0,0 +1,110 @@
+[![GitHub](https://img.shields.io/crates/l/autocxx)](https://github.com/google/autocxx)
+[![crates.io](https://img.shields.io/crates/d/autocxx)](https://crates.io/crates/autocxx)
+[![docs.rs](https://docs.rs/autocxx/badge.svg)](https://docs.rs/autocxx)
+
+# autocxx — automatic safe interop between Rust and C++
+
+Welcome to `autocxx` and thank you for reading!
+
+Use `autocxx` if you have a large existing C++ codebase and you want to use its types and functions from Rust with maximal safety and minimal fuss.
+
+`autocxx` is like `bindgen`, in that it enables you to use C++ functions and types from within Rust. But it automates a lot of the fiddly things you need to do with `bindgen` bindings: calling destructors, converting strings, unsafely handling raw pointers. C++ functions and types within `autocxx` bindings should behave naturally and ergonomically, _almost_ as if they were safe Rust functions and types themselves. These ergonomics and safety improvements come from the [`cxx`](https://cxx.rs) project - hence the name of this tool, `autocxx`.
+
+`autocxx` combines the safety and ergonomics of `cxx` with the automatic bindings generation of `bindgen`. It stands on the shoulders of those giants!
+
+## When is `autocxx` the right tool?
+
+Not always:
+
+* If you are making bindings to C code, as opposed to C++, use [`bindgen`](https://rust-lang.github.io/rust-bindgen/) instead.
+* If you can make unrestricted changes to the C++ code, use [`cxx`](https://cxx.rs) instead.
+* If your C++ to Rust interface is just a few functions or types, use [`cxx`](https://cxx.rs) instead.
+
+But sometimes:
+
+* If you need to call arbitrary functions and use arbitrary types within an existing C++ codebase, use `autocxx`. You're in the right place!
+* Like `cxx`, but unlike `bindgen`, `autocxx` helps with calls from C++ to Rust, too.
+
+## Examples to give you a feel for `autocxx`
+
+Here's a code example:
+
+```rust,ignore,autocxx
+autocxx_integration_tests::doctest(
+"",
+"#include <stdint.h>
+inline uint32_t do_math(uint32_t a, uint32_t b) { return a+b; }",
+{
+// Use all the autocxx types which might be handy.
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("do_math") // allowlist a function
+}
+
+fn main() {
+    assert_eq!(ffi::do_math(12, 13), 25);
+}
+}
+)
+```
+
+A more complex example:
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"
+#include <strstream>
+void Goat::add_a_horn() { horns++; }
+Goat::Goat() : horns(0) {}
+Goat::~Goat() {}
+std::string Goat::describe() const {
+    std::ostrstream oss;
+    std::string plural = horns == 1 ? \"\" : \"s\";
+    oss << \"This goat has \" << horns << \" horn\" << plural << \".\";
+    return oss.str();
+}
+",
+"#include <cstdint>
+#include <string>
+
+class Goat {
+public:
+    Goat();
+    ~Goat();
+    void add_a_horn();
+    std::string describe() const;
+private:
+    uint32_t horns;
+};
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("Goat") // allowlist a type and all its methods
+}
+
+fn main() {
+    let mut goat = ffi::Goat::make_unique(); // returns a cxx::UniquePtr, i.e. a std::unique_ptr
+    goat.pin_mut().add_a_horn();
+    goat.pin_mut().add_a_horn();
+    assert_eq!(goat.describe().as_ref().unwrap().to_string_lossy(), "This goat has 2 horns.");
+}
+}
+)
+```
+
+This is typical `autocxx` code: the C++ objects behave much like Rust objects, but
+sometimes extra steps are required to handle cases like null pointers or converting strings that may not be UTF-8.
+
+Still, fundamentally, you can interact with C++ objects without using `unsafe` in the majority of cases.
+`cxx` takes care of the fundamentals of lifetimes and destructors.
+
+## How to read this book
+
+We'd recommend starting with [tutorial](tutorial.md) and then [workflow](workflow.md).
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/naming.md b/third_party/rust/autocxx/v0_17/crate/book/src/naming.md
new file mode 100644
index 0000000..dee6aaa
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/naming.md
@@ -0,0 +1,77 @@
+# Naming
+
+## Namespaces
+
+The C++ namespace structure is reflected in mods within the generated
+ffi mod. However, at present there is an internal limitation that
+autocxx can't handle multiple types with the same identifier, even
+if they're in different namespaces. This will be fixed in future.
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"
+void generations::hey_boomer() {}
+void submarines::hey_boomer() {}",
+"
+namespace generations {
+  void hey_boomer();
+}
+namespace submarines {
+  void hey_boomer();
+}
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("submarines::hey_boomer")
+    generate!("generations::hey_boomer")
+}
+
+fn main() {
+    ffi::generations::hey_boomer(); // insults your elders and betters
+    ffi::submarines::hey_boomer(); // launches missiles
+}
+}
+)
+```
+
+## Nested types
+
+There is support for generating bindings of nested types, with some
+restrictions. Currently the C++ type `A::B` will be given the Rust name
+`A_B` in the same module as its enclosing namespace.
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"",
+"
+struct Turkey {
+    struct Duck {
+        struct Hen {
+            int wings;
+        };
+    };
+};
+",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate_pod!("Turkey_Duck_Hen")
+}
+
+fn main() {
+    let turducken = ffi::Turkey_Duck_Hen::make_unique();
+}
+}
+)
+```
+
+## Overloads
+
+See [the chapter on C++ functions](cpp_functions.md).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/other_features.md b/third_party/rust/autocxx/v0_17/crate/book/src/other_features.md
new file mode 100644
index 0000000..2cc95cd
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/other_features.md
@@ -0,0 +1,34 @@
+# Other C++ features
+
+You can make Rust subclasses of C++ classes - as these are mostly used to
+implement the Observer pattern, they're documented under [calls from C++ to Rust](rust_calls.md).
+
+## Exceptions
+
+Exceptions are not supported. If your C++ code is compiled with exceptions,
+you can expect serious runtime explosions. The underlying [`cxx`](https://cxx.rs) crate has
+exception support, so it would be possible to add them.
+
+## Preprocessor symbols
+
+`#define` and other preprocessor symbols will appear as constants.
+At present there is no way to do compile-time disablement of code
+(equivalent of `#ifdef`)[^ifdef].
+
+[^ifdef]: [This feature](https://github.com/google/autocxx/issues/57) should add ifdef support.
+
+## String constants
+
+Whether from a preprocessor symbol or from a C++ `char*` constant,
+strings appear as `[u8]` with a null terminator. To get a Rust string,
+do this:
+
+```cpp
+#define BOB "Hello"
+```
+
+```
+# mod ffi { pub static BOB: [u8; 6] = [72u8, 101u8, 108u8, 108u8, 111u8, 0u8]; }
+assert_eq!(std::str::from_utf8(&ffi::BOB).unwrap().trim_end_matches(char::from(0)), "Hello");
+```
+
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/primitives.md b/third_party/rust/autocxx/v0_17/crate/book/src/primitives.md
new file mode 100644
index 0000000..80e8e90
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/primitives.md
@@ -0,0 +1,78 @@
+# Built-in types
+
+`autocxx` relies primarily on the [standard cxx types](https://cxx.rs/bindings.html).
+In particular you should become familiar with [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html) and [`cxx::CxxString`](https://docs.rs/cxx/latest/cxx/struct.CxxString.html).
+
+There are a few additional integer types, such as [`c_int`](https://docs.rs/autocxx/latest/autocxx/struct.c_int.html),
+which are not yet upstreamed to `cxx`. These are to support those pesky C/C++ integer types
+which do not have a predictable number of bits on different machines.
+
+```rust,ignore,autocxx
+autocxx_integration_tests::doctest(
+"",
+"inline int do_math(int a, int b) { return a+b; }",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("do_math")
+}
+
+fn main() {
+    assert_eq!(ffi::do_math(c_int(12), c_int(13)), c_int(25));
+}
+}
+)
+```
+
+## Strings
+
+`autocxx` uses [`cxx::CxxString`](https://docs.rs/cxx/latest/cxx/struct.CxxString.html). However, as noted above, we can't
+just pass a C++ string by value, so we'll box and unbox it automatically
+such that you're really dealing with `UniquePtr<CxxString>` on the Rust
+side, even if the API just took or returned a plain old `std::string`.
+
+However, to ease ergonomics, functions that accept a `std::string` will
+actually accept anything that
+implements a trait called `ffi::ToCppString`. That may either be a
+`UniquePtr<CxxString>` or just a plain old Rust string - which will be
+converted transparently to a C++ string.
+
+This trait, and its implementations, are not present in the `autocxx`
+documentation because they're dynamically generated in _your_ code
+so that they can call through to a `make_string` implementation in
+the C++ that we're injecting into your C++ build system.
+
+(None of that happens if you use [`exclude_utilities`](https://docs.rs/autocxx/latest/autocxx/macro.exclude_utilities.html), so don't do that.)
+
+```rust,ignore,autocxx
+autocxx_integration_tests::doctest(
+"",
+"#include <string>
+#include <cstdint>
+inline uint32_t take_string(std::string a) { return a.size(); }",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("take_string")
+}
+
+fn main() {
+    assert_eq!(ffi::take_string("hello"), 5)
+}
+}
+)
+```
+
+If you need to create a blank `UniquePtr<CxxString>` in Rust, such that
+(for example) you can pass its mutable reference or pointer into some
+pre-existing C++ API, call `ffi::make_string("")` which will return
+a blank `UniquePtr<CxxString>`.
+
+If all you need is a _reference_ to a `CxxString`, you can alternatively use
+[`cxx::let_cpp_string`](https://docs.rs/cxx/latest/cxx/macro.let_cxx_string.html).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/references_etc.md b/third_party/rust/autocxx/v0_17/crate/book/src/references_etc.md
new file mode 100644
index 0000000..d0f618d6
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/references_etc.md
@@ -0,0 +1,70 @@
+# Pointers, references, values
+
+`autocxx` knows how to deal with C++ APIs which take C++ types:
+* By value
+* By reference (const or not)
+* By raw pointer
+* By `std::unique_ptr`
+* By `std::shared_ptr`
+* By `std::weak_ptr`
+* (Soon to come) By rvalue reference (that is, as a move parameter)
+
+(all of this is because the underlying [`cxx`](https://cxx.rs) crate has such versatility).
+Some of these have some quirks in the way they're exposed in Rust, described below.
+
+## Passing between C++ and Rust by value
+
+See the section on [C++ types](cpp_types.md) for the distinction between POD and non-POD types.
+POD types can be passed around however you like. Non-POD types can be passed into functions
+in various ways - see [calling C++ functions](cpp_functions.md) for more details.
+
+## References and pointers
+
+We follow [`cxx`](https://cxx.rs) norms here. Specifically:
+
+* A C++ reference becomes a Rust reference
+* A C++ pointer becomes a Rust pointer.
+* If a reference is returned with an ambiguous lifetime, we don't generate
+  code for the function
+* Pointers require use of `unsafe`, references don't necessarily.
+
+That last point is key. If your C++ API takes pointers, you're going
+to have to use `unsafe`. Similarly, if your C++ API returns a pointer,
+you'll have to use `unsafe` to do anything useful with the pointer in Rust.
+This is intentional: a pointer from C++ might be subject to concurrent
+mutation, or it might have a lifetime that could disappear at any moment.
+As a human, you must promise that you understand the constraints around
+use of that pointer and that's what the `unsafe` keyword is for.
+
+Exactly the same issues apply to C++ references _in theory_, but in practice,
+they usually don't. Therefore [`cxx`](https://cxx.rs) has taken the view that we can "trust"
+a C++ reference to a higher degree than a pointer, and autocxx follows that
+lead. In practice, of course, references are rarely return values from C++
+APIs so we rarely have to navel-gaze about the trustworthiness of a
+reference.
+
+(See also the discussion of [`safety`](safety.md) - if you haven't specified
+an unsafety policy, _all_ C++ APIs require `unsafe` so the discussion is moot.)
+
+If you're given a C++ object by pointer, and you want to interact with it,
+you'll need to figure out the guarantees attached to the C++ object - most
+notably its lifetime. To see some of the decision making process involved
+see the [Steam example](https://github.com/google/autocxx/tree/main/examples/steam-mini/src/main.rs).
+
+## [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html)s tips
+
+We use [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html) in completely the normal way, but there are a few
+quirks which you're more likely to run into with `autocxx`.
+
+* You'll need to use [`.pin_mut()`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html#method.pin_mut) a lot -
+  see [the example at the bottom of C++ functions](cpp_functions.md).
+* If you need to pass a raw pointer to a function, lots of unsafety is required - something like this:
+  ```rust,ignore
+     let mut a = ffi::A::make_unique();
+     unsafe { ffi::TakePointerToA(std::pin::Pin::<&mut ffi::A>::into_inner_unchecked(a.pin_mut())) };
+  ```
+  This may be simplified in future.
+
+## Rvalue references
+
+Currently rvalue references (that is, move parameters) are not supported.
\ No newline at end of file
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/rust_calls.md b/third_party/rust/autocxx/v0_17/crate/book/src/rust_calls.md
new file mode 100644
index 0000000..ebd37c5
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/rust_calls.md
@@ -0,0 +1,177 @@
+# Callbacks into Rust
+
+`autocxx` is primarily to allow calls from Rust to C++, but like `cxx` it also allows you to expose Rust APIs to C++.
+
+You can:
+* Declare that Rust types should be available to C++ using [`extern_rust_type`](https://docs.rs/autocxx/latest/autocxx/extern_rust/attr.extern_rust_type.html)
+* Make Rust functions available to C++ using [`extern_rust_function`](https://docs.rs/autocxx/latest/autocxx/extern_rust/attr.extern_rust_function.html).
+* Allow Rust subclasses of C++ classes.
+
+This latter option is most commonly used for implementing "listeners" or ["observers"](https://en.wikipedia.org/wiki/Observer_pattern), so is often in practice how C++ will call into Rust. More details below.
+
+## Subclasses
+
+There is limited and experimental support for creating Rust subclasses of
+C++ classes. (Yes, even more experimental than all the rest of this!)
+See [`subclass::CppSubclass`](https://docs.rs/autocxx/latest/autocxx/subclass/trait.CppSubclass.html) for information about how you do this.
+This is useful primarily if you want to listen out for messages broadcast
+using the C++ observer/listener pattern.
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"
+GoatObserver* obs = NULL;
+int goat_feed = 0;
+
+void register_observer(const GoatObserver& observer) {
+    obs = const_cast<GoatObserver*>(&observer);
+}
+void deregister_observer() {
+    obs = NULL;
+};
+void feed_goat() {
+    goat_feed++;
+    if (goat_feed > 2 && obs) {
+        obs->goat_full();
+    }
+}
+",
+"#include <memory>
+class GoatObserver {
+public:
+    virtual void goat_full() const = 0;
+    virtual ~GoatObserver() {}
+};
+
+void register_observer(const GoatObserver& observer);
+void deregister_observer();
+void feed_goat();
+",
+{
+use autocxx::prelude::*;
+use autocxx::subclass::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    generate!("register_observer")
+    generate!("deregister_observer")
+    generate!("feed_goat")
+    subclass!("GoatObserver", MyGoatObserver)
+}
+
+use ffi::*;
+
+#[is_subclass(superclass("GoatObserver"))]
+#[derive(Default)]
+pub struct MyGoatObserver;
+
+impl GoatObserver_methods for MyGoatObserver {
+    fn goat_full(&self) {
+        println!("BURP!");
+    }
+}
+
+impl Drop for MyGoatObserver {
+    fn drop(&mut self) {
+        deregister_observer();
+    }
+}
+
+fn main() {
+    let goat_obs = MyGoatObserver::default_rust_owned();
+    // Register a reference to the superclass &ffi::GoatObserver
+    register_observer(goat_obs.as_ref().borrow().as_ref());
+    feed_goat();
+    feed_goat();
+    feed_goat(); // prints BURP!
+}
+}
+)
+```
+
+## Subclass ownership
+
+See [`subclass::CppSubclass`](https://docs.rs/autocxx/latest/autocxx/subclass/trait.CppSubclass.html)
+for full details, but you must decide who owns your subclass:
+
+* C++ owns it
+* Rust owns it
+* It's self-owned, and only ever frees itself (using [`delete_self`](https://docs.rs/autocxx/latest/autocxx/subclass/trait.CppSubclassSelfOwned.html#method.delete_self)).
+
+Please be careful: the observer pattern is a minefield for use-after-free bugs.
+It's recommended that you wrap any such subclass in some sort of Rust newtype
+wrapper which [enforces any ownership invariants](rustic.md) so that users
+of your types literally can't make any mistakes.
+
+## Calling superclass methods
+
+Each subclass also implements a trait called `<superclass name>_supers` which
+includes all superclass methods. You can call methods on that, and if you
+don't implement a particular method, that will be used as the default.
+
+```rust,ignore,autocxx,hidecpp
+autocxx_integration_tests::doctest(
+"",
+"
+#include <iostream>
+class Dinosaur {
+public:
+    Dinosaur() {}
+    virtual void eat() const {
+        std::cout << \"Roarrr!! I ate you!\n\";
+    }
+    virtual ~Dinosaur() {}
+};
+
+// Currently, autocxx requires at least one 'generate!' call.
+inline void do_a_thing() {};
+",
+{
+use autocxx::prelude::*;
+use autocxx::subclass::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe_ffi)
+    subclass!("Dinosaur", TRex)
+    subclass!("Dinosaur", Diplodocus)
+    generate!("do_a_thing")
+}
+
+use ffi::*;
+
+#[is_subclass(superclass("Dinosaur"))]
+#[derive(Default)]
+pub struct TRex;
+
+#[is_subclass(superclass("Dinosaur"))]
+#[derive(Default)]
+pub struct Diplodocus;
+
+impl Dinosaur_methods for TRex {
+    // TRex does NOT implement the 'eat' method
+    // so C++ behavior will be used
+}
+
+impl Dinosaur_methods for Diplodocus {
+    fn eat(&self) {
+        println!("Ahh, some nice juicy leaves.");
+        // Could call self.eat_super() if we
+        // developed unexpected carnivorous cravings.
+    }
+}
+
+fn main() {
+    let trex = TRex::default_rust_owned();
+    trex.borrow().as_ref().eat(); // eats human
+    let diplo = Diplodocus::default_rust_owned();
+    diplo.borrow().as_ref().eat(); // eats shoots and leaves
+}
+}
+)
+```
+
+## Subclass casting
+
+Subclasses implement `AsRef` to enable casting to superclasses.
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/rustic.md b/third_party/rust/autocxx/v0_17/crate/book/src/rustic.md
new file mode 100644
index 0000000..09511b6
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/rustic.md
@@ -0,0 +1,13 @@
+# Using the generated bindings
+
+Congratulations, you've built some bindings using `autocxx`!
+
+But are they Rustic? How can you ensure that users of the bindings get Rust-like safety?
+
+The C++ API may have documented usage invariants. Your ideal is to encode as many as possible of those into compile-time checks in Rust.
+
+Some options to consider:
+
+* Wrap the bindings in a newtype wrapper which enforces compile-time variants in its APIs; for example, taking a mutable reference to enforce exclusive access.
+* Add extra `impl` blocks to add methods with a more Rustic API.
+* Read [the C++ to Rust design FAQ](https://cppfaq.rs).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/safety.md b/third_party/rust/autocxx/v0_17/crate/book/src/safety.md
new file mode 100644
index 0000000..34724d2
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/safety.md
@@ -0,0 +1,91 @@
+# Safety
+
+## Unsafety policies
+
+By default, every `autocxx` function is `unsafe`. That means you can only call C++ functions from `unsafe` blocks, and it's up to you to be sure that the C++ code upholds the invariants the Rust compiler expects.
+
+You can optionally specify:
+
+`safety!(unsafe)`
+
+within your `include_cpp!` macro invocation. If you do this, you are promising the Rust compiler that _all_ your C++ function calls are upholding the invariants which `rustc` expects, and thus each individual function is no longer `unsafe`.
+
+See [`safety!`](https://docs.rs/autocxx/latest/autocxx/macro.safety.html) in the documentation for more details.
+
+## Examples with and without `safety!(unsafe)`
+
+Without a `safety!` directive:
+
+```rust,ignore,autocxx
+autocxx_integration_tests::doctest(
+"",
+"#include <cstdint>
+inline uint32_t do_math(uint32_t a, uint32_t b) { return a+b; }",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    generate!("do_math")
+}
+
+fn main() {
+    assert_eq!(unsafe { ffi::do_math(12, 13) }, 25);
+}
+}
+)
+```
+
+With a `safety!` directive:
+
+```rust,ignore,autocxx
+autocxx_integration_tests::doctest(
+"",
+"#include <cstdint>
+inline uint32_t do_math(uint32_t a, uint32_t b) { return a+b; }",
+{
+use autocxx::prelude::*;
+
+include_cpp! {
+    #include "input.h"
+    safety!(unsafe)
+    generate!("do_math")
+}
+
+fn main() {
+    assert_eq!(ffi::do_math(12, 13), 25);
+}
+}
+)
+```
+
+
+## Pragmatism in a complex C++ codebase
+
+This crate mostly intends to follow the lead of the `cxx` crate in where and when `unsafe` is required. But, this crate is opinionated. It believes some unsafety requires more careful review than other bits, along the following spectrum:
+
+* Rust unsafe code (requires most review)
+* Rust code calling C++ with raw pointers
+* Rust code calling C++ with shared pointers, or anything else where there can be concurrent mutation
+* Rust code calling C++ with unique pointers, where the Rust single-owner model nearly always applies (but we can't _prove_ that the C++ developer isn't doing something weird)
+* Rust safe code (requires least review)
+
+If your project is 90% Rust code, with small bits of C++, _don't use this crate_. You need something where all C++ interaction is marked with big red "this is terrifying" flags. This crate is aimed at cases where there's 90% C++ and small bits of Rust, and so we want the Rust code to be pragmatically reviewable without the signal:noise ratio of `unsafe` in the Rust code becoming so bad that `unsafe` loses all value.
+
+## Worked example
+
+Imagine you have this C++:
+
+```cpp
+struct Thing;
+void print_thing(const Thing& thing);
+```
+
+By using `autocxx` (or `cxx`), you're promising the Rust compiler that the `print_thing` function does sensible things with that
+reference:
+
+* It doesn't store a pointer to the thing anywhere and pass it back to Rust later.
+* It doesn't mutate it.
+* It doesn't delete it.
+* or any of the other things that you're not permitted to do in unsafe Rust.
+
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/storage.md b/third_party/rust/autocxx/v0_17/crate/book/src/storage.md
new file mode 100644
index 0000000..5f79ca96
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/storage.md
@@ -0,0 +1,24 @@
+# Storage - stack and heaps
+
+Ensure you understand the distinction between [POD and non-POD types described in the C++ types section before proceeding](cpp_types.md).
+
+## POD types
+
+POD types are just regular Rust types! Store them on the stack, heap, in a `Vec`, a `HashMap`, whatever you want.
+
+## Non-POD types
+
+Non-POD types can be stored:
+
+* In a [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html). This is cxx's Rust wrapper for `std::unique_ptr` - so the object is stored in the C++ heap. Most of the time you handle a C++ object from `autocxx`, it will be stored in one of these.
+* In a [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) - so the object is stored on the Rust heap. This is kind of pointless. Don't do this.
+* On the Rust stack, using the [`autocxx::moveit`](https://docs.rs/moveit/latest/moveit/macro.moveit.html) macro.
+
+If in doubt, use [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html). It's simple and ergonomic.
+
+See [C++ types](cpp_types.md) for a code example showing a type existing on both the stack and the heap.
+
+## Whose heap is it anyway?
+
+Specifically [`cxx::UniquePtr`](https://docs.rs/cxx/latest/cxx/struct.UniquePtr.html) is a binding to `std::unique_ptr<T,std::default_delete<T>>` which means the object will be deleted using the C++ `delete` operator. This will respect any overridden `operator delete` on the type, and similarly, the functions which `autocxx` provides to _construct_ types should respect overridden `operator new`. This means: if your C++ type has code to create itself in some special or unusual heap partition, that should work fine.
+
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/tutorial.md b/third_party/rust/autocxx/v0_17/crate/book/src/tutorial.md
new file mode 100644
index 0000000..96b3786b
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/tutorial.md
@@ -0,0 +1,67 @@
+# Tutorial
+
+If you're here, you want to call some C++ from Rust, right?
+
+Let's assume you're calling into some _existing_ C++ code.
+
+You will need:
+
+* Some C++ header files (`.h` files)
+* The C++ "include path". That is, the set of directories containing those headers. (That's not necessarily the directory in which each header _file_ lives; C++ might contain `#include "foo/bar.h"` and so your include path would need to include the directory _containing_ the `foo` directory).
+* A list of the APIs (types and functions) from those header files which you wish to make available in Rust.
+* To know how to link the C++ libraries into your Cargo project. This is beyond the scope of what `autocxx` helps with, but one solution is to emit a print from your [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib).
+* [LLVM to be installed](https://rust-lang.github.io/rust-bindgen/requirements.html).
+* Some patience. This is not a magic solution. C++/Rust interop is hard. Avoid it if you can!
+
+The rest of this 'getting started' section assumes Cargo - if you're using something else, see the [building](building.md) section.
+
+First, add `autocxx` *and `cxx`* to your `dependencies` and `autocxx-build` to your `build-dependencies` in your `Cargo.toml`.
+
+```toml
+[dependencies]
+autocxx = "0.17.2"
+cxx = "1.0"
+
+[build-dependencies]
+autocxx-build = "0.17.2"
+```
+
+Now, add a `build.rs` next to your `Cargo.toml` (this is a standard `cargo` [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html)). This is where you need your include path:
+
+```rust,ignore
+fn main() {
+    let path = std::path::PathBuf::from("src"); // include path
+    let mut b = autocxx_build::Builder::new("src/main.rs", &[&path]).expect_build();
+        // This assumes all your C++ bindings are in main.rs
+    b.flag_if_supported("-std=c++14")
+     .compile("autocxx-demo"); // arbitrary library name, pick anything
+    println!("cargo:rerun-if-changed=src/main.rs");
+    // Add instructions to link to any C++ libraries you need.
+}
+```
+
+See [the standard cargo build script output mechanisms for how you can direct Rust to link against pre-existing libraries](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script), and see the [building section of this book for more details about build options - for example, enabling C++17](building.md).
+
+Finally, in your `main.rs` you can use the [`include_cpp`](https://docs.rs/autocxx/latest/autocxx/macro.include_cpp.html) macro which is the heart of `autocxx`:
+
+```rust,ignore
+use autocxx::prelude::*; // use all the main autocxx functions
+
+include_cpp! {
+    #include "my_header.h" // your header file name
+    safety!(unsafe) // see details of unsafety policies described in the 'safety' section of the book
+    generate!("DeepThought") // add this line for each function or type you wish to generate
+}
+```
+
+You should then find you can call the function by referring to an `ffi` namespace:
+
+```rust,ignore
+fn main() {
+    println!("The answer to Life, The Universe and Everything is {}", ffi::DeepThought());
+}
+```
+
+C++ types such as `std::string` and `std::unique_ptr` are represented using the types provided by the marvellous [cxx](https://cxx.rs) library. This provides good ergonomics and safety norms, so unlike with normal `bindgen` bindings, you won't _normally_ need to write `unsafe` code for every function call.
+
+Next, read the section about [workflows](workflow.md).
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/vscode1.png b/third_party/rust/autocxx/v0_17/crate/book/src/vscode1.png
new file mode 100644
index 0000000..c57b4d0
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/vscode1.png
Binary files differ
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/vscode2.png b/third_party/rust/autocxx/v0_17/crate/book/src/vscode2.png
new file mode 100644
index 0000000..c350617
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/vscode2.png
Binary files differ
diff --git a/third_party/rust/autocxx/v0_17/crate/book/src/workflow.md b/third_party/rust/autocxx/v0_17/crate/book/src/workflow.md
new file mode 100644
index 0000000..65cf3c5
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/book/src/workflow.md
@@ -0,0 +1,89 @@
+# Workflow
+
+C++ is complex, and `autocxx` can't ingest everything.
+
+First tip - use an IDE. Type annotation and autocompletion is _incredibly_ helpful in an `autocxx`
+context, where you may be dealing with `UniquePtr<T>` and `Option<&T>` and `Pin<&mut T>` very often.
+![VSCode autocompletion of autocxx APIs](vscode1.png)
+
+As you'll see, it's also _essential_ when `autocxx` can't produce bindings for some reason.
+
+## What if `autocxx` can't generate bindings?
+
+This bit is important.
+
+When you use `autocxx`, you'll ask it to generate Rust bindings for [C++ types or functions](allowlist.md) using
+`generate!` directives.
+
+If you ask to generate bindings for a specific function, and it can't: the build will fail.
+
+If you ask to generate bindings for an entire type, `autocxx` will generate bindings for as
+many methods as possible. For those methods where it can't generate bindings, it will instead
+generate some placeholder function or struct with documentation explaining what went wrong:
+
+![VSCode showing an error for an API where autocxx couldn't generate bindings](vscode2.png)
+
+_This_ is why it's crucial to use an IDE with `autocxx`. (Alternatively, you can use
+`cargo expand`, but it's unpleasant.)
+
+## How to work around cases where `autocxx` can't generate bindings
+
+Your options are:
+
+* Write extra C++ functions with simpler parameters or return types, and generate
+  bindings to them, instead.
+* Write some manual `#[cxx::bridge]` bindings - see below.
+
+## Mixing manual and automated bindings
+
+`autocxx` uses [`cxx`](https://cxx.rs) underneath, and its build process will happily spot and
+process manually-crafted [`cxx::bridge` mods](https://cxx.rs/concepts.html) which you include in your
+Rust source code. A common pattern could be to use `autocxx` to generate
+all the bindings possible, then hand-craft a `cxx::bridge` mod for the
+remainder where `autocxx` falls short.
+
+To do this, you'll need to use the [ability of one cxx::bridge mod to refer to types from another](https://cxx.rs/extern-c++.html#reusing-existing-binding-types),
+for example:
+
+```rust,ignore
+autocxx::include_cpp! {
+    #include "foo.h"
+    safety!(unsafe_ffi)
+    generate!("take_A")
+    generate!("A")
+}
+#[cxx::bridge]
+mod ffi2 {
+    unsafe extern "C++" {
+        include!("foo.h");
+        type A = crate::ffi::A;
+        fn give_A() -> UniquePtr<A>; // in practice, autocxx could happily do this
+    }
+}
+fn main() {
+    let a = ffi2::give_A();
+    assert_eq!(ffi::take_A(&a), autocxx::c_int(5));
+}
+```
+
+## My build entirely failed
+
+`autocxx` should nearly always successfully parse the C++ codebase and
+generate _some_ APIs. It's reliant on `bindgen`, but `bindgen` is excellent
+and rarely bails out entirely.
+
+If it does, you may be able to use the [`block!` macro](https://docs.rs/autocxx/latest/autocxx/macro.block.html).
+
+We'd appreciate a minimized bug report of the troublesome code - see [contributing](contributing.md).
+
+
+## Enabling autocompletion in a rust-analyzer IDE
+
+You'll need to enable _both_:
+* Rust-analyzer: Proc Macro: Enable
+* Rust-analyzer: Experimental: Proc Attr Macros
+
+## Next steps
+
+Now you've read what can go wrong with `autocxx`, and how to diagnose problems - the next step is to give it a try!
+Treat the rest of this manual as a reference.
diff --git a/third_party/rust/autocxx/v0_17/crate/src/lib.rs b/third_party/rust/autocxx/v0_17/crate/src/lib.rs
new file mode 100644
index 0000000..fad1b16f
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/src/lib.rs
@@ -0,0 +1,461 @@
+#![doc = include_str!("../README.md")]
+
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// The crazy macro_rules magic in this file is thanks to dtolnay@
+// and is a way of attaching rustdoc to each of the possible directives
+// within the include_cpp outer macro. None of the directives actually
+// do anything - all the magic is handled entirely by
+// autocxx_macro::include_cpp_impl.
+
+pub mod subclass;
+mod value_param;
+
+#[cfg_attr(doc, aquamarine::aquamarine)]
+/// Include some C++ headers in your Rust project.
+///
+/// This macro allows you to include one or more C++ headers within
+/// your Rust code, and call their functions fairly naturally.
+///
+/// # Examples
+///
+/// C++ header (`input.h`):
+/// ```cpp
+/// #include <cstdint>
+///
+/// uint32_t do_math(uint32_t a);
+/// ```
+///
+/// Rust code:
+/// ```
+/// # use autocxx_macro::include_cpp_impl as include_cpp;
+/// include_cpp!(
+/// #   parse_only!()
+///     #include "input.h"
+///     generate!("do_math")
+///     safety!(unsafe)
+/// );
+///
+/// # mod ffi { pub fn do_math(a: u32) -> u32 { a+3 } }
+/// # fn main() {
+/// ffi::do_math(3);
+/// # }
+/// ```
+///
+/// The resulting bindings will use idiomatic Rust wrappers for types from the [cxx]
+/// crate, for example [`cxx::UniquePtr`] or [`cxx::CxxString`]. Due to the care and thought
+/// that's gone into the [cxx] crate, such bindings are pleasant and idiomatic to use
+/// from Rust, and usually don't require the `unsafe` keyword.
+///
+/// For full documentation, see [the manual](https://google.github.io/autocxx/).
+///
+/// # The [`include_cpp`] macro
+///
+/// Within the braces of the `include_cpp!{...}` macro, you should provide
+/// a list of at least the following:
+///
+/// * `#include "cpp_header.h"`: a header filename to parse and include
+/// * `generate!("type_or_function_name")`: a type or function name whose declaration
+///   should be made available to C++. (See the section on Allowlisting, below).
+/// * Optionally, `safety!(unsafe)` - see discussion of [`safety`].
+///
+/// Other directives are possible as documented in this crate.
+///
+/// Now, try to build your Rust project. `autocxx` may fail to generate bindings
+/// for some of the items you specified with [generate] directives: remove
+/// those directives for now, then see the next section for advice.
+///
+/// # Allowlisting
+///
+/// How do you inform autocxx which bindings to generate? There are three
+/// strategies:
+///
+/// * *Recommended*: provide various [`generate`] directives in the
+///   [`include_cpp`] macro. This can specify functions or types.
+/// * *Not recommended*: in your `build.rs`, call [`Builder::auto_allowlist`].
+///   This will attempt to spot _uses_ of FFI bindings anywhere in your Rust code
+///   and build the allowlist that way. This is experimental and has known limitations.
+/// * *Strongly not recommended*: use [`generate_all`]. This will attempt to
+///   generate Rust bindings for _any_ C++ type or function discovered in the
+///   header files. This is generally a disaster if you're including any
+///   remotely complex header file: we'll try to generate bindings for all sorts
+///   of STL types. This will be slow, and some may well cause problems.
+///   Effectively this is just a debug option to discover such problems. Don't
+///   use it!
+///
+/// # Internals
+///
+/// For documentation on how this all actually _works_, see
+/// `IncludeCppEngine` within the `autocxx_engine` crate.
+#[macro_export]
+macro_rules! include_cpp {
+    (
+        $(#$include:ident $lit:literal)*
+        $($mac:ident!($($arg:tt)*))*
+    ) => {
+        $($crate::$include!{__docs})*
+        $($crate::$mac!{__docs})*
+        $crate::include_cpp_impl! {
+            $(#include $lit)*
+            $($mac!($($arg)*))*
+        }
+    };
+}
+
+/// Include a C++ header. A directive to be included inside
+/// [include_cpp] - see [include_cpp] for details
+#[macro_export]
+macro_rules! include {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Generate Rust bindings for the given C++ type or function.
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+/// See also [generate_pod].
+#[macro_export]
+macro_rules! generate {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Generate as "plain old data" and add to allowlist.
+/// Generate Rust bindings for the given C++ type such that
+/// it can be passed and owned by value in Rust. This only works
+/// for C++ types which have trivial move constructors and no
+/// destructor - you'll encounter a compile error otherwise.
+/// If your type doesn't match that description, use [generate]
+/// instead, and own the type using [UniquePtr][cxx::UniquePtr].
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+#[macro_export]
+macro_rules! generate_pod {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Generate Rust bindings for all C++ types and functions
+/// in a given namespace.
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+/// See also [generate].
+#[macro_export]
+macro_rules! generate_ns {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Generate Rust bindings for all C++ types and functions
+/// found. Highly experimental and not recommended.
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+/// See also [generate].
+#[macro_export]
+macro_rules! generate_all {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Generate as "plain old data". For use with [generate_all]
+/// and similarly experimental.
+#[macro_export]
+macro_rules! pod {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Skip the normal generation of a `make_string` function
+/// and other utilities which we might generate normally.
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+#[macro_export]
+macro_rules! exclude_utilities {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Entirely block some type from appearing in the generated
+/// code. This can be useful if there is a type which is not
+/// understood by bindgen or autocxx, and incorrect code is
+/// otherwise generated.
+/// This is 'greedy' in the sense that any functions/methods
+/// which take or return such a type will _also_ be blocked.
+///
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+#[macro_export]
+macro_rules! block {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Avoid generating implicit constructors for this type.
+/// The rules for when to generate C++ implicit constructors
+/// are complex, and if autocxx gets it wrong, you can block
+/// such constructors using this.
+///
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+#[macro_export]
+macro_rules! block_constructors {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// The name of the mod to be generated with the FFI code.
+/// The default is `ffi`.
+///
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+#[macro_export]
+macro_rules! name {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Specifies a global safety policy for functions generated
+/// from these headers. By default (without such a `safety!`
+/// directive) all such functions are marked as `unsafe` and
+/// therefore can only be called within an `unsafe {}` block
+/// or some `unsafe` function which you create.
+///
+/// Alternatively, by specifying a `safety!` block you can
+/// declare that most generated functions are in fact safe.
+/// Specifically, you'd specify:
+/// `safety!(unsafe)`
+/// or
+/// `safety!(unsafe_ffi)`
+/// These two options are functionally identical. If you're
+/// unsure, simply use `unsafe`. The reason for the
+/// latter option is if you have code review policies which
+/// might want to give a different level of scrutiny to
+/// C++ interop as opposed to other types of unsafe Rust code.
+/// Maybe in your organization, C++ interop is less scary than
+/// a low-level Rust data structure using pointer manipulation.
+/// Or maybe it's more scary. Either way, using `unsafe` for
+/// the data structure and using `unsafe_ffi` for the C++
+/// interop allows you to apply different linting tools and
+/// policies to the different options.
+///
+/// Irrespective, C++ code is of course unsafe. It's worth
+/// noting that use of C++ can cause unexpected unsafety at
+/// a distance in faraway Rust code. As with any use of the
+/// `unsafe` keyword in Rust, *you the human* are declaring
+/// that you've analyzed all possible ways that the code
+/// can be used and you are guaranteeing to the compiler that
+/// no badness can occur. Good luck.
+///
+/// Generated C++ APIs which use raw pointers remain `unsafe`
+/// no matter what policy you choose.
+#[macro_export]
+macro_rules! safety {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Whether to avoid generating [`cxx::UniquePtr`] and [`cxx::Vector`]
+/// implementations. This is primarily useful for reducing test cases and
+/// shouldn't be used in normal operation.
+///
+/// A directive to be included inside
+/// [include_cpp] - see [include_cpp] for general information.
+#[macro_export]
+macro_rules! exclude_impls {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// Deprecated - use [`extern_rust_type`] instead.
+#[macro_export]
+#[deprecated]
+macro_rules! rust_type {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// See [`extern_rust::extern_rust_type`].
+#[macro_export]
+macro_rules! extern_rust_type {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+/// See [`subclass::subclass`].
+#[macro_export]
+macro_rules! subclass {
+    ($($tt:tt)*) => { $crate::usage!{$($tt)*} };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! usage {
+    (__docs) => {};
+    ($($tt:tt)*) => {
+        compile_error! {r#"usage:  include_cpp! {
+                   #include "path/to/header.h"
+                   generate!(...)
+                   generate_pod!(...)
+               }
+"#}
+    };
+}
+
+#[doc(hidden)]
+pub use autocxx_macro::include_cpp_impl;
+
+#[doc(hidden)]
+pub use autocxx_macro::cpp_semantics;
+
+macro_rules! ctype_wrapper {
+    ($r:ident, $c:expr, $d:expr) => {
+        #[doc=$d]
+        #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash)]
+        #[allow(non_camel_case_types)]
+        #[repr(transparent)]
+        pub struct $r(pub ::std::os::raw::$r);
+
+        /// # Safety
+        ///
+        /// We assert that the namespace and type ID refer to a C++
+        /// type which is equivalent to this Rust type.
+        unsafe impl cxx::ExternType for $r {
+            type Id = cxx::type_id!($c);
+            type Kind = cxx::kind::Trivial;
+        }
+
+        impl From<::std::os::raw::$r> for $r {
+            fn from(val: ::std::os::raw::$r) -> Self {
+                Self(val)
+            }
+        }
+
+        impl From<$r> for ::std::os::raw::$r {
+            fn from(val: $r) -> Self {
+                val.0
+            }
+        }
+    };
+}
+
+ctype_wrapper!(
+    c_ulonglong,
+    "c_ulonglong",
+    "Newtype wrapper for an unsigned long long"
+);
+ctype_wrapper!(c_longlong, "c_longlong", "Newtype wrapper for a long long");
+ctype_wrapper!(c_ulong, "c_ulong", "Newtype wrapper for an unsigned long");
+ctype_wrapper!(c_long, "c_long", "Newtype wrapper for a long");
+ctype_wrapper!(
+    c_ushort,
+    "c_ushort",
+    "Newtype wrapper for an unsigned short"
+);
+ctype_wrapper!(c_short, "c_short", "Newtype wrapper for an short");
+ctype_wrapper!(c_uint, "c_uint", "Newtype wrapper for an unsigned int");
+ctype_wrapper!(c_int, "c_int", "Newtype wrapper for an int");
+ctype_wrapper!(c_uchar, "c_uchar", "Newtype wrapper for an unsigned char");
+
+/// Newtype wrapper for a C void. Only useful as a `*c_void`
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+pub struct c_void(pub ::std::os::raw::c_void);
+
+/// # Safety
+///
+/// We assert that the namespace and type ID refer to a C++
+/// type which is equivalent to this Rust type.
+unsafe impl cxx::ExternType for c_void {
+    type Id = cxx::type_id!(c_void);
+    type Kind = cxx::kind::Trivial;
+}
+
+/// autocxx couldn't generate these bindings.
+/// If you come across a method, type or function which refers to this type,
+/// it indicates that autocxx couldn't generate that binding. A documentation
+/// comment should be attached indicating the reason.
+pub struct BindingGenerationFailure {
+    _unallocatable: [*const u8; 0],
+    _pinned: core::marker::PhantomData<core::marker::PhantomPinned>,
+}
+
+/// Tools to export Rust code to C++.
+// These are in a mod to avoid shadowing the definitions of the
+// directives above, which, being macro_rules, are unavoidably
+// in the crate root but must be function-style macros to keep
+// the include_cpp impl happy.
+pub mod extern_rust {
+
+    /// Declare that this is a Rust type which is to be exported to C++.
+    /// You can use this in two ways:
+    /// * as an attribute macro on a Rust type, for instance:
+    ///   ```
+    ///   # use autocxx_macro::extern_rust_type as extern_rust_type;
+    ///   #[extern_rust_type]
+    ///   struct Bar;
+    ///   ```
+    /// * as a directive within the [include_cpp] macro, in which case
+    ///   provide the type path in brackets:
+    ///   ```
+    ///   # use autocxx_macro::include_cpp_impl as include_cpp;
+    ///   include_cpp!(
+    ///   #   parse_only!()
+    ///       #include "input.h"
+    ///       extern_rust_type!(Bar)
+    ///       safety!(unsafe)
+    ///   );
+    ///   struct Bar;
+    ///   ```
+    /// These may be used within references in the signatures of C++ functions,
+    /// for instance. This will contribute to an `extern "Rust"` section of the
+    /// generated `cxx` bindings, and this type will appear in the C++ header
+    /// generated for use in C++.
+    pub use autocxx_macro::extern_rust_type;
+
+    /// Declare that a given function is a Rust function which is to be exported
+    /// to C++. This is used as an attribute macro on a Rust function, for instance:
+    /// ```
+    /// # use autocxx_macro::extern_rust_function as extern_rust_function;
+    /// #[extern_rust_function]
+    /// pub fn call_me_from_cpp() { }
+    /// ```
+    pub use autocxx_macro::extern_rust_function;
+}
+
+/// Equivalent to [`std::convert::AsMut`], but returns a pinned mutable reference
+/// such that cxx methods can be called on it.
+pub trait PinMut<T>: AsRef<T> {
+    /// Return a pinned mutable reference to a type.
+    fn pin_mut(&mut self) -> std::pin::Pin<&mut T>;
+}
+
+pub use value_param::as_copy;
+pub use value_param::as_mov;
+pub use value_param::as_new;
+pub use value_param::ValueParam;
+pub use value_param::ValueParamHandler;
+
+/// Imports which you're likely to want to use.
+pub mod prelude {
+    pub use crate::as_copy;
+    pub use crate::as_mov;
+    pub use crate::as_new;
+    pub use crate::c_int;
+    pub use crate::c_long;
+    pub use crate::c_longlong;
+    pub use crate::c_short;
+    pub use crate::c_uchar;
+    pub use crate::c_uint;
+    pub use crate::c_ulong;
+    pub use crate::c_ulonglong;
+    pub use crate::c_ushort;
+    pub use crate::c_void;
+    pub use crate::cpp_semantics;
+    pub use crate::include_cpp;
+    pub use crate::PinMut;
+    pub use crate::ValueParam;
+    pub use moveit::moveit;
+    pub use moveit::new::New;
+}
+
+/// Re-export moveit for ease of consumers.
+pub use moveit;
+
+/// Re-export cxx such that clients can use the same version as
+/// us. This doesn't enable clients to avoid depending on the cxx
+/// crate too, unfortunately, since generated cxx::bridge code
+/// refers explicitly to ::cxx. See
+/// <https://github.com/google/autocxx/issues/36>
+pub use cxx;
diff --git a/third_party/rust/autocxx/v0_16/crate/src/subclass.rs b/third_party/rust/autocxx/v0_17/crate/src/subclass.rs
similarity index 93%
rename from third_party/rust/autocxx/v0_16/crate/src/subclass.rs
rename to third_party/rust/autocxx/v0_17/crate/src/subclass.rs
index 564528af..d2827ca 100644
--- a/third_party/rust/autocxx/v0_16/crate/src/subclass.rs
+++ b/third_party/rust/autocxx/v0_17/crate/src/subclass.rs
@@ -3,17 +3,11 @@
 
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::{
     cell::RefCell,
@@ -74,11 +68,14 @@
     };
 }
 
+/// A trait representing the C++ side of a Rust/C++ subclass pair.
 #[doc(hidden)]
 pub trait CppSubclassCppPeer: UniquePtrTarget {
     fn relinquish_ownership(&self);
 }
 
+/// A type used for how the C++ side of a Rust/C++ subclass pair refers to
+/// the Rust side.
 #[doc(hidden)]
 pub enum CppSubclassRustPeerHolder<T> {
     Owned(Rc<RefCell<T>>),
@@ -102,6 +99,8 @@
     }
 }
 
+/// A type showing how the Rust side of a Rust/C++ subclass pair refers to
+/// the C++ side.
 #[doc(hidden)]
 pub enum CppSubclassCppPeerHolder<CppPeer: CppSubclassCppPeer> {
     Empty,
@@ -121,6 +120,8 @@
             CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
             CppSubclassCppPeerHolder::Owned(peer) => peer.pin_mut(),
             CppSubclassCppPeerHolder::Unowned(peer) => unsafe {
+                // Safety: guaranteed safe because this is a pointer to a C++ object,
+                // and C++ never moves things in memory.
                 Pin::new_unchecked(peer.as_mut().unwrap())
             },
         }
@@ -129,6 +130,8 @@
         match self {
             CppSubclassCppPeerHolder::Empty => panic!("Peer not set up"),
             CppSubclassCppPeerHolder::Owned(peer) => peer.as_ref(),
+            // Safety: guaranteed safe because this is a pointer to a C++ object,
+            // and C++ never moves things in memory.
             CppSubclassCppPeerHolder::Unowned(peer) => unsafe { peer.as_ref().unwrap() },
         }
     }
@@ -136,6 +139,8 @@
         *self = Self::Owned(Box::new(peer));
     }
     fn set_unowned(&mut self, peer: &mut UniquePtr<CppPeer>) {
+        // Safety: guaranteed safe because this is a pointer to a C++ object,
+        // and C++ never moves things in memory.
         *self = Self::Unowned(unsafe {
             std::pin::Pin::<&mut CppPeer>::into_inner_unchecked(peer.pin_mut())
         });
diff --git a/third_party/rust/autocxx/v0_17/crate/src/value_param.rs b/third_party/rust/autocxx/v0_17/crate/src/value_param.rs
new file mode 100644
index 0000000..ee21a506
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/src/value_param.rs
@@ -0,0 +1,264 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use cxx::{memory::UniquePtrTarget, UniquePtr};
+use moveit::{CopyNew, DerefMove, MoveNew, New};
+use std::{marker::PhantomPinned, mem::MaybeUninit, ops::Deref, pin::Pin};
+
+/// A trait representing a parameter to a C++ function which is received
+/// by value.
+///
+/// Rust has the concept of receiving parameters by _move_ or by _reference_.
+/// C++ has the concept of receiving a parameter by 'value', which means
+/// the parameter gets copied.
+///
+/// To make it easy to pass such parameters from Rust, this trait exists.
+/// It is implemented both for references `&T` and for `UniquePtr<T>`,
+/// subject to the presence or absence of suitable copy and move constructors.
+/// This allows you to pass in parameters by copy (as is ergonomic and normal
+/// in C++) retaining the original parameter; or by move semantics thus
+/// destroying the object you're passing in. Simply use a reference if you want
+/// copy semantics, or the item itself if you want move semantics.
+///
+/// It is not recommended that you implement this trait, nor that you directly
+/// use its methods, which are for use by `autocxx` generated code only.
+///
+/// # Use of `moveit` traits
+///
+/// Most of the implementations of this trait require the type to implement
+/// [`CopyNew`], which is simply the `autocxx`/`moveit` way of saying that
+/// the type has a copy constructor in C++.
+///
+/// # Being explicit
+///
+/// If you wish to explicitly force either a move or a copy of some type,
+/// use [`as_mov`] or [`as_copy`].
+///
+/// # Performance
+///
+/// At present, some additional copying occurs for all implementations of
+/// this trait other than that for [`cxx::UniquePtr`]. In the future it's
+/// hoped that the implementation for `&T where T: CopyNew` can also avoid
+/// this extra copying.
+///
+/// # Panics
+///
+/// The implementations of this trait which take a [`cxx::UniquePtr`] will
+/// panic if the pointer is NULL.
+///
+/// # Safety
+///
+/// Implementers must guarantee that the pointer returned by `get_ptr`
+/// is of the correct size and alignment of `T`.
+pub unsafe trait ValueParam<T> {
+    /// Any stack storage required. If, as part of passing to C++,
+    /// we need to store a temporary copy of the value, this will be `T`,
+    /// otherwise `()`.
+    #[doc(hidden)]
+    type StackStorage;
+    /// Populate the stack storage given as a parameter. Only called if you
+    /// return `true` from `needs_stack_space`.
+    ///
+    /// # Safety
+    ///
+    /// Callers must guarantee that this object will not move in memory
+    /// between this call and any subsequent `get_ptr` call or drop.
+    #[doc(hidden)]
+    unsafe fn populate_stack_space(self, this: Pin<&mut Option<Self::StackStorage>>);
+    /// Retrieve the pointer to the underlying item, to be passed to C++.
+    /// Note that on the C++ side this is currently passed to `std::move`
+    /// and therefore may be mutated.
+    #[doc(hidden)]
+    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T;
+    #[doc(hidden)]
+    /// Any special drop steps required for the stack storage. This is not
+    /// necessary if the `StackStorage` type is something self-dropping
+    /// such as `UniquePtr`; it's only necessary if it's something where
+    /// manual management is required such as `MaybeUninit`.
+    fn do_drop(_stack: Pin<&mut Self::StackStorage>) {}
+}
+
+unsafe impl<T> ValueParam<T> for &T
+where
+    T: CopyNew,
+{
+    type StackStorage = MaybeUninit<T>;
+
+    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
+        // Safety: we won't move/swap things within the pin.
+        let slot = Pin::into_inner_unchecked(stack.as_mut());
+        *slot = Some(MaybeUninit::uninit());
+        crate::moveit::new::copy(self).new(Pin::new_unchecked(slot.as_mut().unwrap()))
+    }
+    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
+        // Safety: it's OK to (briefly) create a reference to the T because we
+        // populated it within `populate_stack_space`. It's OK to unpack the pin
+        // because we're not going to move the contents.
+        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
+    }
+
+    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
+        // Switch to MaybeUninit::assume_init_drop when stabilized
+        // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
+        unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
+    }
+}
+
+unsafe impl<T> ValueParam<T> for UniquePtr<T>
+where
+    T: UniquePtrTarget,
+{
+    type StackStorage = UniquePtr<T>;
+
+    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
+        // Safety: we will not move the contents of the pin.
+        *Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
+    }
+
+    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
+        // Safety: we won't move/swap the contents of the outer pin, nor of the
+        // type stored within the UniquePtr.
+        unsafe {
+            (Pin::into_inner_unchecked(
+                (*Pin::into_inner_unchecked(stack))
+                    .as_mut()
+                    .expect("Passed a NULL UniquePtr as a C++ value parameter"),
+            )) as *mut T
+        }
+    }
+}
+
+unsafe impl<'a, T: 'a> ValueParam<T> for &'a UniquePtr<T>
+where
+    T: UniquePtrTarget + CopyNew,
+{
+    type StackStorage = <&'a T as ValueParam<T>>::StackStorage;
+
+    unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
+        self.as_ref()
+            .expect("Passed a NULL &UniquePtr as a C++ value parameter")
+            .populate_stack_space(stack)
+    }
+
+    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
+        <&'a T as ValueParam<T>>::get_ptr(stack)
+    }
+
+    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
+        <&'a T as ValueParam<T>>::do_drop(stack)
+    }
+}
+
+/// Explicitly force a value parameter to be taken using any type of [`crate::moveit::new::New`],
+/// i.e. a constructor.
+pub fn as_new<N: New<Output = T>, T>(constructor: N) -> impl ValueParam<T> {
+    ByNew(constructor)
+}
+
+/// Explicitly force a value parameter to be taken by copy.
+pub fn as_copy<P: Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T>
+where
+    T: CopyNew,
+{
+    ByNew(crate::moveit::new::copy(ptr))
+}
+
+/// Explicitly force a value parameter to be taken usign C++ move semantics.
+pub fn as_mov<P: DerefMove + Deref<Target = T>, T>(ptr: impl Into<Pin<P>>) -> impl ValueParam<T>
+where
+    P: DerefMove,
+    P::Target: MoveNew,
+{
+    ByNew(crate::moveit::new::mov(ptr))
+}
+
+#[doc(hidden)]
+pub struct ByNew<N: New>(N);
+
+unsafe impl<N, T> ValueParam<T> for ByNew<N>
+where
+    N: New<Output = T>,
+{
+    type StackStorage = MaybeUninit<T>;
+
+    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
+        // Safety: we won't move/swap things within the pin.
+        let slot = Pin::into_inner_unchecked(stack.as_mut());
+        *slot = Some(MaybeUninit::uninit());
+        self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
+    }
+    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
+        // Safety: it's OK to (briefly) create a reference to the T because we
+        // populated it within `populate_stack_space`. It's OK to unpack the pin
+        // because we're not going to move the contents.
+        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
+    }
+
+    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
+        // Switch to MaybeUninit::assume_init_drop when stabilized
+        // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
+        unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
+    }
+}
+
+/// Implementation detail for how we pass value parameters into C++.
+/// This type is instantiated by auto-generated autocxx code each time we
+/// need to pass a value parameter into C++, and will take responsibility
+/// for extracting that value parameter from the [`ValueParam`] and doing
+/// any later cleanup.
+#[doc(hidden)]
+pub struct ValueParamHandler<T, VP: ValueParam<T>> {
+    // We can't populate this on 'new' because the object may move.
+    // Hence this is an Option - it's None until populate is called.
+    space: Option<VP::StackStorage>,
+    _pinned: PhantomPinned,
+}
+
+impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
+    /// Populate this stack space if needs be. Note safety guarantees
+    /// on [`get_ptr`].
+    ///
+    /// # Safety
+    ///
+    /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
+    pub unsafe fn populate(self: Pin<&mut Self>, param: VP) {
+        // Structural pinning, as documented in [`std::pin`].
+        param.populate_stack_space(self.map_unchecked_mut(|s| &mut s.space))
+    }
+
+    /// Return a pointer to the underlying value which can be passed to C++.
+    ///
+    /// Per the unsafety contract of [`populate`], [`populate`] has been called exactly once
+    /// prior to this call.
+    pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
+        // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
+        // nicely with `unwrap`, so we have to do it manually.
+        unsafe {
+            VP::get_ptr(Pin::new_unchecked(
+                self.get_unchecked_mut().space.as_mut().unwrap(),
+            ))
+        }
+    }
+}
+
+impl<T, VP: ValueParam<T>> Default for ValueParamHandler<T, VP> {
+    fn default() -> Self {
+        Self {
+            space: None,
+            _pinned: PhantomPinned,
+        }
+    }
+}
+
+impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
+    fn drop(&mut self) {
+        if let Some(space) = self.space.as_mut() {
+            unsafe { VP::do_drop(Pin::new_unchecked(space)) }
+        }
+    }
+}
diff --git a/third_party/rust/autocxx/v0_16/crate/tools/publish-all.sh b/third_party/rust/autocxx/v0_17/crate/tools/publish-all.sh
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/tools/publish-all.sh
rename to third_party/rust/autocxx/v0_17/crate/tools/publish-all.sh
diff --git a/third_party/rust/autocxx/v0_16/crate/tools/upgrade-cxx.sh b/third_party/rust/autocxx/v0_17/crate/tools/upgrade-cxx.sh
similarity index 100%
rename from third_party/rust/autocxx/v0_16/crate/tools/upgrade-cxx.sh
rename to third_party/rust/autocxx/v0_17/crate/tools/upgrade-cxx.sh
diff --git a/third_party/rust/autocxx/v0_17/crate/tools/upgrade-version.sh b/third_party/rust/autocxx/v0_17/crate/tools/upgrade-version.sh
new file mode 100755
index 0000000..58ed04a3
--- /dev/null
+++ b/third_party/rust/autocxx/v0_17/crate/tools/upgrade-version.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+OLD=$1
+NEW=$2
+find . -type f -name "Cargo.toml" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g"
+find . -type f -name "*.md" -print0 | xargs -0 sed -i '' -e "s/$OLD/$NEW/g"
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_engine/v0_16/crate/.cargo_vcs_info.json
deleted file mode 100644
index e27bcad..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74"
-  },
-  "path_in_vcs": "engine"
-}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/abstract_types.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/abstract_types.rs
deleted file mode 100644
index ce90f86..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/abstract_types.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use autocxx_parser::IncludeCppConfig;
-
-use super::{
-    fun::{FnAnalysis, FnKind, FnPhase, MethodKind, TraitMethodKind},
-    pod::PodAnalysis,
-};
-use crate::conversion::{
-    api::TypeKind,
-    error_reporter::{convert_apis, convert_item_apis},
-    ConvertError,
-};
-use crate::{conversion::api::Api, types::QualifiedName};
-use std::collections::HashSet;
-
-/// Spot types with pure virtual functions and mark them abstract.
-pub(crate) fn mark_types_abstract(
-    config: &IncludeCppConfig,
-    mut apis: Vec<Api<FnPhase>>,
-) -> Vec<Api<FnPhase>> {
-    let mut abstract_types: HashSet<_> = apis
-        .iter()
-        .filter_map(|api| match &api {
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind: FnKind::Method(self_ty_name, MethodKind::PureVirtual(_)),
-                        ..
-                    },
-                ..
-            } => Some(self_ty_name.clone()),
-            _ => None,
-        })
-        .collect();
-
-    for mut api in apis.iter_mut() {
-        match &mut api {
-            Api::Struct { analysis, name, .. } if abstract_types.contains(&name.name) => {
-                analysis.kind = TypeKind::Abstract;
-            }
-            _ => {}
-        }
-    }
-
-    // Spot any derived classes (recursively). Also, any types which have a base
-    // class that's not on the allowlist are presumed to be abstract, because we
-    // have no way of knowing (as they're not on the allowlist, there will be
-    // no methods associated so we won't be able to spot pure virtual methods).
-    let mut iterate = true;
-    while iterate {
-        iterate = false;
-        for mut api in apis.iter_mut() {
-            match &mut api {
-                Api::Struct {
-                    analysis: PodAnalysis { bases, kind, .. },
-                    ..
-                } if *kind != TypeKind::Abstract
-                    && (!abstract_types.is_disjoint(bases)
-                        || any_missing_from_allowlist(config, bases)) =>
-                {
-                    *kind = TypeKind::Abstract;
-                    abstract_types.insert(api.name().clone());
-                    // Recurse in case there are further dependent types
-                    iterate = true;
-                }
-                _ => {}
-            }
-        }
-    }
-
-    // We also need to remove any constructors belonging to these
-    // abstract types.
-    apis.retain(|api| {
-        !matches!(&api,
-        Api::Function {
-            analysis:
-                FnAnalysis {
-                    kind: FnKind::Method(self_ty, MethodKind::MakeUnique | MethodKind::Constructor)
-                        | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
-                    ..
-                },
-                ..
-        } if abstract_types.contains(self_ty))
-    });
-
-    // Finally, if there are any types which are nested inside other types,
-    // they can't be abstract. This is due to two small limitations in cxx.
-    // Imagine we have class Foo { class Bar }
-    // 1) using "type Foo = super::bindgen::root::Foo_Bar" results
-    //    in the creation of std::unique_ptr code which isn't acceptable
-    //    for an abtract class
-    // 2) using "type Foo;" isn't possible unless Foo is a top-level item
-    //    within its namespace. Any outer names will be interpreted as namespace
-    //    names and result in cxx generating "namespace Foo { class Bar }"".
-    let mut results = Vec::new();
-    convert_item_apis(apis, &mut results, |api| match api {
-        Api::Struct {
-            analysis:
-                PodAnalysis {
-                    kind: TypeKind::Abstract,
-                    ..
-                },
-            ..
-        } if api
-            .cpp_name()
-            .as_ref()
-            .map(|n| n.contains("::"))
-            .unwrap_or_default() =>
-        {
-            Err(ConvertError::AbstractNestedType)
-        }
-        _ => Ok(Box::new(std::iter::once(api))),
-    });
-    results
-}
-
-fn any_missing_from_allowlist(config: &IncludeCppConfig, bases: &HashSet<QualifiedName>) -> bool {
-    bases
-        .iter()
-        .any(|qn| !config.is_on_allowlist(&qn.to_cpp_name()))
-}
-
-pub(crate) fn discard_ignored_functions(apis: Vec<Api<FnPhase>>) -> Vec<Api<FnPhase>> {
-    // Some APIs can't be generated, e.g. because they're protected.
-    // Now we've finished analyzing abstract types and constructors, we'll
-    // convert them to IgnoredI
-    let mut apis_new = Vec::new();
-    convert_apis(
-        apis,
-        &mut apis_new,
-        |name, fun, analysis, name_for_gc| {
-            analysis.ignore_reason.clone()?;
-            Ok(Box::new(std::iter::once(Api::Function {
-                name,
-                fun,
-                analysis,
-                name_for_gc,
-            })))
-        },
-        Api::struct_unchanged,
-        Api::enum_unchanged,
-        Api::typedef_unchanged,
-    );
-    apis_new
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/ctypes.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/ctypes.rs
deleted file mode 100644
index fef56d1..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/ctypes.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::HashMap;
-
-use syn::Ident;
-
-use crate::conversion::api::ApiName;
-use crate::types::Namespace;
-use crate::{conversion::api::Api, known_types::known_types, types::QualifiedName};
-
-use super::fun::FnPhase;
-
-/// Spot any variable-length C types (e.g. unsigned long)
-/// used in the [Api]s and append those as extra APIs.
-pub(crate) fn append_ctype_information(apis: &mut Vec<Api<FnPhase>>) {
-    let ctypes: HashMap<Ident, QualifiedName> = apis
-        .iter()
-        .flat_map(|api| api.deps())
-        .filter(|ty| known_types().is_ctype(ty))
-        .map(|ty| (ty.get_final_ident(), ty))
-        .collect();
-    for (id, typename) in ctypes {
-        apis.push(Api::CType {
-            name: ApiName::new(&Namespace::new(), id),
-            typename,
-        });
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructor_rules.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructor_rules.rs
deleted file mode 100644
index d3df4c85..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructor_rules.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Module which understands C++ constructor synthesis rules.
-
-#[cfg_attr(test, derive(Eq, PartialEq))]
-pub(super) struct ImplicitConstructorsNeeded {
-    pub(super) default_constructor: bool,
-    pub(super) copy_constructor_taking_t: bool,
-    pub(super) copy_constructor_taking_const_t: bool,
-    pub(super) move_constructor: bool,
-}
-
-#[derive(Default)]
-pub(super) struct ExplicitItemsFound {
-    pub(super) move_constructor: bool,
-    pub(super) copy_constructor: bool,
-    pub(super) any_other_constructor: bool,
-    pub(super) any_bases_or_fields_lack_const_copy_constructors: bool,
-    pub(super) any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors: bool,
-    pub(super) destructor: bool,
-    pub(super) any_bases_have_deleted_or_inaccessible_destructors: bool,
-    pub(super) copy_assignment_operator: bool,
-    pub(super) move_assignment_operator: bool,
-    pub(super) has_rvalue_reference_fields: bool,
-}
-
-pub(super) fn determine_implicit_constructors(
-    explicits: ExplicitItemsFound,
-) -> ImplicitConstructorsNeeded {
-    let any_constructor =
-        explicits.copy_constructor || explicits.move_constructor || explicits.any_other_constructor;
-    // If no user-declared constructors of any kind are provided for a class type (struct, class, or union),
-    // the compiler will always declare a default constructor as an inline public member of its class.
-    let default_constructor = !any_constructor;
-
-    // If no user-defined copy constructors are provided for a class type (struct, class, or union),
-    // the compiler will always declare a copy constructor as a non-explicit inline public member of its class.
-    // This implicitly-declared copy constructor has the form T::T(const T&) if all of the following are true:
-    //  each direct and virtual base B of T has a copy constructor whose parameters are const B& or const volatile B&;
-    //  each non-static data member M of T of class type or array of class type has a copy constructor whose parameters are const M& or const volatile M&.
-
-    // The implicitly-declared or defaulted copy constructor for class T is defined as deleted if any of the following conditions are true:
-    // T is a union-like class and has a variant member with non-trivial copy constructor; // we don't support unions anyway
-    // T has a user-defined move constructor or move assignment operator (this condition only causes the implicitly-declared, not the defaulted, copy constructor to be deleted).
-    // T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);
-    // T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors);
-    // T has direct or virtual base class with a deleted or inaccessible destructor;
-    // T has a data member of rvalue reference type;
-    let copy_constructor_is_deleted = explicits.move_constructor
-        || explicits.move_assignment_operator
-        || explicits.any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors
-        || explicits.any_bases_have_deleted_or_inaccessible_destructors
-        || explicits.has_rvalue_reference_fields;
-
-    let (copy_constructor_taking_const_t, copy_constructor_taking_t) =
-        if explicits.copy_constructor || copy_constructor_is_deleted {
-            (false, false)
-        } else if explicits.any_bases_or_fields_lack_const_copy_constructors {
-            (false, true)
-        } else {
-            (true, false)
-        };
-
-    // If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
-    // there are no user-declared copy constructors;
-    // there are no user-declared copy assignment operators;
-    // there are no user-declared move assignment operators;
-    // there is no user-declared destructor.
-    // then the compiler will declare a move constructor
-    let move_constructor = !(explicits.move_constructor
-        || explicits.copy_constructor
-        || explicits.destructor
-        || explicits.copy_assignment_operator
-        || explicits.move_assignment_operator);
-
-    ImplicitConstructorsNeeded {
-        default_constructor,
-        copy_constructor_taking_t,
-        copy_constructor_taking_const_t,
-        move_constructor,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::determine_implicit_constructors;
-
-    use super::ExplicitItemsFound;
-
-    #[test]
-    fn test_simple() {
-        let inputs = ExplicitItemsFound::default();
-        let outputs = determine_implicit_constructors(inputs);
-        assert_eq!(true, outputs.default_constructor);
-        assert_eq!(true, outputs.copy_constructor_taking_const_t);
-        assert_eq!(false, outputs.copy_constructor_taking_t);
-        assert_eq!(true, outputs.move_constructor);
-    }
-
-    #[test]
-    fn test_with_destructor() {
-        let inputs = ExplicitItemsFound {
-            destructor: true,
-            ..Default::default()
-        };
-        let outputs = determine_implicit_constructors(inputs);
-        assert_eq!(true, outputs.default_constructor);
-        assert_eq!(true, outputs.copy_constructor_taking_const_t);
-        assert_eq!(false, outputs.copy_constructor_taking_t);
-        assert_eq!(false, outputs.move_constructor);
-    }
-
-    #[test]
-    fn test_with_pesky_base() {
-        let inputs = ExplicitItemsFound {
-            any_bases_or_fields_lack_const_copy_constructors: true,
-            ..Default::default()
-        };
-        let outputs = determine_implicit_constructors(inputs);
-        assert_eq!(true, outputs.default_constructor);
-        assert_eq!(false, outputs.copy_constructor_taking_const_t);
-        assert_eq!(true, outputs.copy_constructor_taking_t);
-        assert_eq!(true, outputs.move_constructor);
-    }
-
-    #[test]
-    fn test_with_user_defined_move_constructor() {
-        let inputs = ExplicitItemsFound {
-            move_constructor: true,
-            ..Default::default()
-        };
-        let outputs = determine_implicit_constructors(inputs);
-        assert_eq!(false, outputs.default_constructor);
-        assert_eq!(false, outputs.copy_constructor_taking_const_t);
-        assert_eq!(false, outputs.copy_constructor_taking_t);
-        assert_eq!(false, outputs.move_constructor);
-    }
-
-    #[test]
-    fn test_with_user_defined_misc_constructor() {
-        let inputs = ExplicitItemsFound {
-            any_other_constructor: true,
-            ..Default::default()
-        };
-        let outputs = determine_implicit_constructors(inputs);
-        assert_eq!(false, outputs.default_constructor);
-        assert_eq!(true, outputs.copy_constructor_taking_const_t);
-        assert_eq!(false, outputs.copy_constructor_taking_t);
-        assert_eq!(true, outputs.move_constructor);
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructors.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructors.rs
deleted file mode 100644
index 27931a2..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/implicit_constructors.rs
+++ /dev/null
@@ -1,269 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::{HashMap, HashSet};
-
-use crate::{
-    conversion::{
-        analysis::{depth_first::depth_first, pod::PodAnalysis},
-        api::{Api, CppVisibility, FuncToConvert, SpecialMemberKind},
-    },
-    types::QualifiedName,
-};
-
-use super::{
-    implicit_constructor_rules::{
-        determine_implicit_constructors, ExplicitItemsFound, ImplicitConstructorsNeeded,
-    },
-    FnAnalysis, FnKind, FnPhase, MethodKind, ReceiverMutability, TraitMethodKind,
-};
-
-#[derive(Hash, Eq, PartialEq)]
-enum ExplicitKind {
-    MoveConstructor,
-    ConstCopyConstructor,
-    NonConstCopyConstructor,
-    OtherConstructor,
-    Destructor,
-    CopyAssignmentOperator,
-    MoveAssignmentOperator,
-    DeletedOrInaccessibleCopyConstructor,
-    DeletedOrInaccessibleDestructor,
-}
-
-#[derive(Hash, Eq, PartialEq)]
-struct ExplicitFound {
-    ty: QualifiedName,
-    kind: ExplicitKind,
-}
-
-/// If a type has explicit constructors, bindgen will generate corresponding
-/// constructor functions, which we'll have already converted to make_unique methods.
-/// For types with implicit constructors, we synthesize them here.
-/// It is tempting to make this a separate analysis phase, to be run later than
-/// the function analysis; but that would make the code much more complex as it
-/// would need to output a `FnAnalysisBody`. By running it as part of this phase
-/// we can simply generate the sort of thing bindgen generates, then ask
-/// the existing code in this phase to figure out what to do with it.
-pub(super) fn find_missing_constructors(
-    apis: &[Api<FnPhase>],
-) -> HashMap<QualifiedName, ImplicitConstructorsNeeded> {
-    let explicits = find_explicit_items(apis);
-    let mut implicit_constructors_needed = HashMap::new();
-    for api in depth_first(apis) {
-        if let Api::Struct {
-            name,
-            analysis: PodAnalysis {
-                bases, field_types, ..
-            },
-            details,
-            ..
-        } = api
-        {
-            let name = &name.name;
-            let find = |kind: ExplicitKind| -> bool {
-                explicits.contains(&ExplicitFound {
-                    ty: name.clone(),
-                    kind,
-                })
-            };
-            let any_bases_or_fields_lack_const_copy_constructors =
-                bases.iter().chain(field_types.iter()).any(|qn| {
-                    let has_explicit = explicits.contains(&ExplicitFound {
-                        ty: qn.clone(),
-                        kind: ExplicitKind::ConstCopyConstructor,
-                    });
-                    let has_implicit = implicit_constructors_needed
-                        .get(qn)
-                        .map(|imp: &ImplicitConstructorsNeeded| imp.copy_constructor_taking_const_t)
-                        .unwrap_or_default();
-                    !has_explicit && !has_implicit
-                });
-            let any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors =
-                bases.iter().chain(field_types.iter()).any(|qn| {
-                    explicits.contains(&ExplicitFound {
-                        ty: qn.clone(),
-                        kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
-                    })
-                });
-            let any_bases_have_deleted_or_inaccessible_destructors = bases.iter().any(|qn| {
-                explicits.contains(&ExplicitFound {
-                    ty: qn.clone(),
-                    kind: ExplicitKind::DeletedOrInaccessibleDestructor,
-                })
-            });
-            let explicit_items_found = ExplicitItemsFound {
-                move_constructor: find(ExplicitKind::MoveConstructor),
-                copy_constructor: find(ExplicitKind::ConstCopyConstructor)
-                    || find(ExplicitKind::NonConstCopyConstructor)
-                    || find(ExplicitKind::DeletedOrInaccessibleCopyConstructor),
-                any_other_constructor: find(ExplicitKind::OtherConstructor),
-                any_bases_or_fields_lack_const_copy_constructors,
-                any_bases_or_fields_have_deleted_or_inaccessible_copy_constructors,
-                any_bases_have_deleted_or_inaccessible_destructors,
-                destructor: find(ExplicitKind::Destructor)
-                    || find(ExplicitKind::DeletedOrInaccessibleDestructor),
-                copy_assignment_operator: find(ExplicitKind::CopyAssignmentOperator),
-                move_assignment_operator: find(ExplicitKind::MoveAssignmentOperator),
-                has_rvalue_reference_fields: details.has_rvalue_reference_fields,
-            };
-            let implicits = determine_implicit_constructors(explicit_items_found);
-            implicit_constructors_needed.insert(name.clone(), implicits);
-        }
-    }
-    implicit_constructors_needed
-}
-
-fn find_explicit_items(apis: &[Api<FnPhase>]) -> HashSet<ExplicitFound> {
-    apis.iter()
-        .filter_map(|api| match api {
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind: FnKind::Method(self_ty, MethodKind::Constructor),
-                        ..
-                    },
-                ..
-            } => Some(ExplicitFound {
-                ty: self_ty.clone(),
-                kind: ExplicitKind::OtherConstructor,
-            }),
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind:
-                            FnKind::TraitMethod {
-                                kind: TraitMethodKind::MoveConstructor,
-                                impl_for,
-                                ..
-                            },
-                        ..
-                    },
-                ..
-            } => Some(ExplicitFound {
-                ty: impl_for.clone(),
-                kind: ExplicitKind::MoveConstructor,
-            }),
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind:
-                            FnKind::TraitMethod {
-                                kind: TraitMethodKind::Destructor,
-                                impl_for,
-                                ..
-                            },
-                        ..
-                    },
-                fun,
-                ..
-            } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound {
-                ty: impl_for.clone(),
-                kind: ExplicitKind::DeletedOrInaccessibleDestructor,
-            }),
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind:
-                            FnKind::TraitMethod {
-                                kind: TraitMethodKind::Destructor,
-                                impl_for,
-                                ..
-                            },
-                        ..
-                    },
-                ..
-            } => Some(ExplicitFound {
-                ty: impl_for.clone(),
-                kind: ExplicitKind::Destructor,
-            }),
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind:
-                            FnKind::TraitMethod {
-                                kind: TraitMethodKind::CopyConstructor,
-                                impl_for,
-                                ..
-                            },
-                        ..
-                    },
-                fun,
-                ..
-            } if is_deleted_or_inaccessible(fun) => Some(ExplicitFound {
-                ty: impl_for.clone(),
-                kind: ExplicitKind::DeletedOrInaccessibleCopyConstructor,
-            }),
-            Api::Function {
-                analysis:
-                    FnAnalysis {
-                        kind:
-                            FnKind::TraitMethod {
-                                kind: TraitMethodKind::CopyConstructor,
-                                impl_for,
-                                ..
-                            },
-                        param_details,
-                        ..
-                    },
-                ..
-            } => {
-                let receiver_mutability = &param_details
-                    .iter()
-                    .next()
-                    .unwrap()
-                    .self_type
-                    .as_ref()
-                    .unwrap()
-                    .1;
-                let kind = match receiver_mutability {
-                    ReceiverMutability::Const => ExplicitKind::ConstCopyConstructor,
-                    ReceiverMutability::Mutable => ExplicitKind::NonConstCopyConstructor,
-                };
-                Some(ExplicitFound {
-                    ty: impl_for.clone(),
-                    kind,
-                })
-            }
-            Api::Function {
-                fun,
-                analysis:
-                    FnAnalysis {
-                        kind: FnKind::Method(self_ty, ..),
-                        ..
-                    },
-                ..
-            } if matches!(
-                fun.special_member,
-                Some(SpecialMemberKind::AssignmentOperator)
-            ) =>
-            {
-                let is_move_assignment_operator = !fun.references.rvalue_ref_params.is_empty();
-                Some(ExplicitFound {
-                    ty: self_ty.clone(),
-                    kind: if is_move_assignment_operator {
-                        ExplicitKind::MoveAssignmentOperator
-                    } else {
-                        ExplicitKind::CopyAssignmentOperator
-                    },
-                })
-            }
-            _ => None,
-        })
-        .collect()
-}
-
-fn is_deleted_or_inaccessible(fun: &FuncToConvert) -> bool {
-    fun.cpp_vis == CppVisibility::Private || fun.is_deleted
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/rust_name_tracker.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/rust_name_tracker.rs
deleted file mode 100644
index bd98a5ef..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/rust_name_tracker.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::HashSet;
-
-/// Type which tracks the uniqueness of the _output_ names from
-/// cxx (as dictated by the #[rust_name] attribute).
-/// See also `bridge_name_tracker` which tracks
-/// the names of the items in the cxx::bridge: there's a big comment
-/// there explaining the relationship of all the names.
-#[derive(Default)]
-pub(crate) struct RustNameTracker {
-    rust_names_used: HashSet<String>,
-}
-
-impl RustNameTracker {
-    pub(crate) fn new() -> Self {
-        Self::default()
-    }
-
-    /// Is it OK to create a global Rust function with this name
-    /// in the output of the cxx module?
-    pub(crate) fn ok_to_use_rust_name(&mut self, rust_name: &str) -> bool {
-        self.rust_names_used.insert(rust_name.to_string())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::RustNameTracker;
-
-    #[test]
-    fn test() {
-        let mut rnt = RustNameTracker::new();
-        assert!(rnt.ok_to_use_rust_name("a"));
-        assert!(!rnt.ok_to_use_rust_name("a"));
-        assert!(rnt.ok_to_use_rust_name("b"));
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/mod.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/mod.rs
deleted file mode 100644
index 8453adb4..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/mod.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-pub(crate) mod abstract_types;
-pub(crate) mod casts;
-pub(crate) mod ctypes;
-mod depth_first;
-pub(crate) mod fun;
-pub(crate) mod gc;
-mod name_check;
-pub(crate) mod pod; // hey, that rhymes
-pub(crate) mod remove_ignored;
-pub(crate) mod tdef;
-mod type_converter;
-
-pub(crate) use name_check::check_names;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs
deleted file mode 100644
index fb79c9e..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use crate::conversion::{
-    analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy},
-    ConvertError,
-};
-use crate::known_types::type_lacks_copy_constructor;
-
-use super::type_to_cpp::{type_to_cpp, CppNameMap};
-
-impl TypeConversionPolicy {
-    pub(super) fn unconverted_type(
-        &self,
-        cpp_name_map: &CppNameMap,
-    ) -> Result<String, ConvertError> {
-        match self.cpp_conversion {
-            CppConversionType::FromUniquePtrToValue => self.wrapped_type(cpp_name_map),
-            _ => self.unwrapped_type_as_string(cpp_name_map),
-        }
-    }
-
-    pub(super) fn converted_type(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
-        match self.cpp_conversion {
-            CppConversionType::FromValueToUniquePtr => self.wrapped_type(cpp_name_map),
-            _ => self.unwrapped_type_as_string(cpp_name_map),
-        }
-    }
-
-    fn unwrapped_type_as_string(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
-        type_to_cpp(&self.unwrapped_type, cpp_name_map)
-    }
-
-    fn wrapped_type(&self, original_name_map: &CppNameMap) -> Result<String, ConvertError> {
-        Ok(format!(
-            "std::unique_ptr<{}>",
-            self.unwrapped_type_as_string(original_name_map)?
-        ))
-    }
-
-    pub(super) fn cpp_conversion(
-        &self,
-        var_name: &str,
-        cpp_name_map: &CppNameMap,
-        use_rvo: bool,
-    ) -> Result<String, ConvertError> {
-        Ok(match self.cpp_conversion {
-            CppConversionType::None => {
-                if type_lacks_copy_constructor(&self.unwrapped_type) && !use_rvo {
-                    format!("std::move({})", var_name)
-                } else {
-                    var_name.to_string()
-                }
-            }
-            CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToMove => {
-                format!("std::move(*{})", var_name)
-            }
-            CppConversionType::FromValueToUniquePtr => format!(
-                "std::make_unique<{}>({})",
-                self.unconverted_type(cpp_name_map)?,
-                var_name
-            ),
-        })
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/fun_codegen.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/fun_codegen.rs
deleted file mode 100644
index 0078688..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/fun_codegen.rs
+++ /dev/null
@@ -1,358 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use proc_macro2::TokenStream;
-use quote::quote;
-use syn::{
-    parse::Parser,
-    parse_quote,
-    punctuated::Punctuated,
-    token::{Comma, Unsafe},
-    Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType,
-};
-
-use super::{
-    unqualify::{unqualify_params, unqualify_ret_type},
-    RsCodegenResult, Use,
-};
-use crate::{
-    conversion::{
-        analysis::fun::{
-            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy,
-            TraitMethodDetails, UnsafetyNeeded,
-        },
-        api::ImplBlockDetails,
-        codegen_rs::lifetime::add_lifetime_to_all_params,
-    },
-    types::{Namespace, QualifiedName},
-};
-use crate::{
-    conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary},
-    types::make_ident,
-};
-
-impl UnsafetyNeeded {
-    fn bridge_token(&self) -> Option<Unsafe> {
-        match self {
-            UnsafetyNeeded::None => None,
-            _ => Some(parse_quote! { unsafe }),
-        }
-    }
-
-    fn wrapper_token(&self) -> Option<Unsafe> {
-        match self {
-            UnsafetyNeeded::Always => Some(parse_quote! { unsafe }),
-            _ => None,
-        }
-    }
-}
-
-pub(super) fn gen_function(
-    ns: &Namespace,
-    fun: FuncToConvert,
-    analysis: FnAnalysis,
-    cpp_call_name: String,
-) -> RsCodegenResult {
-    if analysis.ignore_reason.is_err() || !analysis.externally_callable {
-        return RsCodegenResult::default();
-    }
-    let cxxbridge_name = analysis.cxxbridge_name;
-    let rust_name = analysis.rust_name;
-    let ret_type = analysis.ret_type;
-    let param_details = analysis.param_details;
-    let wrapper_function_needed = analysis.cpp_wrapper.is_some();
-    let params = analysis.params;
-    let vis = analysis.vis;
-    let kind = analysis.kind;
-    let doc_attr = fun.doc_attr;
-
-    let mut cpp_name_attr = Vec::new();
-    let mut impl_entry = None;
-    let mut trait_impl_entry = None;
-    let rust_name_attr: Vec<_> = match &analysis.rust_rename_strategy {
-        RustRenameStrategy::RenameUsingRustAttr => Attribute::parse_outer
-            .parse2(quote!(
-                #[rust_name = #rust_name]
-            ))
-            .unwrap(),
-        _ => Vec::new(),
-    };
-    let wrapper_unsafety = analysis.requires_unsafe.wrapper_token();
-    let fn_generator = FnGenerator {
-        param_details: &param_details,
-        cxxbridge_name: &cxxbridge_name,
-        rust_name: &rust_name,
-        unsafety: &wrapper_unsafety,
-        doc_attr: &doc_attr,
-    };
-    let mut materialization = match kind {
-        FnKind::Method(..) | FnKind::TraitMethod { .. } => None,
-        FnKind::Function => match analysis.rust_rename_strategy {
-            RustRenameStrategy::RenameInOutputMod(alias) => {
-                Some(Use::UsedFromCxxBridgeWithAlias(alias))
-            }
-            _ => Some(Use::UsedFromCxxBridge),
-        },
-    };
-    let any_param_needs_rust_conversion = param_details
-        .iter()
-        .any(|pd| pd.conversion.rust_work_needed());
-    let rust_wrapper_needed = match kind {
-        FnKind::TraitMethod { .. } => true,
-        FnKind::Method(..) => any_param_needs_rust_conversion || cxxbridge_name != rust_name,
-        _ => any_param_needs_rust_conversion,
-    };
-    if rust_wrapper_needed {
-        match kind {
-            FnKind::Method(ref type_name, MethodKind::Constructor) => {
-                // Constructor.
-                impl_entry = Some(fn_generator.generate_constructor_impl(type_name));
-            }
-            FnKind::Method(ref type_name, ref method_kind) => {
-                // Method, or static method.
-                impl_entry = Some(fn_generator.generate_method_impl(
-                    matches!(
-                        method_kind,
-                        MethodKind::MakeUnique | MethodKind::Constructor
-                    ),
-                    type_name,
-                    &ret_type,
-                ));
-            }
-            FnKind::TraitMethod { ref details, .. } => {
-                trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type));
-            }
-            _ => {
-                // Generate plain old function
-                materialization = Some(Use::Custom(fn_generator.generate_function_impl(&ret_type)));
-            }
-        }
-    }
-    if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
-        cpp_name_attr = Attribute::parse_outer
-            .parse2(quote!(
-                #[cxx_name = #cpp_call_name]
-            ))
-            .unwrap();
-    }
-    // In very rare occasions, we might need to give an explicit lifetime.
-    let (lifetime_tokens, params, ret_type) =
-        add_explicit_lifetime_if_necessary(&param_details, params, &ret_type);
-
-    // Finally - namespace support. All the Types in everything
-    // above this point are fully qualified. We need to unqualify them.
-    // We need to do that _after_ the above wrapper_function_needed
-    // work, because it relies upon spotting fully qualified names like
-    // std::unique_ptr. However, after it's done its job, all such
-    // well-known types should be unqualified already (e.g. just UniquePtr)
-    // and the following code will act to unqualify only those types
-    // which the user has declared.
-    let params = unqualify_params(params);
-    let ret_type = unqualify_ret_type(ret_type.into_owned());
-    // And we need to make an attribute for the namespace that the function
-    // itself is in.
-    let namespace_attr = if ns.is_empty() || wrapper_function_needed {
-        Vec::new()
-    } else {
-        let namespace_string = ns.to_string();
-        Attribute::parse_outer
-            .parse2(quote!(
-                #[namespace = #namespace_string]
-            ))
-            .unwrap()
-    };
-    // At last, actually generate the cxx::bridge entry.
-    let bridge_unsafety = analysis.requires_unsafe.bridge_token();
-    let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
-        #(#namespace_attr)*
-        #(#rust_name_attr)*
-        #(#cpp_name_attr)*
-        #doc_attr
-        #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type;
-    ));
-    RsCodegenResult {
-        extern_c_mod_items: vec![extern_c_mod_item],
-        bridge_items: Vec::new(),
-        global_items: trait_impl_entry.into_iter().collect(),
-        bindgen_mod_items: Vec::new(),
-        impl_entry,
-        materializations: materialization.into_iter().collect(),
-        extern_rust_mod_items: Vec::new(),
-    }
-}
-
-/// Knows how to generate a given function.
-#[derive(Clone)]
-struct FnGenerator<'a> {
-    param_details: &'a [ArgumentAnalysis],
-    cxxbridge_name: &'a Ident,
-    rust_name: &'a str,
-    unsafety: &'a Option<Unsafe>,
-    doc_attr: &'a Option<Attribute>,
-}
-
-impl<'a> FnGenerator<'a> {
-    fn generate_arg_lists(&self, avoid_self: bool) -> (Punctuated<FnArg, Comma>, Vec<TokenStream>) {
-        let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new();
-        let mut arg_list = Vec::new();
-
-        for pd in self.param_details {
-            let type_name = pd.conversion.rust_wrapper_unconverted_type();
-            let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
-                parse_quote!(self)
-            } else {
-                pd.name.clone()
-            };
-            let param_mutability = pd.conversion.rust_conversion.requires_mutability();
-            wrapper_params.push(parse_quote!(
-                #param_mutability #wrapper_arg_name: #type_name
-            ));
-            arg_list.push(pd.conversion.rust_conversion(wrapper_arg_name));
-        }
-        (wrapper_params, arg_list)
-    }
-
-    /// Generate an 'impl Type { methods-go-here }' item
-    fn generate_method_impl(
-        &self,
-        avoid_self: bool,
-        impl_block_type_name: &QualifiedName,
-        ret_type: &ReturnType,
-    ) -> Box<ImplBlockDetails> {
-        let (wrapper_params, arg_list) = self.generate_arg_lists(avoid_self);
-        let (lifetime_tokens, wrapper_params, ret_type) =
-            add_explicit_lifetime_if_necessary(self.param_details, wrapper_params, ret_type);
-        let rust_name = make_ident(self.rust_name);
-        let unsafety = self.unsafety;
-        let doc_attr = self.doc_attr;
-        let cxxbridge_name = self.cxxbridge_name;
-        Box::new(ImplBlockDetails {
-            item: ImplItem::Method(parse_quote! {
-                #doc_attr
-                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
-                    cxxbridge::#cxxbridge_name ( #(#arg_list),* )
-                }
-            }),
-            ty: impl_block_type_name.get_final_ident(),
-        })
-    }
-
-    /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety.
-    fn generate_trait_impl(&self, details: &TraitMethodDetails, ret_type: &ReturnType) -> Item {
-        let (mut wrapper_params, arg_list) = self.generate_arg_lists(details.avoid_self);
-        if let Some(parameter_reordering) = &details.parameter_reordering {
-            wrapper_params = Self::reorder_parameters(wrapper_params, parameter_reordering);
-        }
-        let (lifetime_tokens, wrapper_params, ret_type) =
-            add_explicit_lifetime_if_necessary(self.param_details, wrapper_params, ret_type);
-        let doc_attr = self.doc_attr;
-        let unsafety = self.unsafety;
-        let cxxbridge_name = self.cxxbridge_name;
-        let trait_signature = &details.trait_signature;
-        let trait_unsafety = &details.trait_unsafety;
-        let impl_for_specifics = &details.impl_for_specifics;
-        let method_name = &details.method_name;
-        let call_body = quote! {
-            cxxbridge::#cxxbridge_name ( #(#arg_list),* )
-        };
-        let call_body = if details.trait_call_is_unsafe {
-            quote! {
-                unsafe {
-                    #call_body
-                }
-            }
-        } else {
-            call_body
-        };
-        parse_quote! {
-            #trait_unsafety impl #lifetime_tokens #trait_signature for #impl_for_specifics {
-                #doc_attr
-                #unsafety fn #method_name ( #wrapper_params ) #ret_type {
-                    #call_body
-                }
-            }
-        }
-    }
-
-    /// Generate a 'impl Type { methods-go-here }' item which is a constructor
-    /// for use with moveit traits.
-    fn generate_constructor_impl(
-        &self,
-        impl_block_type_name: &QualifiedName,
-    ) -> Box<ImplBlockDetails> {
-        let (wrapper_params, arg_list) = self.generate_arg_lists(true);
-        let mut wrapper_params: Punctuated<FnArg, Comma> =
-            wrapper_params.into_iter().skip(1).collect();
-        let ptr_arg_name = &arg_list[0];
-        let rust_name = make_ident(&self.rust_name);
-        let any_references = self.param_details.iter().any(|pd| pd.was_reference);
-        let (lifetime_param, lifetime_addition) = if any_references {
-            add_lifetime_to_all_params(&mut wrapper_params);
-            (quote! { <'a> }, quote! { + 'a })
-        } else {
-            (quote! {}, quote! {})
-        };
-        let cxxbridge_name = self.cxxbridge_name;
-        let body = quote! {
-            autocxx::moveit::new::by_raw(move |#ptr_arg_name| {
-                let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr();
-                cxxbridge::#cxxbridge_name(#(#arg_list),* )
-            })
-        };
-        let body = if self.unsafety.is_some() {
-            // No need for `unsafe` inside the function
-            body
-        } else {
-            quote! {
-                unsafe { #body }
-            }
-        };
-        let doc_attr = self.doc_attr;
-        let unsafety = self.unsafety;
-        Box::new(ImplBlockDetails {
-            item: ImplItem::Method(parse_quote! {
-                #doc_attr
-                pub #unsafety fn #rust_name #lifetime_param ( #wrapper_params ) -> impl autocxx::moveit::new::New<Output=Self> #lifetime_addition {
-                    #body
-                }
-            }),
-            ty: impl_block_type_name.get_final_ident(),
-        })
-    }
-
-    /// Generate a function call wrapper
-    fn generate_function_impl(&self, ret_type: &ReturnType) -> Box<Item> {
-        let (wrapper_params, arg_list) = self.generate_arg_lists(false);
-        let rust_name = make_ident(self.rust_name);
-        let doc_attr = self.doc_attr;
-        let unsafety = self.unsafety;
-        Box::new(Item::Fn(parse_quote! {
-            #doc_attr
-            pub #unsafety fn #rust_name ( #wrapper_params ) #ret_type {
-                cxxbridge::#rust_name ( #(#arg_list),* )
-            }
-        }))
-    }
-
-    fn reorder_parameters(
-        params: Punctuated<FnArg, Comma>,
-        parameter_ordering: &[usize],
-    ) -> Punctuated<FnArg, Comma> {
-        let old_params = params.into_iter().collect::<Vec<_>>();
-        parameter_ordering
-            .iter()
-            .map(|n| old_params.get(*n).unwrap().clone())
-            .collect()
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/function_wrapper_rs.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/function_wrapper_rs.rs
deleted file mode 100644
index c484b58..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/function_wrapper_rs.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use proc_macro2::TokenStream;
-use syn::{Pat, Type, TypePtr};
-
-use crate::conversion::analysis::fun::function_wrapper::{
-    RustConversionType, TypeConversionPolicy,
-};
-use quote::quote;
-use syn::parse_quote;
-
-impl TypeConversionPolicy {
-    pub(super) fn rust_wrapper_unconverted_type(&self) -> Type {
-        match self.rust_conversion {
-            RustConversionType::None => self.converted_rust_type(),
-            RustConversionType::ToBoxedUpHolder(ref sub) => {
-                let id = sub.id();
-                parse_quote! { autocxx::subclass::CppSubclassRustPeerHolder<
-                    super::super::super:: #id>
-                }
-            }
-            RustConversionType::FromStr => parse_quote! { impl ToCppString },
-            RustConversionType::FromPinMaybeUninitToPtr => {
-                let ty = match &self.unwrapped_type {
-                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
-                    _ => panic!("Not a ptr"),
-                };
-                parse_quote! {
-                    ::std::pin::Pin<&mut ::std::mem::MaybeUninit< #ty >>
-                }
-            }
-            RustConversionType::FromPinMoveRefToPtr => {
-                let ty = match &self.unwrapped_type {
-                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
-                    _ => panic!("Not a ptr"),
-                };
-                parse_quote! {
-                    ::std::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
-                }
-            }
-            RustConversionType::FromTypeToPtr => {
-                let ty = match &self.unwrapped_type {
-                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
-                    _ => panic!("Not a ptr"),
-                };
-                parse_quote! { &mut #ty }
-            }
-        }
-    }
-
-    pub(super) fn rust_conversion(&self, var: Pat) -> TokenStream {
-        match self.rust_conversion {
-            RustConversionType::None => quote! { #var },
-            RustConversionType::FromStr => quote! ( #var .into_cpp() ),
-            RustConversionType::ToBoxedUpHolder(ref sub) => {
-                let holder_type = sub.holder();
-                quote! {
-                    Box::new(#holder_type(#var))
-                }
-            }
-            RustConversionType::FromPinMaybeUninitToPtr => quote! {
-                #var.get_unchecked_mut().as_mut_ptr()
-            },
-            RustConversionType::FromPinMoveRefToPtr => quote! {
-                { let r: &mut _ = ::std::pin::Pin::into_inner_unchecked(#var.as_mut());
-                r
-                }
-            },
-            RustConversionType::FromTypeToPtr => quote! {
-                #var
-            },
-        }
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/impl_item_creator.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/impl_item_creator.rs
deleted file mode 100644
index db6e1d0a..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/impl_item_creator.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use autocxx_parser::IncludeCppConfig;
-use syn::{parse_quote, Ident, Item};
-
-pub(crate) fn create_impl_items(id: &Ident, movable: bool, config: &IncludeCppConfig) -> Vec<Item> {
-    if config.exclude_impls {
-        return vec![];
-    }
-    let mut results = vec![
-        Item::Impl(parse_quote! {
-            impl UniquePtr<#id> {}
-        }),
-        Item::Impl(parse_quote! {
-            impl SharedPtr<#id> {}
-        }),
-        Item::Impl(parse_quote! {
-            impl WeakPtr<#id> {}
-        }),
-    ];
-    if movable {
-        results.push(Item::Impl(parse_quote! {
-            impl CxxVector<#id> {}
-        }))
-    }
-    results
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/doc_attr.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/doc_attr.rs
deleted file mode 100644
index 99e4d2d..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/doc_attr.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use syn::Attribute;
-
-/// Returns the attribute (if any) which contains a doc comment.
-pub(super) fn get_doc_attr(attrs: &[Attribute]) -> Option<Attribute> {
-    attrs
-        .iter()
-        .find(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
-        .cloned()
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/mod.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/mod.rs
deleted file mode 100644
index 1c85fe4..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/mod.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-mod bindgen_semantic_attributes;
-mod parse_bindgen;
-mod parse_foreign_mod;
-
-pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes;
-pub(crate) use parse_bindgen::ParseBindgen;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/utilities.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/utilities.rs
deleted file mode 100644
index cfef9e04..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/utilities.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use autocxx_parser::IncludeCppConfig;
-
-use super::api::{ApiName, UnanalyzedApi};
-use crate::types::{make_ident, Namespace};
-
-/// Adds items which we always add, cos they're useful.
-/// Any APIs or techniques which do not involve actual C++ interop
-/// shouldn't go here, but instead should go into the main autocxx
-/// src/lib.rs.
-pub(crate) fn generate_utilities(apis: &mut Vec<UnanalyzedApi>, config: &IncludeCppConfig) {
-    // Unless we've been specifically asked not to do so, we always
-    // generate a 'make_string' function. That pretty much *always* means
-    // we run two passes through bindgen. i.e. the next 'if' is always true,
-    // and we always generate an additional C++ file for our bindings additions,
-    // unless the include_cpp macro has specified ExcludeUtilities.
-    apis.push(UnanalyzedApi::StringConstructor {
-        name: ApiName::new(&Namespace::new(), make_ident(config.get_makestring_name())),
-    });
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_callbacks.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/parse_callbacks.rs
deleted file mode 100644
index b766b25..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_callbacks.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::panic::UnwindSafe;
-
-use crate::RebuildDependencyRecorder;
-use autocxx_bindgen::callbacks::ParseCallbacks;
-
-#[derive(Debug)]
-pub(crate) struct AutocxxParseCallbacks(pub(crate) Box<dyn RebuildDependencyRecorder>);
-
-impl UnwindSafe for AutocxxParseCallbacks {}
-
-impl ParseCallbacks for AutocxxParseCallbacks {
-    fn include_file(&self, filename: &str) {
-        self.0.record_header_file_dependency(filename);
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_file.rs b/third_party/rust/autocxx_engine/v0_16/crate/src/parse_file.rs
deleted file mode 100644
index 9064c02c..0000000
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/parse_file.rs
+++ /dev/null
@@ -1,324 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use crate::ast_discoverer::Discoveries;
-use crate::CppCodegenOptions;
-use crate::{
-    cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine,
-    RebuildDependencyRecorder,
-};
-use autocxx_parser::directives::SUBCLASS;
-use autocxx_parser::{Subclass, SubclassAttrs};
-use proc_macro2::{Span, TokenStream};
-use quote::ToTokens;
-use std::{collections::HashSet, fmt::Display, io::Read, path::PathBuf};
-use std::{panic::UnwindSafe, path::Path, rc::Rc};
-use syn::{Item, LitStr};
-
-/// Errors which may occur when parsing a Rust source file to discover
-/// and interpret include_cxx macros.
-#[derive(Debug)]
-pub enum ParseError {
-    /// Unable to open the source file
-    FileOpen(std::io::Error),
-    /// The .rs file couldn't be read.
-    FileRead(std::io::Error),
-    /// The .rs file couldn't be parsed.
-    Syntax(syn::Error),
-    /// The include CPP macro could not be expanded into
-    /// Rust bindings to C++, because of some problem during the conversion
-    /// process. This could be anything from a C++ parsing error to some
-    /// C++ feature that autocxx can't yet handle and isn't able to skip
-    /// over. It could also cover errors in your syntax of the `include_cpp`
-    /// macro or the directives inside.
-    AutocxxCodegenError(EngineError),
-    /// There are two or more `include_cpp` macros with the same
-    /// mod name.
-    ConflictingModNames,
-    ZeroModsForDynamicDiscovery,
-    MultipleModsForDynamicDiscovery,
-    DiscoveredRustItemsWhenNotInAutoDiscover,
-}
-
-impl Display for ParseError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?,
-            ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?,
-            ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?,
-            ParseError::AutocxxCodegenError(err) =>
-                write!(f, "Unable to parse include_cpp! macro: {}", err)?,
-            ParseError::ConflictingModNames =>
-                write!(f, "There are two or more include_cpp! macros with the same output mod name. Use name!")?,
-            ParseError::ZeroModsForDynamicDiscovery =>
-                write!(f, "This file contains extra information to append to an include_cpp! but no such include_cpp! was found in this file.")?,
-            ParseError::MultipleModsForDynamicDiscovery =>
-                write!(f, "This file contains extra information to append to an include_cpp! but multiple such include_cpp! declarations were found in this file.")?,
-            ParseError::DiscoveredRustItemsWhenNotInAutoDiscover =>
-                write!(f, "This file contains extra information to append to an \"extern Rust\" but auto-discover was switched off.")?,
-        }
-        Ok(())
-    }
-}
-
-/// Parse a Rust file, and spot any include_cpp macros within it.
-pub fn parse_file<P1: AsRef<Path>>(
-    rs_file: P1,
-    auto_allowlist: bool,
-) -> Result<ParsedFile, ParseError> {
-    let mut source = String::new();
-    let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?;
-    file.read_to_string(&mut source)
-        .map_err(ParseError::FileRead)?;
-    proc_macro2::fallback::force();
-    let source = syn::parse_file(&source).map_err(ParseError::Syntax)?;
-    parse_file_contents(source, auto_allowlist)
-}
-
-fn parse_file_contents(source: syn::File, auto_allowlist: bool) -> Result<ParsedFile, ParseError> {
-    let mut results = Vec::new();
-    let mut extra_superclasses = Vec::new();
-    let mut discoveries = Discoveries::default();
-    for item in source.items {
-        results.push(match item {
-            Item::Macro(mac)
-                if mac
-                    .mac
-                    .path
-                    .segments
-                    .last()
-                    .map(|s| s.ident == "include_cpp")
-                    .unwrap_or(false) =>
-            {
-                Segment::Autocxx(
-                    crate::IncludeCppEngine::new_from_syn(mac.mac.clone())
-                        .map_err(ParseError::AutocxxCodegenError)?,
-                )
-            }
-            Item::Mod(itm)
-                if itm
-                    .attrs
-                    .iter()
-                    .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") =>
-            {
-                Segment::Cxx(CxxBridge::from(itm))
-            }
-            Item::Struct(ref its) if auto_allowlist => {
-                let attrs = &its.attrs;
-                let is_superclass_attr = attrs.iter().find(|attr| {
-                    attr.path
-                        .segments
-                        .last()
-                        .map(|seg| seg.ident == "is_subclass" || seg.ident == SUBCLASS)
-                        .unwrap_or(false)
-                });
-                if let Some(is_superclass_attr) = is_superclass_attr {
-                    if !is_superclass_attr.tokens.is_empty() {
-                        let subclass = its.ident.clone();
-                        let args: SubclassAttrs = is_superclass_attr
-                            .parse_args()
-                            .map_err(ParseError::Syntax)?;
-                        if let Some(superclass) = args.superclass {
-                            extra_superclasses.push(Subclass {
-                                superclass,
-                                subclass,
-                            })
-                        }
-                    }
-                }
-                discoveries.search_item(&item);
-                Segment::Other(item)
-            }
-            _ => {
-                discoveries.search_item(&item);
-                Segment::Other(item)
-            }
-        });
-    }
-    if !auto_allowlist
-        && (!discoveries.extern_rust_types.is_empty() || !discoveries.extern_rust_funs.is_empty())
-    {
-        return Err(ParseError::DiscoveredRustItemsWhenNotInAutoDiscover);
-    }
-    if !extra_superclasses.is_empty() || (auto_allowlist && !discoveries.is_empty()) {
-        let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
-            Segment::Autocxx(engine) => Some(engine),
-            _ => None,
-        });
-        let our_seg = autocxx_seg_iterator.next();
-        match our_seg {
-            None => return Err(ParseError::ZeroModsForDynamicDiscovery),
-            Some(engine) => {
-                engine
-                    .config_mut()
-                    .subclasses
-                    .append(&mut extra_superclasses);
-                if auto_allowlist {
-                    for cpp in discoveries.cpp_list {
-                        engine
-                            .config_mut()
-                            .allowlist
-                            .push(LitStr::new(&cpp, Span::call_site()))
-                            .map_err(ParseError::Syntax)?;
-                    }
-                }
-                engine
-                    .config_mut()
-                    .extern_rust_funs
-                    .append(&mut discoveries.extern_rust_funs);
-                engine
-                    .config_mut()
-                    .rust_types
-                    .append(&mut discoveries.extern_rust_types);
-            }
-        }
-        if autocxx_seg_iterator.next().is_some() {
-            return Err(ParseError::MultipleModsForDynamicDiscovery);
-        }
-    }
-    let autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
-        Segment::Autocxx(engine) => Some(engine),
-        _ => None,
-    });
-    for seg in autocxx_seg_iterator {
-        seg.config
-            .confirm_complete(auto_allowlist)
-            .map_err(ParseError::Syntax)?;
-    }
-    Ok(ParsedFile(results))
-}
-
-/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines',
-/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp
-/// macros within this file. Also contains `syn::Item` structures for all
-/// the rest of the Rust code, such that it can be reconstituted if necessary.
-pub struct ParsedFile(Vec<Segment>);
-
-#[allow(clippy::large_enum_variant)]
-enum Segment {
-    Autocxx(IncludeCppEngine),
-    Cxx(CxxBridge),
-    Other(Item),
-}
-
-pub trait CppBuildable {
-    fn generate_h_and_cxx(
-        &self,
-        cpp_codegen_options: &CppCodegenOptions,
-    ) -> Result<GeneratedCpp, cxx_gen::Error>;
-}
-
-impl ParsedFile {
-    /// Get all the autocxxes in this parsed file.
-    pub fn get_rs_buildables(&self) -> impl Iterator<Item = &IncludeCppEngine> {
-        self.0.iter().filter_map(|s| match s {
-            Segment::Autocxx(includecpp) => Some(includecpp),
-            _ => None,
-        })
-    }
-
-    /// Get all items which can result in C++ code
-    pub fn get_cpp_buildables(&self) -> impl Iterator<Item = &dyn CppBuildable> {
-        self.0.iter().filter_map(|s| match s {
-            Segment::Autocxx(includecpp) => Some(includecpp as &dyn CppBuildable),
-            Segment::Cxx(cxxbridge) => Some(cxxbridge as &dyn CppBuildable),
-            _ => None,
-        })
-    }
-
-    fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> {
-        self.0.iter_mut().filter_map(|s| match s {
-            Segment::Autocxx(includecpp) => Some(includecpp),
-            _ => None,
-        })
-    }
-
-    pub fn include_dirs(&self) -> impl Iterator<Item = &PathBuf> {
-        self.0
-            .iter()
-            .filter_map(|s| match s {
-                Segment::Autocxx(includecpp) => Some(includecpp.include_dirs()),
-                _ => None,
-            })
-            .flatten()
-    }
-
-    pub fn resolve_all(
-        &mut self,
-        autocxx_inc: Vec<PathBuf>,
-        extra_clang_args: &[&str],
-        dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
-        cpp_codegen_options: &CppCodegenOptions,
-    ) -> Result<(), ParseError> {
-        let mut mods_found = HashSet::new();
-        let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
-            dep_recorder.map(Rc::from);
-        for include_cpp in self.get_autocxxes_mut() {
-            #[allow(clippy::manual_map)] // because of dyn shenanigans
-            let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder
-            {
-                None => None,
-                Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new(
-                    inner_dep_recorder.clone(),
-                ))),
-            };
-            if !mods_found.insert(include_cpp.get_mod_name()) {
-                return Err(ParseError::ConflictingModNames);
-            }
-            include_cpp
-                .generate(
-                    autocxx_inc.clone(),
-                    extra_clang_args,
-                    dep_recorder,
-                    cpp_codegen_options,
-                )
-                .map_err(ParseError::AutocxxCodegenError)?
-        }
-        Ok(())
-    }
-}
-
-impl ToTokens for ParsedFile {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        for seg in &self.0 {
-            match seg {
-                Segment::Other(item) => item.to_tokens(tokens),
-                Segment::Autocxx(autocxx) => {
-                    let these_tokens = autocxx.generate_rs();
-                    tokens.extend(these_tokens);
-                }
-                Segment::Cxx(itemmod) => itemmod.to_tokens(tokens),
-            }
-        }
-    }
-}
-
-/// Shenanigans required to share the same RebuildDependencyRecorder
-/// with all of the include_cpp instances in this one file.
-#[derive(Debug, Clone)]
-struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>);
-
-impl CompositeDepRecorder {
-    fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self {
-        CompositeDepRecorder(inner)
-    }
-}
-
-impl UnwindSafe for CompositeDepRecorder {}
-
-impl RebuildDependencyRecorder for CompositeDepRecorder {
-    fn record_header_file_dependency(&self, filename: &str) {
-        self.0.record_header_file_dependency(filename);
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/BUILD.gn b/third_party/rust/autocxx_engine/v0_17/BUILD.gn
similarity index 86%
rename from third_party/rust/autocxx_engine/v0_16/BUILD.gn
rename to third_party/rust/autocxx_engine/v0_17/BUILD.gn
index cf4e0231..50fe1441 100644
--- a/third_party/rust/autocxx_engine/v0_16/BUILD.gn
+++ b/third_party/rust/autocxx_engine/v0_17/BUILD.gn
@@ -6,7 +6,7 @@
 
 cargo_crate("lib") {
   crate_name = "autocxx_engine"
-  epoch = "0.16"
+  epoch = "0.17"
   crate_type = "rlib"
 
   # Only for usage from third-party crates. Add the crate to
@@ -21,8 +21,7 @@
   deps = [
     "//third_party/rust/aquamarine/v0_1:lib",
     "//third_party/rust/autocxx_bindgen/v0_59:lib",
-    "//third_party/rust/autocxx_parser/v0_16:lib",
-    "//third_party/rust/cxx/v1:lib",
+    "//third_party/rust/autocxx_parser/v0_17:lib",
     "//third_party/rust/cxx_gen/v0_7:lib",
     "//third_party/rust/indoc/v1:lib",
     "//third_party/rust/itertools/v0_10:lib",
@@ -31,14 +30,14 @@
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
     "//third_party/rust/serde_json/v1:lib",
-    "//third_party/rust/strum_macros/v0_23:lib",
+    "//third_party/rust/strum_macros/v0_24:lib",
     "//third_party/rust/syn/v1:lib",
     "//third_party/rust/tempfile/v3:lib",
-    "//third_party/rust/unzip_n/v0_1:lib",
     "//third_party/rust/version_check/v0_9:lib",
   ]
   features = [
     "reproduction_case",
     "serde_json",
+    "static",
   ]
 }
diff --git a/third_party/rust/autocxx_engine/v0_16/README.chromium b/third_party/rust/autocxx_engine/v0_17/README.chromium
similarity index 78%
rename from third_party/rust/autocxx_engine/v0_16/README.chromium
rename to third_party/rust/autocxx_engine/v0_17/README.chromium
index c784f05b..cad75921 100644
--- a/third_party/rust/autocxx_engine/v0_16/README.chromium
+++ b/third_party/rust/autocxx_engine/v0_17/README.chromium
@@ -1,6 +1,6 @@
 Name: autocxx-engine
 URL: https://crates.io/crates/autocxx-engine
 Description: Safe autogenerated interop between Rust and C++
-Version: 0.16.0
-Security Critical: no
+Version: 0.17.2
+Security Critical: yes
 License: Apache 2.0
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_engine/v0_17/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..a34c1ec6
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd"
+  },
+  "path_in_vcs": "engine"
+}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml
similarity index 92%
rename from third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml
rename to third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml
index d4654bb..1d0bc25 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml
+++ b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "autocxx-engine"
-version = "0.16.0"
+version = "0.17.2"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 description = "Safe autogenerated interop between Rust and C++"
 keywords = ["ffi"]
@@ -37,15 +37,12 @@
 version = "=0.59.13"
 
 [dependencies.autocxx-parser]
-version = "=0.16.0"
+version = "=0.17.2"
 
 [dependencies.cc]
 version = "1.0"
 optional = true
 
-[dependencies.cxx]
-version = "1.0.54"
-
 [dependencies.cxx-gen]
 version = "0.7.54"
 
@@ -53,7 +50,7 @@
 version = "1.0"
 
 [dependencies.itertools]
-version = "0.10"
+version = "0.10.3"
 
 [dependencies.log]
 version = "0.4"
@@ -72,7 +69,7 @@
 optional = true
 
 [dependencies.strum_macros]
-version = "0.23"
+version = "0.24"
 
 [dependencies.syn]
 version = "1.0.39"
@@ -84,9 +81,6 @@
 [dependencies.tempfile]
 version = "3.1"
 
-[dependencies.unzip-n]
-version = "0.1.2"
-
 [dependencies.version_check]
 version = "0.9"
 
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml.orig
similarity index 67%
rename from third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml.orig
rename to third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml.orig
index 726cf58..311214a 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/Cargo.toml.orig
+++ b/third_party/rust/autocxx_engine/v0_17/crate/Cargo.toml.orig
@@ -1,20 +1,14 @@
 # Copyright 2020 Google LLC
 #
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
 
 [package]
 name = "autocxx-engine"
-version = "0.16.0"
+version = "0.17.2"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 license = "MIT OR Apache-2.0"
 description = "Safe autogenerated interop between Rust and C++"
@@ -38,20 +32,18 @@
 indoc = "1.0"
 autocxx-bindgen = "=0.59.13"
 #autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "denote-deleted-move-constructors" }
-itertools = "0.10"
+itertools = "0.10.3"
 cc = { version = "1.0", optional = true }
-unzip-n = "0.1.2"
 # Note: Keep the patch-level version of cxx-gen and cxx in sync.
 # There can be interdependencies between the code generated by cxx-gen and
 # what cxx expects to be there.
 cxx-gen = "0.7.54"
-cxx = "1.0.54"
-autocxx-parser = { version = "=0.16.0", path="../parser" }
+autocxx-parser = { version = "=0.17.2", path="../parser" }
 version_check = "0.9"
 aquamarine = "0.1" # docs
 tempfile = "3.1"
 once_cell = "1.7"
-strum_macros = "0.23"
+strum_macros = "0.24"
 serde_json = { version = "1.0", optional = true }
 
 [dependencies.syn]
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/README.md b/third_party/rust/autocxx_engine/v0_17/crate/README.md
new file mode 100644
index 0000000..9b91d4a
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/README.md
@@ -0,0 +1 @@
+This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/ast_discoverer.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/ast_discoverer.rs
similarity index 92%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/ast_discoverer.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/ast_discoverer.rs
index 2336e43..7374a51 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/ast_discoverer.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/ast_discoverer.rs
@@ -1,16 +1,10 @@
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashSet;
 
@@ -36,10 +30,10 @@
 }
 
 impl Discoveries {
-    pub(super) fn search_item(&mut self, item: &Item) {
+    pub(super) fn search_item(&mut self, item: &Item, mod_path: Option<RustPath>) {
         let mut this_mod = PerModDiscoveries {
             discoveries: self,
-            mod_path: None,
+            mod_path,
         };
         this_mod.search_item(item);
     }
@@ -49,6 +43,12 @@
             && self.extern_rust_funs.is_empty()
             && self.extern_rust_types.is_empty()
     }
+
+    pub(crate) fn extend(&mut self, other: Self) {
+        self.cpp_list.extend(other.cpp_list);
+        self.extern_rust_funs.extend(other.extern_rust_funs);
+        self.extern_rust_types.extend(other.extern_rust_types);
+    }
 }
 
 struct PerModDiscoveries<'a> {
@@ -425,7 +425,7 @@
                 }
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert_cpp_found(&discoveries);
     }
 
@@ -437,7 +437,7 @@
                 ffi::xxx()
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert_cpp_found(&discoveries);
     }
 
@@ -449,7 +449,7 @@
                 ffi::xxx();
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert_cpp_found(&discoveries);
     }
 
@@ -461,7 +461,7 @@
                 ffi::a::b::xxx();
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert!(!discoveries.cpp_list.is_empty());
         assert!(discoveries.cpp_list.iter().next().unwrap() == "a::b::xxx");
     }
@@ -474,7 +474,7 @@
                 a + 3 * foo(ffi::xxx());
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert_cpp_found(&discoveries);
     }
 
@@ -486,7 +486,7 @@
                 let foo: ffi::xxx = bar();
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert_cpp_found(&discoveries);
     }
 
@@ -497,7 +497,7 @@
             fn bar(a: &mut ffi::xxx) {
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert_cpp_found(&discoveries);
     }
 
@@ -508,7 +508,7 @@
             fn bar(a: cxx::UniquePtr<ffi::xxx>) {
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert_cpp_found(&discoveries);
     }
 
@@ -520,17 +520,8 @@
             fn bar(a: cxx::UniquePtr<ffi::xxx>) {
             }
         };
-        discoveries.search_item(&itm);
-        assert!(
-            discoveries
-                .extern_rust_funs
-                .iter()
-                .next()
-                .unwrap()
-                .sig
-                .ident
-                == "bar"
-        );
+        discoveries.search_item(&itm, None);
+        assert!(discoveries.extern_rust_funs.get(0).unwrap().sig.ident == "bar");
     }
 
     #[test]
@@ -542,12 +533,11 @@
 
             }
         };
-        discoveries.search_item(&itm);
+        discoveries.search_item(&itm, None);
         assert!(
             discoveries
                 .extern_rust_types
-                .iter()
-                .next()
+                .get(0)
                 .unwrap()
                 .get_final_ident()
                 == "Bar"
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/builder.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/builder.rs
similarity index 94%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/builder.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/builder.rs
index 58906ea..50af5b2 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/builder.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/builder.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use autocxx_parser::file_locations::FileLocationStrategy;
 use proc_macro2::TokenStream;
@@ -83,14 +77,14 @@
 /// It would be unusual to use this directly - see the `autocxx_build` or
 /// `autocxx_gen` crates.
 #[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
-pub struct Builder<BuilderContext> {
+pub struct Builder<'a, BuilderContext> {
     rs_file: PathBuf,
     autocxx_incs: Vec<OsString>,
     extra_clang_args: Vec<String>,
     dependency_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
     custom_gendir: Option<PathBuf>,
     auto_allowlist: bool,
-    cpp_codegen_options: CppCodegenOptions,
+    cpp_codegen_options: CppCodegenOptions<'a>,
     // This member is to ensure that this type is parameterized
     // by a BuilderContext. The goal is to balance three needs:
     // (1) have most of the functionality over in autocxx_engine,
@@ -101,7 +95,7 @@
     ctx: PhantomData<BuilderContext>,
 }
 
-impl<CTX: BuilderContext> Builder<CTX> {
+impl<CTX: BuilderContext> Builder<'_, CTX> {
     #[doc(hidden)]
     pub fn new(
         rs_file: impl AsRef<Path>,
@@ -179,6 +173,13 @@
         self
     }
 
+    /// Whether to skip using [`cxx_gen`] to generate the C++ code,
+    /// so that some other process can handle that.
+    pub fn skip_cxx_gen(mut self, skip_cxx_gen: bool) -> Self {
+        self.cpp_codegen_options.skip_cxx_gen = skip_cxx_gen;
+        self
+    }
+
     /// Build autocxx C++ files and return a cc::Build you can use to build
     /// more from a build.rs file.
     pub fn build(self) -> Result<BuilderBuild, BuilderError> {
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/abstract_types.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/abstract_types.rs
new file mode 100644
index 0000000..43a0eb1
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/abstract_types.rs
@@ -0,0 +1,172 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::{
+    fun::{
+        FnAnalysis, FnKind, FnPhase, FnPrePhase2, MethodKind, PodAndConstructorAnalysis,
+        TraitMethodKind,
+    },
+    pod::PodAnalysis,
+};
+use crate::conversion::{api::Api, apivec::ApiVec};
+use crate::conversion::{
+    api::TypeKind,
+    error_reporter::{convert_apis, convert_item_apis},
+    ConvertError,
+};
+use std::collections::HashSet;
+
+/// Spot types with pure virtual functions and mark them abstract.
+pub(crate) fn mark_types_abstract(mut apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPrePhase2> {
+    let mut abstract_types: HashSet<_> = apis
+        .iter()
+        .filter_map(|api| match &api {
+            Api::Function {
+                analysis:
+                    FnAnalysis {
+                        kind:
+                            FnKind::Method {
+                                impl_for: self_ty_name,
+                                method_kind: MethodKind::PureVirtual(_),
+                                ..
+                            },
+                        ..
+                    },
+                ..
+            } => Some(self_ty_name.clone()),
+            _ => None,
+        })
+        .collect();
+
+    // Spot any derived classes (recursively). Also, any types which have a base
+    // class that's not on the allowlist are presumed to be abstract, because we
+    // have no way of knowing (as they're not on the allowlist, there will be
+    // no methods associated so we won't be able to spot pure virtual methods).
+    let mut iterate = true;
+    while iterate {
+        iterate = false;
+        apis = apis
+            .into_iter()
+            .map(|api| {
+                match api {
+                    Api::Struct {
+                        analysis:
+                            PodAndConstructorAnalysis {
+                                pod:
+                                    PodAnalysis {
+                                        bases,
+                                        kind: TypeKind::Pod | TypeKind::NonPod,
+                                        castable_bases,
+                                        field_deps,
+                                        field_info,
+                                        is_generic,
+                                    },
+                                constructors,
+                            },
+                        name,
+                        details,
+                    } if abstract_types.contains(&name.name)
+                        || !abstract_types.is_disjoint(&bases) =>
+                    {
+                        abstract_types.insert(name.name.clone());
+                        // Recurse in case there are further dependent types
+                        iterate = true;
+                        Api::Struct {
+                            analysis: PodAndConstructorAnalysis {
+                                pod: PodAnalysis {
+                                    bases,
+                                    kind: TypeKind::Abstract,
+                                    castable_bases,
+                                    field_deps,
+                                    field_info,
+                                    is_generic,
+                                },
+                                constructors,
+                            },
+                            name,
+                            details,
+                        }
+                    }
+                    _ => api,
+                }
+            })
+            .collect()
+    }
+
+    // We also need to remove any constructors belonging to these
+    // abstract types.
+    apis.retain(|api| {
+        !matches!(&api,
+        Api::Function {
+            analysis:
+                FnAnalysis {
+                    kind: FnKind::Method{impl_for: self_ty, method_kind: MethodKind::MakeUnique | MethodKind::Constructor{..}, ..}
+                        | FnKind::TraitMethod{ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor, impl_for: self_ty, ..},
+                    ..
+                },
+                ..
+        } if abstract_types.contains(self_ty))
+    });
+
+    // Finally, if there are any types which are nested inside other types,
+    // they can't be abstract. This is due to two small limitations in cxx.
+    // Imagine we have class Foo { class Bar }
+    // 1) using "type Foo = super::bindgen::root::Foo_Bar" results
+    //    in the creation of std::unique_ptr code which isn't acceptable
+    //    for an abtract class
+    // 2) using "type Foo;" isn't possible unless Foo is a top-level item
+    //    within its namespace. Any outer names will be interpreted as namespace
+    //    names and result in cxx generating "namespace Foo { class Bar }"".
+    let mut results = ApiVec::new();
+    convert_item_apis(apis, &mut results, |api| match api {
+        Api::Struct {
+            analysis:
+                PodAndConstructorAnalysis {
+                    pod:
+                        PodAnalysis {
+                            kind: TypeKind::Abstract,
+                            ..
+                        },
+                    ..
+                },
+            ..
+        } if api
+            .cpp_name()
+            .as_ref()
+            .map(|n| n.contains("::"))
+            .unwrap_or_default() =>
+        {
+            Err(ConvertError::AbstractNestedType)
+        }
+        _ => Ok(Box::new(std::iter::once(api))),
+    });
+    results
+}
+
+pub(crate) fn discard_ignored_functions(apis: ApiVec<FnPhase>) -> ApiVec<FnPhase> {
+    // Some APIs can't be generated, e.g. because they're protected.
+    // Now we've finished analyzing abstract types and constructors, we'll
+    // convert them to IgnoredItems.
+    let mut apis_new = ApiVec::new();
+    convert_apis(
+        apis,
+        &mut apis_new,
+        |name, fun, analysis| {
+            analysis.ignore_reason.clone()?;
+            Ok(Box::new(std::iter::once(Api::Function {
+                name,
+                fun,
+                analysis,
+            })))
+        },
+        Api::struct_unchanged,
+        Api::enum_unchanged,
+        Api::typedef_unchanged,
+    );
+    apis_new
+}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/allocators.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/allocators.rs
new file mode 100644
index 0000000..f796de7
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/allocators.rs
@@ -0,0 +1,110 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Code to create functions to alloc and free while unitialized.
+
+use syn::{parse_quote, punctuated::Punctuated, token::Comma, FnArg, ReturnType};
+
+use crate::{
+    conversion::{
+        api::{Api, ApiName, CppVisibility, FuncToConvert, Provenance, References, TraitSynthesis},
+        apivec::ApiVec,
+    },
+    types::{make_ident, QualifiedName},
+};
+
+use super::{
+    fun::function_wrapper::{CppFunctionBody, CppFunctionKind},
+    pod::PodPhase,
+};
+
+pub(crate) fn create_alloc_and_frees(apis: ApiVec<PodPhase>) -> ApiVec<PodPhase> {
+    apis.into_iter()
+        .flat_map(|api| -> Box<dyn Iterator<Item = Api<PodPhase>>> {
+            match &api {
+                Api::Struct { name, .. } => {
+                    Box::new(create_alloc_and_free(name.name.clone()).chain(std::iter::once(api)))
+                }
+                Api::Subclass { name, .. } => {
+                    Box::new(create_alloc_and_free(name.cpp()).chain(std::iter::once(api)))
+                }
+                _ => Box::new(std::iter::once(api)),
+            }
+        })
+        .collect()
+}
+
+fn create_alloc_and_free(ty_name: QualifiedName) -> impl Iterator<Item = Api<PodPhase>> {
+    let typ = ty_name.to_type_path();
+    let free_inputs: Punctuated<FnArg, Comma> = parse_quote! {
+        arg0: *mut #typ
+    };
+    let alloc_return: ReturnType = parse_quote! {
+        -> *mut #typ
+    };
+    [
+        (
+            TraitSynthesis::AllocUninitialized(ty_name.clone()),
+            get_alloc_name(&ty_name),
+            Punctuated::new(),
+            alloc_return,
+            CppFunctionBody::AllocUninitialized(ty_name.clone()),
+        ),
+        (
+            TraitSynthesis::FreeUninitialized(ty_name.clone()),
+            get_free_name(&ty_name),
+            free_inputs,
+            ReturnType::Default,
+            CppFunctionBody::FreeUninitialized(ty_name.clone()),
+        ),
+    ]
+    .into_iter()
+    .map(
+        move |(synthesis, name, inputs, output, cpp_function_body)| {
+            let ident = name.get_final_ident();
+            let api_name = ApiName::new_from_qualified_name(name);
+            Api::Function {
+                name: api_name,
+                fun: Box::new(FuncToConvert {
+                    ident,
+                    doc_attr: None,
+                    inputs,
+                    output,
+                    vis: parse_quote! { pub },
+                    virtualness: crate::conversion::api::Virtualness::None,
+                    cpp_vis: CppVisibility::Public,
+                    special_member: None,
+                    unused_template_param: false,
+                    references: References::default(),
+                    original_name: None,
+                    self_ty: None,
+                    synthesized_this_type: None,
+                    synthetic_cpp: Some((cpp_function_body, CppFunctionKind::Function)),
+                    add_to_trait: Some(synthesis),
+                    is_deleted: false,
+                    provenance: Provenance::SynthesizedOther,
+                }),
+                analysis: (),
+            }
+        },
+    )
+}
+
+pub(crate) fn get_alloc_name(ty_name: &QualifiedName) -> QualifiedName {
+    get_name(ty_name, "alloc")
+}
+
+pub(crate) fn get_free_name(ty_name: &QualifiedName) -> QualifiedName {
+    get_name(ty_name, "free")
+}
+
+fn get_name(ty_name: &QualifiedName, label: &str) -> QualifiedName {
+    let name = format!("{}_{}", ty_name.get_final_item(), label);
+    let name_id = make_ident(name);
+    QualifiedName::new(ty_name.get_namespace(), name_id)
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/casts.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/casts.rs
similarity index 82%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/casts.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/casts.rs
index 5b5cf3c..330430f 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/casts.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/casts.rs
@@ -1,23 +1,20 @@
 // Copyright 2022 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use itertools::Itertools;
 use quote::quote;
 use syn::{parse_quote, FnArg};
 
 use crate::{
-    conversion::api::{Api, ApiName, CastMutability, References, Synthesis},
+    conversion::{
+        api::{Api, ApiName, CastMutability, Provenance, References, TraitSynthesis},
+        apivec::ApiVec,
+    },
     types::{make_ident, QualifiedName},
 };
 
@@ -28,9 +25,12 @@
 /// But the related code may be useful in future so I'm keeping it around.
 const SUPPORT_MUTABLE_CASTS: bool = false;
 
-use super::pod::{PodAnalysis, PodPhase};
+use super::{
+    fun::function_wrapper::{CppFunctionBody, CppFunctionKind},
+    pod::{PodAnalysis, PodPhase},
+};
 
-pub(crate) fn add_casts(apis: Vec<Api<PodPhase>>) -> Vec<Api<PodPhase>> {
+pub(crate) fn add_casts(apis: ApiVec<PodPhase>) -> ApiVec<PodPhase> {
     apis.into_iter()
         .flat_map(|api| {
             let mut resultant_apis = match api {
@@ -91,7 +91,6 @@
     };
     Api::Function {
         name: ApiName::new_from_qualified_name(name),
-        name_for_gc: None,
         fun: Box::new(crate::conversion::api::FuncToConvert {
             ident,
             doc_attr: None,
@@ -108,11 +107,13 @@
             original_name: None,
             self_ty: Some(from.clone()),
             synthesized_this_type: None,
-            synthesis: Some(Synthesis::Cast {
+            add_to_trait: Some(TraitSynthesis::Cast {
                 to_type: to.clone(),
                 mutable,
             }),
+            synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)),
             is_deleted: false,
+            provenance: Provenance::SynthesizedOther,
         }),
         analysis: (),
     }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/constructor_deps.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/constructor_deps.rs
new file mode 100644
index 0000000..4fe2825
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/constructor_deps.rs
@@ -0,0 +1,105 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+
+use crate::{
+    conversion::{
+        api::{Api, ApiName, StructDetails, TypeKind},
+        apivec::ApiVec,
+        convert_error::ConvertErrorWithContext,
+        error_reporter::convert_apis,
+    },
+    types::QualifiedName,
+};
+
+use super::fun::{
+    FnAnalysis, FnKind, FnPhase, FnPrePhase2, PodAndConstructorAnalysis, PodAndDepAnalysis,
+    TraitMethodKind,
+};
+
+/// We've now analyzed all functions (including both implicit and explicit
+/// constructors). Decorate each struct with a note of its constructors,
+/// which will later be used as edges in the garbage collection, because
+/// typically any use of a type will require us to call its copy or move
+/// constructor. The same applies to its alloc/free functions.
+pub(crate) fn decorate_types_with_constructor_deps(apis: ApiVec<FnPrePhase2>) -> ApiVec<FnPhase> {
+    let mut constructors_and_allocators_by_type = find_important_constructors(&apis);
+    let mut results = ApiVec::new();
+    convert_apis(
+        apis,
+        &mut results,
+        Api::fun_unchanged,
+        |name, details, pod| {
+            decorate_struct(name, details, pod, &mut constructors_and_allocators_by_type)
+        },
+        Api::enum_unchanged,
+        Api::typedef_unchanged,
+    );
+    results
+}
+
+fn decorate_struct(
+    name: ApiName,
+    details: Box<StructDetails>,
+    fn_struct: PodAndConstructorAnalysis,
+    constructors_and_allocators_by_type: &mut HashMap<QualifiedName, Vec<QualifiedName>>,
+) -> Result<Box<dyn Iterator<Item = Api<FnPhase>>>, ConvertErrorWithContext> {
+    let pod = fn_struct.pod;
+    let is_abstract = matches!(pod.kind, TypeKind::Abstract);
+    let constructor_and_allocator_deps = if is_abstract || pod.is_generic {
+        Vec::new()
+    } else {
+        constructors_and_allocators_by_type
+            .remove(&name.name)
+            .unwrap_or_default()
+    };
+    Ok(Box::new(std::iter::once(Api::Struct {
+        name,
+        details,
+        analysis: PodAndDepAnalysis {
+            pod,
+            constructor_and_allocator_deps,
+            constructors: fn_struct.constructors,
+        },
+    })))
+}
+
+fn find_important_constructors(
+    apis: &ApiVec<FnPrePhase2>,
+) -> HashMap<QualifiedName, Vec<QualifiedName>> {
+    let mut results: HashMap<QualifiedName, Vec<QualifiedName>> = HashMap::new();
+    for api in apis.iter() {
+        if let Api::Function {
+            name,
+            analysis:
+                FnAnalysis {
+                    kind:
+                        FnKind::TraitMethod {
+                            kind:
+                                TraitMethodKind::Alloc
+                                | TraitMethodKind::Dealloc
+                                | TraitMethodKind::CopyConstructor
+                                | TraitMethodKind::MoveConstructor,
+                            impl_for,
+                            ..
+                        },
+                    ignore_reason: Ok(_),
+                    ..
+                },
+            ..
+        } = api
+        {
+            results
+                .entry(impl_for.clone())
+                .or_default()
+                .push(name.name.clone())
+        }
+    }
+    results
+}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/ctypes.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/ctypes.rs
new file mode 100644
index 0000000..714ef0c
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/ctypes.rs
@@ -0,0 +1,36 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+
+use syn::Ident;
+
+use crate::conversion::api::ApiName;
+use crate::conversion::apivec::ApiVec;
+use crate::types::Namespace;
+use crate::{conversion::api::Api, known_types::known_types, types::QualifiedName};
+
+use super::deps::HasDependencies;
+use super::fun::FnPhase;
+
+/// Spot any variable-length C types (e.g. unsigned long)
+/// used in the [Api]s and append those as extra APIs.
+pub(crate) fn append_ctype_information(apis: &mut ApiVec<FnPhase>) {
+    let ctypes: HashMap<Ident, QualifiedName> = apis
+        .iter()
+        .flat_map(|api| api.deps())
+        .filter(|ty| known_types().is_ctype(ty))
+        .map(|ty| (ty.get_final_ident(), ty.clone()))
+        .collect();
+    for (id, typename) in ctypes {
+        apis.push(Api::CType {
+            name: ApiName::new(&Namespace::new(), id),
+            typename,
+        });
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/deps.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/deps.rs
new file mode 100644
index 0000000..26dd081b
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/deps.rs
@@ -0,0 +1,114 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use itertools::Itertools;
+
+use crate::{
+    conversion::api::{Api, TypeKind},
+    types::QualifiedName,
+};
+
+use super::{
+    fun::{FnPhase, FnPrePhase1, PodAndDepAnalysis},
+    pod::PodAnalysis,
+    tdef::TypedefAnalysis,
+};
+
+pub(crate) trait HasDependencies {
+    fn name(&self) -> &QualifiedName;
+    fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_>;
+
+    fn format_deps(&self) -> String {
+        self.deps().join(",")
+    }
+}
+
+impl HasDependencies for Api<FnPrePhase1> {
+    fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        match self {
+            Api::Typedef {
+                old_tyname,
+                analysis: TypedefAnalysis { deps, .. },
+                ..
+            } => Box::new(old_tyname.iter().chain(deps.iter())),
+            Api::Struct {
+                analysis:
+                    PodAnalysis {
+                        kind: TypeKind::Pod,
+                        bases,
+                        field_deps,
+                        ..
+                    },
+                ..
+            } => Box::new(field_deps.iter().chain(bases.iter())),
+            Api::Function { analysis, .. } => Box::new(analysis.deps.iter()),
+            Api::Subclass {
+                name: _,
+                superclass,
+            } => Box::new(std::iter::once(superclass)),
+            Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
+            _ => Box::new(std::iter::empty()),
+        }
+    }
+
+    fn name(&self) -> &QualifiedName {
+        self.name()
+    }
+}
+
+impl HasDependencies for Api<FnPhase> {
+    /// Any dependencies on other APIs which this API has.
+    fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+        match self {
+            Api::Typedef {
+                old_tyname,
+                analysis: TypedefAnalysis { deps, .. },
+                ..
+            } => Box::new(old_tyname.iter().chain(deps.iter())),
+            Api::Struct {
+                analysis:
+                    PodAndDepAnalysis {
+                        pod:
+                            PodAnalysis {
+                                kind: TypeKind::Pod,
+                                bases,
+                                field_deps,
+                                ..
+                            },
+                        constructor_and_allocator_deps,
+                        ..
+                    },
+                ..
+            } => Box::new(
+                field_deps
+                    .iter()
+                    .chain(bases.iter())
+                    .chain(constructor_and_allocator_deps.iter()),
+            ),
+            Api::Struct {
+                analysis:
+                    PodAndDepAnalysis {
+                        constructor_and_allocator_deps,
+                        ..
+                    },
+                ..
+            } => Box::new(constructor_and_allocator_deps.iter()),
+            Api::Function { analysis, .. } => Box::new(analysis.deps.iter()),
+            Api::Subclass {
+                name: _,
+                superclass,
+            } => Box::new(std::iter::once(superclass)),
+            Api::RustSubclassFn { details, .. } => Box::new(details.dependencies.iter()),
+            _ => Box::new(std::iter::empty()),
+        }
+    }
+
+    fn name(&self) -> &QualifiedName {
+        self.name()
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/depth_first.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/depth_first.rs
similarity index 60%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/depth_first.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/depth_first.rs
index 7056b50c..bcfc1af1 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/depth_first.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/depth_first.rs
@@ -1,41 +1,27 @@
 // Copyright 2022 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::{HashSet, VecDeque};
 use std::fmt::Debug;
 
 use itertools::Itertools;
 
-use crate::{conversion::api::Api, types::QualifiedName};
+use crate::types::QualifiedName;
 
-use super::fun::FnPhase;
+use super::deps::HasDependencies;
 
 /// Return APIs in a depth-first order, i.e. those with no dependencies first.
-pub(super) fn depth_first(apis: &[Api<FnPhase>]) -> impl Iterator<Item = &Api<FnPhase>> {
-    depth_first_impl(apis)
-}
-
-fn depth_first_impl<T: HasDependencies + Debug>(items: &[T]) -> impl Iterator<Item = &T> {
-    DepthFirstIter {
-        queue: items.iter().collect(),
-        yet_to_do: items.iter().map(|api| api.name()).collect(),
-    }
-}
-
-trait HasDependencies {
-    fn name(&self) -> &QualifiedName;
-    fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_>;
+pub(super) fn depth_first<'a, T: HasDependencies + Debug + 'a>(
+    inputs: impl Iterator<Item = &'a T> + 'a,
+) -> impl Iterator<Item = &'a T> {
+    let queue: VecDeque<_> = inputs.collect();
+    let yet_to_do = queue.iter().map(|api| api.name()).collect();
+    DepthFirstIter { queue, yet_to_do }
 }
 
 struct DepthFirstIter<'a, T: HasDependencies + Debug> {
@@ -68,21 +54,11 @@
     }
 }
 
-impl HasDependencies for Api<FnPhase> {
-    fn name(&self) -> &QualifiedName {
-        self.name()
-    }
-
-    fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_> {
-        self.deps()
-    }
-}
-
 #[cfg(test)]
 mod test {
     use crate::types::QualifiedName;
 
-    use super::{depth_first_impl, HasDependencies};
+    use super::{depth_first, HasDependencies};
 
     #[derive(Debug)]
     struct Thing(QualifiedName, Vec<QualifiedName>);
@@ -92,8 +68,8 @@
             &self.0
         }
 
-        fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_> {
-            Box::new(self.1.iter().cloned())
+        fn deps(&self) -> Box<dyn Iterator<Item = &QualifiedName> + '_> {
+            Box::new(self.1.iter())
         }
     }
 
@@ -112,7 +88,7 @@
             vec![QualifiedName::new_from_cpp_name("a")],
         );
         let api_list = vec![a, b, c];
-        let mut it = depth_first_impl(&api_list);
+        let mut it = depth_first(api_list.iter());
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("a"));
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("c"));
         assert_eq!(it.next().unwrap().0, QualifiedName::new_from_cpp_name("b"));
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/bridge_name_tracker.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/bridge_name_tracker.rs
similarity index 90%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/bridge_name_tracker.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/bridge_name_tracker.rs
index 9658e28..7e81c59 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/bridge_name_tracker.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/bridge_name_tracker.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::types::Namespace;
 use itertools::Itertools;
@@ -82,6 +76,11 @@
         found_name: &str,
         ns: &Namespace,
     ) -> String {
+        let found_name = if found_name == "new" {
+            "new_autocxx"
+        } else {
+            found_name
+        };
         let count = self
             .next_cxx_bridge_name_for_prefix
             .entry(found_name.to_string())
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/function_wrapper.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/function_wrapper.rs
similarity index 74%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/function_wrapper.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/function_wrapper.rs
index aaa68ad..b260bfb 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/function_wrapper.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/function_wrapper.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::{
     conversion::api::SubclassName,
@@ -21,18 +15,25 @@
 #[derive(Clone, Debug)]
 pub(crate) enum CppConversionType {
     None,
+    Move,
     FromUniquePtrToValue,
+    FromPtrToValue,
     FromValueToUniquePtr,
     FromPtrToMove,
 }
 
 impl CppConversionType {
+    /// If we've found a function which does X to its parameter, what
+    /// is the opposite of X? This is used for subclasses where calls
+    /// from Rust to C++ might also involve calls from C++ to Rust.
     fn inverse(&self) -> Self {
         match self {
             CppConversionType::None => CppConversionType::None,
-            CppConversionType::FromUniquePtrToValue => CppConversionType::FromValueToUniquePtr,
+            CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToValue => {
+                CppConversionType::FromValueToUniquePtr
+            }
             CppConversionType::FromValueToUniquePtr => CppConversionType::FromUniquePtrToValue,
-            CppConversionType::FromPtrToMove => panic!("Did not expect to have to invert move"),
+            _ => panic!("Did not expect to have to invert this conversion"),
         }
     }
 }
@@ -45,6 +46,7 @@
     FromPinMaybeUninitToPtr,
     FromPinMoveRefToPtr,
     FromTypeToPtr,
+    FromValueParamToPtr,
 }
 
 impl RustConversionType {
@@ -105,6 +107,12 @@
     pub(crate) fn converted_rust_type(&self) -> Type {
         match self.cpp_conversion {
             CppConversionType::FromUniquePtrToValue => self.make_unique_ptr_type(),
+            CppConversionType::FromPtrToValue => {
+                let innerty = &self.unwrapped_type;
+                parse_quote! {
+                    *mut #innerty
+                }
+            }
             _ => self.unwrapped_type.clone(),
         }
     }
@@ -120,6 +128,9 @@
         !matches!(self.rust_conversion, RustConversionType::None)
     }
 
+    /// Subclass support involves calls from Rust -> C++, but
+    /// also from C++ -> Rust. Work out the correct argument conversion
+    /// type for the latter call, when given the former.
     pub(crate) fn inverse(&self) -> Self {
         Self {
             unwrapped_type: self.unwrapped_type.clone(),
@@ -127,10 +138,16 @@
             rust_conversion: self.rust_conversion.clone(),
         }
     }
+
+    pub(crate) fn bridge_unsafe_needed(&self) -> bool {
+        matches!(
+            self.rust_conversion,
+            RustConversionType::FromValueParamToPtr
+        )
+    }
 }
 
 #[derive(Clone)]
-
 pub(crate) enum CppFunctionBody {
     FunctionCall(Namespace, Ident),
     StaticMethodCall(Namespace, Ident, Ident),
@@ -139,10 +156,11 @@
     ConstructSuperclass(String),
     Cast,
     Destructor(Namespace, Ident),
+    AllocUninitialized(QualifiedName),
+    FreeUninitialized(QualifiedName),
 }
 
 #[derive(Clone)]
-
 pub(crate) enum CppFunctionKind {
     Function,
     Method,
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/implicit_constructors.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/implicit_constructors.rs
new file mode 100644
index 0000000..a0356ca
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/implicit_constructors.rs
@@ -0,0 +1,659 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::{hash_map, HashMap};
+
+use syn::Type;
+
+use crate::{
+    conversion::{
+        analysis::{depth_first::depth_first, pod::PodAnalysis, type_converter::TypeKind},
+        api::{Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind},
+        apivec::ApiVec,
+    },
+    known_types::{known_types, KnownTypeConstructorDetails},
+    types::QualifiedName,
+};
+
+use super::{FnAnalysis, FnKind, FnPrePhase1, MethodKind, ReceiverMutability, TraitMethodKind};
+
+/// Indicates what we found out about a category of special member function.
+///
+/// In the end, we only care whether it's public and exists, but we track a bit more information to
+/// support determining the information for dependent classes.
+#[derive(Debug, Copy, Clone)]
+pub(super) enum SpecialMemberFound {
+    /// This covers being deleted in any way:
+    ///   * Explicitly deleted
+    ///   * Implicitly defaulted when that means being deleted
+    ///   * Explicitly defaulted when that means being deleted
+    ///
+    /// It also covers not being either user declared or implicitly defaulted.
+    NotPresent,
+    /// Implicit special member functions, indicated by this, are always public.
+    Implicit,
+    /// This covers being explicitly defaulted (when that is not deleted) or being user-defined.
+    Explicit(CppVisibility),
+}
+
+impl SpecialMemberFound {
+    /// Returns whether code outside of subclasses can call this special member function.
+    pub fn callable_any(&self) -> bool {
+        matches!(self, Self::Explicit(CppVisibility::Public) | Self::Implicit)
+    }
+
+    /// Returns whether code in a subclass can call this special member function.
+    pub fn callable_subclass(&self) -> bool {
+        matches!(
+            self,
+            Self::Explicit(CppVisibility::Public)
+                | Self::Explicit(CppVisibility::Protected)
+                | Self::Implicit
+        )
+    }
+
+    /// Returns whether this exists at all. Note that this will return true even if it's private,
+    /// which is generally not very useful, but does come into play for some rules around which
+    /// default special member functions are deleted vs don't exist.
+    pub fn exists(&self) -> bool {
+        matches!(self, Self::Explicit(_) | Self::Implicit)
+    }
+
+    pub fn exists_implicit(&self) -> bool {
+        matches!(self, Self::Implicit)
+    }
+
+    pub fn exists_explicit(&self) -> bool {
+        matches!(self, Self::Explicit(_))
+    }
+}
+
+/// Information about which special member functions exist based on the C++ rules.
+///
+/// Not all of this information is used directly, but we need to track it to determine the
+/// information we do need for classes which are used as members or base classes.
+#[derive(Debug, Clone)]
+pub(super) struct ItemsFound {
+    pub(super) default_constructor: SpecialMemberFound,
+    pub(super) destructor: SpecialMemberFound,
+    pub(super) const_copy_constructor: SpecialMemberFound,
+    /// Remember that [`const_copy_constructor`] may be used in place of this if it exists.
+    pub(super) non_const_copy_constructor: SpecialMemberFound,
+    pub(super) move_constructor: SpecialMemberFound,
+
+    /// The full name of the type. We identify instances by [`QualifiedName`], because that's
+    /// the only thing which [`FnKind::Method`] has to tie it to, and that's unique enough for
+    /// identification.  However, when generating functions for implicit special members, we need
+    /// the extra information here.
+    ///
+    /// Will always be `Some` if any of the other fields are [`SpecialMemberFound::Implict`],
+    /// otherwise optional.
+    pub(super) name: Option<ApiName>,
+}
+
+impl ItemsFound {
+    /// Returns whether we should generate a default constructor wrapper, because bindgen won't do
+    /// one for the implicit default constructor which exists.
+    pub(super) fn implicit_default_constructor_needed(&self) -> bool {
+        self.default_constructor.exists_implicit()
+    }
+
+    /// Returns whether we should generate a copy constructor wrapper, because bindgen won't do one
+    /// for the implicit copy constructor which exists.
+    pub(super) fn implicit_copy_constructor_needed(&self) -> bool {
+        let any_implicit_copy = self.const_copy_constructor.exists_implicit()
+            || self.non_const_copy_constructor.exists_implicit();
+        let no_explicit_copy = !(self.const_copy_constructor.exists_explicit()
+            || self.non_const_copy_constructor.exists_explicit());
+        any_implicit_copy && no_explicit_copy
+    }
+
+    /// Returns whether we should generate a move constructor wrapper, because bindgen won't do one
+    /// for the implicit move constructor which exists.
+    pub(super) fn implicit_move_constructor_needed(&self) -> bool {
+        self.move_constructor.exists_implicit()
+    }
+
+    /// Returns whether we should generate a destructor wrapper, because bindgen won't do one for
+    /// the implicit destructor which exists.
+    pub(super) fn implicit_destructor_needed(&self) -> bool {
+        self.destructor.exists_implicit()
+    }
+}
+#[derive(Hash, Eq, PartialEq)]
+enum ExplicitKind {
+    DefaultConstructor,
+    ConstCopyConstructor,
+    NonConstCopyConstructor,
+    MoveConstructor,
+    OtherConstructor,
+    Destructor,
+    ConstCopyAssignmentOperator,
+    NonConstCopyAssignmentOperator,
+    MoveAssignmentOperator,
+}
+
+/// Denotes a specific kind of explicit member function that we found.
+#[derive(Hash, Eq, PartialEq)]
+struct ExplicitType {
+    ty: QualifiedName,
+    kind: ExplicitKind,
+}
+
+/// Includes information about an explicit special member function which was found.
+// TODO: Add Defaulted(CppVisibility) for https://github.com/google/autocxx/issues/815.
+#[derive(Copy, Clone, Debug)]
+enum ExplicitFound {
+    UserDefined(CppVisibility),
+    /// Note that this always means explicitly deleted, because this enum only represents
+    /// explicit declarations.
+    Deleted,
+    /// Indicates that we found more than one explicit of this kind. This is possible with most of
+    /// them, and we just bail and mostly act as if they're deleted. We'd have to decide whether
+    /// they're ambiguous to use them, which is really complicated.
+    Multiple,
+}
+
+/// Analyzes which constructors are present for each type.
+///
+/// If a type has explicit constructors, bindgen will generate corresponding
+/// constructor functions, which we'll have already converted to make_unique methods.
+/// For types with implicit constructors, we enumerate them here.
+///
+/// It is tempting to make this a separate analysis phase, to be run later than
+/// the function analysis; but that would make the code much more complex as it
+/// would need to output a `FnAnalysisBody`. By running it as part of this phase
+/// we can simply generate the sort of thing bindgen generates, then ask
+/// the existing code in this phase to figure out what to do with it.
+pub(super) fn find_constructors_present(
+    apis: &ApiVec<FnPrePhase1>,
+) -> HashMap<QualifiedName, ItemsFound> {
+    let explicits = find_explicit_items(apis);
+
+    // These contain all the classes we've seen so far with the relevant properties on their
+    // constructors of each kind. We iterate via [`depth_first`], so analyzing later classes
+    // just needs to check these.
+    //
+    // Important only to ask for a depth-first analysis of structs, because
+    // when all APIs are considered there may be reference loops and that would
+    // panic.
+    //
+    // These analyses include all bases and members of each class.
+    let mut all_items_found: HashMap<QualifiedName, ItemsFound> = HashMap::new();
+
+    for api in depth_first(apis.iter()) {
+        if let Api::Struct {
+            name,
+            analysis: PodAnalysis {
+                bases, field_info, ..
+            },
+            details,
+            ..
+        } = api
+        {
+            let find_explicit = |kind: ExplicitKind| -> Option<&ExplicitFound> {
+                explicits.get(&ExplicitType {
+                    ty: name.name.clone(),
+                    kind,
+                })
+            };
+            let get_items_found = |qn: &QualifiedName| -> Option<ItemsFound> {
+                if let Some(constructor_details) = known_types().get_constructor_details(qn) {
+                    Some(known_type_items_found(constructor_details))
+                } else {
+                    all_items_found.get(qn).cloned()
+                }
+            };
+            let bases_items_found: Vec<_> = bases.iter().map_while(get_items_found).collect();
+            let fields_items_found: Vec<_> = field_info
+                .iter()
+                .filter_map(|field_info| match field_info.type_kind {
+                    TypeKind::Regular | TypeKind::SubclassHolder(_) => match field_info.ty {
+                        Type::Path(ref qn) => get_items_found(&QualifiedName::from_type_path(qn)),
+                        _ => None,
+                    },
+                    // TODO: https://github.com/google/autocxx/issues/865 Figure out how to
+                    // differentiate between pointers and references coming from C++. Pointers
+                    // have a default constructor.
+                    TypeKind::Pointer | TypeKind::Reference | TypeKind::MutableReference => {
+                        Some(ItemsFound {
+                            default_constructor: SpecialMemberFound::NotPresent,
+                            destructor: SpecialMemberFound::Implicit,
+                            const_copy_constructor: SpecialMemberFound::Implicit,
+                            non_const_copy_constructor: SpecialMemberFound::NotPresent,
+                            move_constructor: SpecialMemberFound::Implicit,
+                            name: Some(name.clone()),
+                        })
+                    }
+                })
+                .collect();
+            let has_rvalue_reference_fields = details.has_rvalue_reference_fields;
+
+            // Check that all the bases and field types are known first. This combined with
+            // iterating via [`depth_first`] means we can safely search in `items_found` for all of
+            // them.
+            //
+            // Conservatively, we will not acknowledge the existence of most defaulted or implicit
+            // special member functions for any struct/class where we don't fully understand all
+            // field types.  However, we can still look for explictly declared versions and use
+            // those. See below for destructors.
+            //
+            // We need to extend our knowledge to understand the constructor behavior of things in
+            // known_types.rs, then we'll be able to cope with types which contain strings,
+            // unique_ptrs etc.
+            let items_found = if bases_items_found.len() != bases.len()
+                || fields_items_found.len() != field_info.len()
+            {
+                let is_explicit = |kind: ExplicitKind| -> SpecialMemberFound {
+                    // TODO: For https://github.com/google/autocxx/issues/815, map
+                    // ExplicitFound::Defaulted(_) to NotPresent.
+                    match find_explicit(kind) {
+                        None => SpecialMemberFound::NotPresent,
+                        Some(ExplicitFound::Deleted | ExplicitFound::Multiple) => {
+                            SpecialMemberFound::NotPresent
+                        }
+                        Some(ExplicitFound::UserDefined(visibility)) => {
+                            SpecialMemberFound::Explicit(*visibility)
+                        }
+                    }
+                };
+                let items_found = ItemsFound {
+                    default_constructor: is_explicit(ExplicitKind::DefaultConstructor),
+                    destructor: match find_explicit(ExplicitKind::Destructor) {
+                        // Assume that unknown types have destructors. This is common, and allows
+                        // use to generate UniquePtr wrappers with them.
+                        //
+                        // However, this will generate C++ code that doesn't compile if the unknown
+                        // type does not have an accessible destructor. Maybe we should have a way
+                        // to disable that?
+                        //
+                        // TODO: For https://github.com/google/autocxx/issues/815, map
+                        // ExplicitFound::Defaulted(_) to Explicit.
+                        None => SpecialMemberFound::Implicit,
+                        // If there are multiple destructors, assume that one of them will be
+                        // selected by overload resolution.
+                        Some(ExplicitFound::Multiple) => {
+                            SpecialMemberFound::Explicit(CppVisibility::Public)
+                        }
+                        Some(ExplicitFound::Deleted) => SpecialMemberFound::NotPresent,
+                        Some(ExplicitFound::UserDefined(visibility)) => {
+                            SpecialMemberFound::Explicit(*visibility)
+                        }
+                    },
+                    const_copy_constructor: is_explicit(ExplicitKind::ConstCopyConstructor),
+                    non_const_copy_constructor: is_explicit(ExplicitKind::NonConstCopyConstructor),
+                    move_constructor: is_explicit(ExplicitKind::MoveConstructor),
+                    name: Some(name.clone()),
+                };
+                log::info!(
+                    "Special member functions (explicits only) found for {:?}: {:?}",
+                    name,
+                    items_found
+                );
+                items_found
+            } else {
+                // If no user-declared constructors of any kind are provided for a class type (struct, class, or union),
+                // the compiler will always declare a default constructor as an inline public member of its class.
+                //
+                // The implicitly-declared or defaulted default constructor for class T is defined as deleted if any of the following is true:
+                // T has a member of reference type without a default initializer.
+                // T has a non-const-default-constructible const member without a default member initializer.
+                // T has a member (without a default member initializer) which has a deleted default constructor, or its default constructor is ambiguous or inaccessible from this constructor.
+                // T has a direct or virtual base which has a deleted default constructor, or it is ambiguous or inaccessible from this constructor.
+                // T has a direct or virtual base or a non-static data member which has a deleted destructor, or a destructor that is inaccessible from this constructor.
+                // T is a union with at least one variant member with non-trivial default constructor, and no variant member of T has a default member initializer. // we don't support unions anyway
+                // T is a non-union class with a variant member M with a non-trivial default constructor, and no variant member of the anonymous union containing M has a default member initializer.
+                // T is a union and all of its variant members are const. // we don't support unions anyway
+                //
+                // Variant members are the members of anonymous unions.
+                let default_constructor = {
+                    let explicit = find_explicit(ExplicitKind::DefaultConstructor);
+                    // TODO: For https://github.com/google/autocxx/issues/815, replace the first term with:
+                    //   explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_)))
+                    let have_defaulted = explicit.is_none()
+                        && !explicits.iter().any(|(ExplicitType { ty, kind }, _)| {
+                            ty == &name.name
+                                && match *kind {
+                                    ExplicitKind::DefaultConstructor => false,
+                                    ExplicitKind::ConstCopyConstructor => true,
+                                    ExplicitKind::NonConstCopyConstructor => true,
+                                    ExplicitKind::MoveConstructor => true,
+                                    ExplicitKind::OtherConstructor => true,
+                                    ExplicitKind::Destructor => false,
+                                    ExplicitKind::ConstCopyAssignmentOperator => false,
+                                    ExplicitKind::NonConstCopyAssignmentOperator => false,
+                                    ExplicitKind::MoveAssignmentOperator => false,
+                                }
+                        });
+                    if have_defaulted {
+                        let bases_allow = bases_items_found.iter().all(|items_found| {
+                            items_found.destructor.callable_subclass()
+                                && items_found.default_constructor.callable_subclass()
+                        });
+                        // TODO: Allow member initializers for
+                        // https://github.com/google/autocxx/issues/816.
+                        let members_allow = fields_items_found.iter().all(|items_found| {
+                            items_found.destructor.callable_any()
+                                && items_found.default_constructor.callable_any()
+                        });
+                        if !has_rvalue_reference_fields && bases_allow && members_allow {
+                            // TODO: For https://github.com/google/autocxx/issues/815, grab the
+                            // visibility from an explicit default if present.
+                            SpecialMemberFound::Implicit
+                        } else {
+                            SpecialMemberFound::NotPresent
+                        }
+                    } else if let Some(ExplicitFound::UserDefined(visibility)) = explicit {
+                        SpecialMemberFound::Explicit(*visibility)
+                    } else {
+                        SpecialMemberFound::NotPresent
+                    }
+                };
+
+                // If no user-declared prospective destructor is provided for a class type (struct, class, or union), the compiler will always declare a destructor as an inline public member of its class.
+                //
+                // The implicitly-declared or explicitly defaulted destructor for class T is defined as deleted if any of the following is true:
+                // T has a non-static data member that cannot be destructed (has deleted or inaccessible destructor)
+                // T has direct or virtual base class that cannot be destructed (has deleted or inaccessible destructors)
+                // T is a union and has a variant member with non-trivial destructor. // we don't support unions anyway
+                // The implicitly-declared destructor is virtual (because the base class has a virtual destructor) and the lookup for the deallocation function (operator delete()) results in a call to ambiguous, deleted, or inaccessible function.
+                let destructor = {
+                    let explicit = find_explicit(ExplicitKind::Destructor);
+                    // TODO: For https://github.com/google/autocxx/issues/815, replace the condition with:
+                    //   explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_)))
+                    if explicit.is_none() {
+                        let bases_allow = bases_items_found
+                            .iter()
+                            .all(|items_found| items_found.destructor.callable_subclass());
+                        let members_allow = fields_items_found
+                            .iter()
+                            .all(|items_found| items_found.destructor.callable_any());
+                        if bases_allow && members_allow {
+                            // TODO: For https://github.com/google/autocxx/issues/815, grab the
+                            // visibility from an explicit default if present.
+                            SpecialMemberFound::Implicit
+                        } else {
+                            SpecialMemberFound::NotPresent
+                        }
+                    } else if let Some(ExplicitFound::UserDefined(visibility)) = explicit {
+                        SpecialMemberFound::Explicit(*visibility)
+                    } else {
+                        SpecialMemberFound::NotPresent
+                    }
+                };
+
+                // If no user-defined copy constructors are provided for a class type (struct, class, or union),
+                // the compiler will always declare a copy constructor as a non-explicit inline public member of its class.
+                // This implicitly-declared copy constructor has the form T::T(const T&) if all of the following are true:
+                //  each direct and virtual base B of T has a copy constructor whose parameters are const B& or const volatile B&;
+                //  each non-static data member M of T of class type or array of class type has a copy constructor whose parameters are const M& or const volatile M&.
+                //
+                // The implicitly-declared or defaulted copy constructor for class T is defined as deleted if any of the following conditions are true:
+                // T is a union-like class and has a variant member with non-trivial copy constructor; // we don't support unions anyway
+                // T has a user-defined move constructor or move assignment operator (this condition only causes the implicitly-declared, not the defaulted, copy constructor to be deleted).
+                // T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);
+                // T has direct or virtual base class that cannot be copied (has deleted, inaccessible, or ambiguous copy constructors);
+                // T has direct or virtual base class or a non-static data member with a deleted or inaccessible destructor;
+                // T has a data member of rvalue reference type;
+                let (const_copy_constructor, non_const_copy_constructor) = {
+                    let explicit_const = find_explicit(ExplicitKind::ConstCopyConstructor);
+                    let explicit_non_const = find_explicit(ExplicitKind::NonConstCopyConstructor);
+                    let explicit_move = find_explicit(ExplicitKind::MoveConstructor);
+
+                    // TODO: For https://github.com/google/autocxx/issues/815, replace both terms with something like:
+                    //   explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_)))
+                    let have_defaulted = explicit_const.is_none() && explicit_non_const.is_none();
+                    if have_defaulted {
+                        // TODO: For https://github.com/google/autocxx/issues/815, ignore this if
+                        // the relevant (based on bases_are_const) copy constructor is explicitly defaulted.
+                        let class_allows = explicit_move.is_none() && !has_rvalue_reference_fields;
+                        let bases_allow = bases_items_found.iter().all(|items_found| {
+                            items_found.destructor.callable_subclass()
+                                && (items_found.const_copy_constructor.callable_subclass()
+                                    || items_found.non_const_copy_constructor.callable_subclass())
+                        });
+                        let members_allow = fields_items_found.iter().all(|items_found| {
+                            items_found.destructor.callable_any()
+                                && (items_found.const_copy_constructor.callable_any()
+                                    || items_found.non_const_copy_constructor.callable_any())
+                        });
+                        if class_allows && bases_allow && members_allow {
+                            // TODO: For https://github.com/google/autocxx/issues/815, grab the
+                            // visibility and existence of const and non-const from an explicit default if present.
+                            let dependencies_are_const = bases_items_found
+                                .iter()
+                                .chain(fields_items_found.iter())
+                                .all(|items_found| items_found.const_copy_constructor.exists());
+                            if dependencies_are_const {
+                                (SpecialMemberFound::Implicit, SpecialMemberFound::NotPresent)
+                            } else {
+                                (SpecialMemberFound::NotPresent, SpecialMemberFound::Implicit)
+                            }
+                        } else {
+                            (
+                                SpecialMemberFound::NotPresent,
+                                SpecialMemberFound::NotPresent,
+                            )
+                        }
+                    } else {
+                        (
+                            if let Some(ExplicitFound::UserDefined(visibility)) = explicit_const {
+                                SpecialMemberFound::Explicit(*visibility)
+                            } else {
+                                SpecialMemberFound::NotPresent
+                            },
+                            if let Some(ExplicitFound::UserDefined(visibility)) = explicit_non_const
+                            {
+                                SpecialMemberFound::Explicit(*visibility)
+                            } else {
+                                SpecialMemberFound::NotPresent
+                            },
+                        )
+                    }
+                };
+
+                // If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
+                // there are no user-declared copy constructors;
+                // there are no user-declared copy assignment operators;
+                // there are no user-declared move assignment operators;
+                // there is no user-declared destructor.
+                // then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).
+                //
+                // A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&). If some user-defined move constructors are present, the user may still force the generation of the implicitly declared move constructor with the keyword default.
+                //
+                // The implicitly-declared or defaulted move constructor for class T is defined as deleted if any of the following is true:
+                // T has non-static data members that cannot be moved (have deleted, inaccessible, or ambiguous move constructors);
+                // T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors);
+                // T has direct or virtual base class with a deleted or inaccessible destructor;
+                // T is a union-like class and has a variant member with non-trivial move constructor. // we don't support unions anyway
+                let move_constructor = {
+                    let explicit = find_explicit(ExplicitKind::MoveConstructor);
+                    // TODO: For https://github.com/google/autocxx/issues/815, replace relevant terms with something like:
+                    //   explicit.map_or(true, |explicit_found| matches!(explicit_found, ExplicitFound::Defaulted(_)))
+                    let have_defaulted = !(explicit.is_some()
+                        || find_explicit(ExplicitKind::ConstCopyConstructor).is_some()
+                        || find_explicit(ExplicitKind::NonConstCopyConstructor).is_some()
+                        || find_explicit(ExplicitKind::ConstCopyAssignmentOperator).is_some()
+                        || find_explicit(ExplicitKind::NonConstCopyAssignmentOperator).is_some()
+                        || find_explicit(ExplicitKind::MoveAssignmentOperator).is_some()
+                        || find_explicit(ExplicitKind::Destructor).is_some());
+                    if have_defaulted {
+                        let bases_allow = bases_items_found.iter().all(|items_found| {
+                            items_found.destructor.callable_subclass()
+                                && items_found.move_constructor.callable_subclass()
+                        });
+                        let members_allow = fields_items_found
+                            .iter()
+                            .all(|items_found| items_found.move_constructor.callable_any());
+                        if bases_allow && members_allow {
+                            // TODO: For https://github.com/google/autocxx/issues/815, grab the
+                            // visibility from an explicit default if present.
+                            SpecialMemberFound::Implicit
+                        } else {
+                            SpecialMemberFound::NotPresent
+                        }
+                    } else if let Some(ExplicitFound::UserDefined(visibility)) = explicit {
+                        SpecialMemberFound::Explicit(*visibility)
+                    } else {
+                        SpecialMemberFound::NotPresent
+                    }
+                };
+
+                let items_found = ItemsFound {
+                    default_constructor,
+                    destructor,
+                    const_copy_constructor,
+                    non_const_copy_constructor,
+                    move_constructor,
+                    name: Some(name.clone()),
+                };
+                log::info!(
+                    "Special member items found for {:?}: {:?}",
+                    name,
+                    items_found
+                );
+                items_found
+            };
+            assert!(
+                all_items_found
+                    .insert(name.name.clone(), items_found)
+                    .is_none(),
+                "Duplicate struct: {:?}",
+                name
+            );
+        }
+    }
+
+    all_items_found
+}
+
+fn find_explicit_items(apis: &ApiVec<FnPrePhase1>) -> HashMap<ExplicitType, ExplicitFound> {
+    let mut result = HashMap::new();
+    let mut merge_fun = |ty: QualifiedName, kind: ExplicitKind, fun: &FuncToConvert| match result
+        .entry(ExplicitType { ty, kind })
+    {
+        hash_map::Entry::Vacant(entry) => {
+            entry.insert(if fun.is_deleted {
+                ExplicitFound::Deleted
+            } else {
+                ExplicitFound::UserDefined(fun.cpp_vis)
+            });
+        }
+        hash_map::Entry::Occupied(mut entry) => {
+            entry.insert(ExplicitFound::Multiple);
+        }
+    };
+    for api in apis.iter() {
+        match api {
+            Api::Function {
+                analysis:
+                    FnAnalysis {
+                        kind: FnKind::Method { impl_for, .. },
+                        param_details,
+                        ..
+                    },
+                fun,
+                ..
+            } if matches!(
+                fun.special_member,
+                Some(SpecialMemberKind::AssignmentOperator)
+            ) =>
+            {
+                let is_move_assignment_operator = !fun.references.rvalue_ref_params.is_empty();
+                merge_fun(
+                    impl_for.clone(),
+                    if is_move_assignment_operator {
+                        ExplicitKind::MoveAssignmentOperator
+                    } else {
+                        let receiver_mutability = &param_details
+                            .iter()
+                            .next()
+                            .unwrap()
+                            .self_type
+                            .as_ref()
+                            .unwrap()
+                            .1;
+                        match receiver_mutability {
+                            ReceiverMutability::Const => ExplicitKind::ConstCopyAssignmentOperator,
+                            ReceiverMutability::Mutable => {
+                                ExplicitKind::NonConstCopyAssignmentOperator
+                            }
+                        }
+                    },
+                    fun,
+                )
+            }
+            Api::Function {
+                analysis:
+                    FnAnalysis {
+                        kind:
+                            FnKind::Method {
+                                impl_for,
+                                method_kind,
+                                ..
+                            },
+                        ..
+                    },
+                fun,
+                ..
+            } => match method_kind {
+                MethodKind::Constructor { is_default: true } => {
+                    Some(ExplicitKind::DefaultConstructor)
+                }
+                MethodKind::Constructor { is_default: false } => {
+                    Some(ExplicitKind::OtherConstructor)
+                }
+                _ => None,
+            }
+            .map_or((), |explicit_kind| {
+                merge_fun(impl_for.clone(), explicit_kind, fun)
+            }),
+            Api::Function {
+                analysis:
+                    FnAnalysis {
+                        kind: FnKind::TraitMethod { impl_for, kind, .. },
+                        ..
+                    },
+                fun,
+                ..
+            } => match kind {
+                TraitMethodKind::Destructor => Some(ExplicitKind::Destructor),
+                // In `analyze_foreign_fn` we mark non-const copy constructors as not being copy
+                // constructors for now, so we don't have to worry about them.
+                TraitMethodKind::CopyConstructor => Some(ExplicitKind::ConstCopyConstructor),
+                TraitMethodKind::MoveConstructor => Some(ExplicitKind::MoveConstructor),
+                _ => None,
+            }
+            .map_or((), |explicit_kind| {
+                merge_fun(impl_for.clone(), explicit_kind, fun)
+            }),
+            _ => (),
+        }
+    }
+    result
+}
+
+/// Returns the information for a given known type.
+fn known_type_items_found(constructor_details: KnownTypeConstructorDetails) -> ItemsFound {
+    let exists_public = SpecialMemberFound::Explicit(CppVisibility::Public);
+    let exists_public_if = |exists| {
+        if exists {
+            exists_public
+        } else {
+            SpecialMemberFound::NotPresent
+        }
+    };
+    ItemsFound {
+        default_constructor: exists_public,
+        destructor: exists_public,
+        const_copy_constructor: exists_public_if(constructor_details.has_const_copy_constructor),
+        non_const_copy_constructor: SpecialMemberFound::NotPresent,
+        move_constructor: exists_public_if(constructor_details.has_move_constructor),
+        name: None,
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/mod.rs
similarity index 61%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/mod.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/mod.rs
index 11806f1..f22f749 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/mod.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/mod.rs
@@ -1,23 +1,15 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 mod bridge_name_tracker;
 pub(crate) mod function_wrapper;
-mod implicit_constructor_rules;
 mod implicit_constructors;
 mod overload_tracker;
-mod rust_name_tracker;
 mod subclass;
 
 use crate::{
@@ -27,11 +19,13 @@
             type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
         },
         api::{
-            ApiName, CastMutability, CppVisibility, FuncToConvert, References, SpecialMemberKind,
-            SubclassName, Synthesis, Virtualness,
+            ApiName, CastMutability, CppVisibility, FuncToConvert, NullPhase, Provenance,
+            References, SpecialMemberKind, SubclassName, TraitImplSignature, TraitSynthesis,
+            UnsafetyNeeded, Virtualness,
         },
-        convert_error::ConvertErrorWithContext,
+        apivec::ApiVec,
         convert_error::ErrorContext,
+        convert_error::{ConvertErrorWithContext, ErrorContextType},
         error_reporter::{convert_apis, report_any_error},
     },
     known_types::known_types,
@@ -42,18 +36,16 @@
 use autocxx_parser::{IncludeCppConfig, UnsafePolicy};
 use function_wrapper::{CppFunction, CppFunctionBody, TypeConversionPolicy};
 use itertools::Itertools;
-use proc_macro2::{Span, TokenStream};
-use quote::{quote, ToTokens};
+use proc_macro2::Span;
+use quote::quote;
 use syn::{
-    parse_quote,
-    punctuated::Punctuated,
-    token::{Comma, Unsafe},
-    FnArg, Ident, Pat, ReturnType, Type, TypePtr, Visibility,
+    parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type,
+    TypePtr, Visibility,
 };
 
 use crate::{
     conversion::{
-        api::{AnalysisPhase, Api, TypeKind, UnanalyzedApi},
+        api::{AnalysisPhase, Api, TypeKind},
         ConvertError,
     },
     types::{make_ident, validate_ident_ok_for_cxx, Namespace, QualifiedName},
@@ -62,10 +54,12 @@
 use self::{
     bridge_name_tracker::BridgeNameTracker,
     function_wrapper::RustConversionType,
-    implicit_constructors::find_missing_constructors,
+    implicit_constructors::{find_constructors_present, ItemsFound},
     overload_tracker::OverloadTracker,
-    rust_name_tracker::RustNameTracker,
-    subclass::{create_subclass_constructor, create_subclass_fn_wrapper, create_subclass_function},
+    subclass::{
+        create_subclass_constructor, create_subclass_fn_wrapper, create_subclass_function,
+        create_subclass_trait_item,
+    },
 };
 
 use super::{
@@ -80,10 +74,10 @@
     Mutable,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum MethodKind {
     Normal(ReceiverMutability),
-    Constructor,
+    Constructor { is_default: bool },
     MakeUnique,
     Static,
     Virtual(ReceiverMutability),
@@ -96,14 +90,13 @@
     MoveConstructor,
     Cast,
     Destructor,
+    Alloc,
+    Dealloc,
 }
 
 #[derive(Clone)]
 pub(crate) struct TraitMethodDetails {
-    pub(crate) impl_for_specifics: TokenStream,
-    pub(crate) trait_signature: TokenStream,
-    /// The trait is 'unsafe' itself
-    pub(crate) trait_unsafety: Option<Unsafe>,
+    pub(crate) trt: TraitImplSignature,
     pub(crate) avoid_self: bool,
     pub(crate) method_name: Ident,
     /// For traits, where we're trying to implement a specific existing
@@ -111,22 +104,25 @@
     /// interface.
     pub(crate) parameter_reordering: Option<Vec<usize>>,
     /// The function we're calling from the trait requires unsafe even
-    /// though the trait isn't.
+    /// though the trait and its function aren't.
     pub(crate) trait_call_is_unsafe: bool,
 }
 
 #[derive(Clone)]
 pub(crate) enum FnKind {
     Function,
-    Method(QualifiedName, MethodKind),
+    Method {
+        method_kind: MethodKind,
+        impl_for: QualifiedName,
+    },
     TraitMethod {
         kind: TraitMethodKind,
         /// The name of the type T for which we're implementing a trait,
         /// though we may be actually implementing the trait for &mut T or
-        /// similar....
+        /// similar, so we store more details of both the type and the
+        /// method in `details`
         impl_for: QualifiedName,
-        /// ... so we also store the tokenstream of the type.
-        details: TraitMethodDetails,
+        details: Box<TraitMethodDetails>,
     },
 }
 
@@ -137,23 +133,21 @@
 pub(crate) enum RustRenameStrategy {
     /// cxx::bridge name matches user expectations
     None,
-    /// We can rename using the #[rust_name] attribute in the cxx::bridge
-    RenameUsingRustAttr,
     /// Even the #[rust_name] attribute would cause conflicts, and we need
     /// to use a 'use XYZ as ABC'
     RenameInOutputMod(Ident),
-}
-
-#[derive(Clone)]
-pub(crate) enum UnsafetyNeeded {
-    None,
-    JustReceiver,
-    Always,
+    /// This function requires us to generate a Rust function to do
+    /// parameter conversion.
+    RenameUsingWrapperFunction,
 }
 
 #[derive(Clone)]
 pub(crate) struct FnAnalysis {
+    /// Each entry in the cxx::bridge needs to have a unique name, even if
+    /// (from the perspective of Rust and C++) things are in different
+    /// namespaces/mods.
     pub(crate) cxxbridge_name: Ident,
+    /// ... so record also the name under which we wish to expose it in Rust.
     pub(crate) rust_name: String,
     pub(crate) rust_rename_strategy: RustRenameStrategy,
     pub(crate) params: Punctuated<FnArg, Comma>,
@@ -173,6 +167,8 @@
     /// Whether this can be called by external code. Not so for
     /// protected methods.
     pub(crate) externally_callable: bool,
+    /// Whether we need to generate a Rust-side calling function
+    pub(crate) rust_wrapper_needed: bool,
 }
 
 #[derive(Clone)]
@@ -182,7 +178,7 @@
     pub(crate) self_type: Option<(QualifiedName, ReceiverMutability)>,
     pub(crate) was_reference: bool,
     pub(crate) deps: HashSet<QualifiedName>,
-    pub(crate) requires_unsafe: bool,
+    pub(crate) requires_unsafe: UnsafetyNeeded,
 }
 
 struct ReturnTypeAnalysis {
@@ -203,18 +199,75 @@
     }
 }
 
-pub(crate) struct FnPhase;
+pub(crate) struct PodAndConstructorAnalysis {
+    pub(crate) pod: PodAnalysis,
+    pub(crate) constructors: PublicConstructors,
+}
 
-impl AnalysisPhase for FnPhase {
+/// An analysis phase where we've analyzed each function, but
+/// haven't yet determined which constructors/etc. belong to each type.
+pub(crate) struct FnPrePhase1;
+
+impl AnalysisPhase for FnPrePhase1 {
     type TypedefAnalysis = TypedefAnalysis;
     type StructAnalysis = PodAnalysis;
     type FunAnalysis = FnAnalysis;
 }
 
+/// An analysis phase where we've analyzed each function, and identified
+/// what implicit constructors/destructors are present in each type.
+pub(crate) struct FnPrePhase2;
+
+impl AnalysisPhase for FnPrePhase2 {
+    type TypedefAnalysis = TypedefAnalysis;
+    type StructAnalysis = PodAndConstructorAnalysis;
+    type FunAnalysis = FnAnalysis;
+}
+
+pub(crate) struct PodAndDepAnalysis {
+    pub(crate) pod: PodAnalysis,
+    pub(crate) constructor_and_allocator_deps: Vec<QualifiedName>,
+    pub(crate) constructors: PublicConstructors,
+}
+
+/// Analysis phase after we've finished analyzing functions and determined
+/// which constructors etc. belong to them.
+pub(crate) struct FnPhase;
+
+/// Indicates which kinds of public constructors are known to exist for a type.
+#[derive(Debug, Default, Copy, Clone)]
+pub(crate) struct PublicConstructors {
+    pub(crate) move_constructor: bool,
+    pub(crate) destructor: bool,
+}
+
+impl PublicConstructors {
+    fn from_items_found(items_found: &ItemsFound) -> Self {
+        Self {
+            move_constructor: items_found.move_constructor.callable_any(),
+            destructor: items_found.destructor.callable_any(),
+        }
+    }
+}
+
+impl AnalysisPhase for FnPhase {
+    type TypedefAnalysis = TypedefAnalysis;
+    type StructAnalysis = PodAndDepAnalysis;
+    type FunAnalysis = FnAnalysis;
+}
+
+/// Whether to allow highly optimized calls because this is a simple Rust->C++ call,
+/// or to use a simpler set of policies because this is a subclass call where
+/// we may have C++->Rust->C++ etc.
+#[derive(Copy, Clone)]
+enum TypeConversionSophistication {
+    Regular,
+    SimpleForSubclasses,
+}
+
 pub(crate) struct FnAnalyzer<'a> {
     unsafe_policy: UnsafePolicy,
-    rust_name_tracker: RustNameTracker,
-    extra_apis: Vec<UnanalyzedApi>,
+    extra_apis: ApiVec<NullPhase>,
     type_converter: TypeConverter<'a>,
     bridge_name_tracker: BridgeNameTracker,
     pod_safe_types: HashSet<QualifiedName>,
@@ -222,18 +275,19 @@
     overload_trackers_by_mod: HashMap<Namespace, OverloadTracker>,
     subclasses_by_superclass: HashMap<QualifiedName, Vec<SubclassName>>,
     nested_type_name_map: HashMap<QualifiedName, String>,
+    generic_types: HashSet<QualifiedName>,
+    existing_superclass_trait_api_names: HashSet<QualifiedName>,
 }
 
 impl<'a> FnAnalyzer<'a> {
     pub(crate) fn analyze_functions(
-        apis: Vec<Api<PodPhase>>,
+        apis: ApiVec<PodPhase>,
         unsafe_policy: UnsafePolicy,
         config: &'a IncludeCppConfig,
-    ) -> Vec<Api<FnPhase>> {
+    ) -> ApiVec<FnPrePhase2> {
         let mut me = Self {
             unsafe_policy,
-            rust_name_tracker: RustNameTracker::new(),
-            extra_apis: Vec::new(),
+            extra_apis: ApiVec::new(),
             type_converter: TypeConverter::new(config, &apis),
             bridge_name_tracker: BridgeNameTracker::new(),
             config,
@@ -241,22 +295,25 @@
             pod_safe_types: Self::build_pod_safe_type_set(&apis),
             subclasses_by_superclass: subclass::subclasses_by_superclass(&apis),
             nested_type_name_map: Self::build_nested_type_map(&apis),
+            generic_types: Self::build_generic_type_set(&apis),
+            existing_superclass_trait_api_names: HashSet::new(),
         };
-        let mut results = Vec::new();
+        let mut results = ApiVec::new();
         convert_apis(
             apis,
             &mut results,
-            |name, fun, _, _| me.analyze_foreign_fn_and_subclasses(name, fun),
+            |name, fun, _| me.analyze_foreign_fn_and_subclasses(name, fun),
             Api::struct_unchanged,
             Api::enum_unchanged,
             Api::typedef_unchanged,
         );
-        me.add_missing_constructors(&mut results);
+        let mut results = me.add_constructors_present(results);
+        me.add_make_uniques(&mut results);
         results.extend(me.extra_apis.into_iter().map(add_analysis));
         results
     }
 
-    fn build_pod_safe_type_set(apis: &[Api<PodPhase>]) -> HashSet<QualifiedName> {
+    fn build_pod_safe_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
         apis.iter()
             .filter_map(|api| match api {
                 Api::Struct {
@@ -286,9 +343,24 @@
             .collect()
     }
 
+    fn build_generic_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
+        apis.iter()
+            .filter_map(|api| match api {
+                Api::Struct {
+                    analysis:
+                        PodAnalysis {
+                            is_generic: true, ..
+                        },
+                    ..
+                } => Some(api.name().clone()),
+                _ => None,
+            })
+            .collect()
+    }
+
     /// Builds a mapping from a qualified type name to the last 'nest'
     /// of its name, if it has multiple elements.
-    fn build_nested_type_map(apis: &[Api<PodPhase>]) -> HashMap<QualifiedName, String> {
+    fn build_nested_type_map(apis: &ApiVec<PodPhase>) -> HashMap<QualifiedName, String> {
         apis.iter()
             .filter_map(|api| match api {
                 Api::Struct { name, .. } | Api::Enum { name, .. } => {
@@ -329,14 +401,14 @@
             .get_unique_cxx_bridge_name(type_name, found_name, ns)
     }
 
-    fn ok_to_use_rust_name(&mut self, rust_name: &str) -> bool {
-        self.rust_name_tracker.ok_to_use_rust_name(rust_name)
-    }
-
     fn is_on_allowlist(&self, type_name: &QualifiedName) -> bool {
         self.config.is_on_allowlist(&type_name.to_cpp_name())
     }
 
+    fn is_generic_type(&self, type_name: &QualifiedName) -> bool {
+        self.generic_types.contains(type_name)
+    }
+
     #[allow(clippy::if_same_then_else)] // clippy bug doesn't notice the two
                                         // closures below are different.
     fn should_be_unsafe(
@@ -344,145 +416,262 @@
         param_details: &[ArgumentAnalysis],
         kind: &FnKind,
     ) -> UnsafetyNeeded {
-        if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe {
-            UnsafetyNeeded::Always
-        } else if param_details
-            .iter()
-            .any(|pd| pd.self_type.is_none() && pd.requires_unsafe)
-        {
-            UnsafetyNeeded::Always
-        } else if matches!(
-            kind,
+        let unsafest_non_self_param = UnsafetyNeeded::from_param_details(param_details, true);
+        let unsafest_param = UnsafetyNeeded::from_param_details(param_details, false);
+        match kind {
+            // Trait unsafety must always correspond to the norms for the
+            // trait we're implementing.
             FnKind::TraitMethod {
-                kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor,
+                kind:
+                    TraitMethodKind::CopyConstructor
+                    | TraitMethodKind::MoveConstructor
+                    | TraitMethodKind::Alloc
+                    | TraitMethodKind::Dealloc,
                 ..
-            }
-        ) {
-            UnsafetyNeeded::Always
-        } else if param_details.iter().any(|pd| pd.requires_unsafe) {
-            UnsafetyNeeded::JustReceiver
-        } else {
-            UnsafetyNeeded::None
+            } => UnsafetyNeeded::Always,
+            FnKind::TraitMethod { .. } => match unsafest_param {
+                UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
+                _ => unsafest_param,
+            },
+            _ if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe => UnsafetyNeeded::Always,
+            _ => match unsafest_non_self_param {
+                UnsafetyNeeded::Always => UnsafetyNeeded::Always,
+                UnsafetyNeeded::JustBridge => match unsafest_param {
+                    UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
+                    _ => unsafest_non_self_param,
+                },
+                UnsafetyNeeded::None => match unsafest_param {
+                    UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
+                    _ => unsafest_param,
+                },
+            },
         }
     }
 
-    /// Analyze a given function, and any permutations of that function which
-    /// we might additionally generate (e.g. for subclasses.)
-    fn analyze_foreign_fn_and_subclasses(
-        &mut self,
-        name: ApiName,
-        fun: Box<FuncToConvert>,
-    ) -> Result<Box<dyn Iterator<Item = Api<FnPhase>>>, ConvertErrorWithContext> {
-        let initial_name = name.clone();
-        let maybe_analysis_and_name = self.analyze_foreign_fn(name, &fun);
+    fn add_make_uniques(&mut self, apis: &mut ApiVec<FnPrePhase2>) {
+        let mut results = ApiVec::new();
 
-        let (analysis, name) = match maybe_analysis_and_name {
-            None => return Ok(Box::new(std::iter::empty())),
-            Some((analysis, name)) => (analysis, name),
-        };
+        // Pre-assemble a list of types with known destructors, to avoid having to
+        // do a O(n^2) nested loop.
+        let types_with_destructors: HashSet<_> = apis
+            .iter()
+            .filter_map(|api| match api {
+                Api::Function {
+                    fun,
+                    analysis:
+                        FnAnalysis {
+                            kind: FnKind::TraitMethod { impl_for, .. },
+                            ..
+                        },
+                    ..
+                } if matches!(
+                    **fun,
+                    FuncToConvert {
+                        special_member: Some(SpecialMemberKind::Destructor),
+                        is_deleted: false,
+                        cpp_vis: CppVisibility::Public,
+                        ..
+                    }
+                ) =>
+                {
+                    Some(impl_for)
+                }
+                _ => None,
+            })
+            .cloned()
+            .collect();
 
-        let mut results = Vec::new();
+        for api in apis.iter() {
+            if let Api::Function {
+                name,
+                fun,
+                analysis:
+                    analysis @ FnAnalysis {
+                        kind:
+                            FnKind::Method {
+                                impl_for: sup,
+                                method_kind: MethodKind::Constructor { .. },
+                                ..
+                            },
+                        ..
+                    },
+                ..
+            } = api
+            {
+                let initial_name = name.clone();
+                // If we don't have an accessible destructor, then std::unique_ptr cannot be
+                // instantiated for this C++ type.
+                if !types_with_destructors.contains(sup) {
+                    continue;
+                }
 
-        // Consider whether we need to synthesize subclass items.
-        match &analysis.kind {
-            FnKind::Method(sup, MethodKind::Constructor) => {
                 // Create a make_unique too
-                let make_unique_func = self.create_make_unique(&fun);
-                self.analyze_and_add_if_necessary(initial_name, make_unique_func, &mut results);
+                self.create_make_unique(fun, initial_name, &mut results);
 
                 for sub in self.subclasses_by_superclass(sup) {
                     // Create a subclass constructor. This is a synthesized function
                     // which didn't exist in the original C++.
                     let (subclass_constructor_func, subclass_constructor_name) =
-                        create_subclass_constructor(sub, &analysis, sup, &fun);
-                    self.analyze_and_add_if_necessary(
+                        create_subclass_constructor(sub, analysis, sup, fun);
+                    self.analyze_and_add(
                         subclass_constructor_name.clone(),
                         subclass_constructor_func.clone(),
                         &mut results,
+                        TypeConversionSophistication::Regular,
                     );
                     // and its corresponding make_unique
-                    let make_unique_func = self.create_make_unique(&subclass_constructor_func);
-                    self.analyze_and_add_if_necessary(
+                    self.create_make_unique(
+                        &subclass_constructor_func,
                         subclass_constructor_name,
-                        make_unique_func,
                         &mut results,
                     );
                 }
             }
-            FnKind::Method(
-                sup,
-                MethodKind::Virtual(receiver_mutability)
-                | MethodKind::PureVirtual(receiver_mutability),
-            ) => {
-                for sub in self.subclasses_by_superclass(sup) {
-                    // For each subclass, we need to create a plain-C++ method to call its superclass
-                    // and a Rust/C++ bridge API to call _that_.
-                    // What we're generating here is entirely about the subclass, so the
-                    // superclass's namespace is irrelevant. We generate
-                    // all subclasses in the root namespace.
-                    let is_pure_virtual = matches!(
-                        &analysis.kind,
-                        FnKind::Method(_, MethodKind::PureVirtual(..))
-                    );
-                    let super_fn_name =
-                        SubclassName::get_super_fn_name(&Namespace::new(), &analysis.rust_name);
+        }
+        apis.extend(results.into_iter());
+    }
 
-                    results.push(create_subclass_function(
-                        &sub,
-                        &analysis,
-                        &name,
-                        receiver_mutability,
-                        sup,
-                        if is_pure_virtual {
-                            None
-                        } else {
-                            Some(&super_fn_name)
-                        },
-                    ));
+    /// Analyze a given function, and any permutations of that function which
+    /// we might additionally generate (e.g. for subclasses.)
+    ///
+    /// Leaves the [`FnKind::Method::type_constructors`] at its default for [`add_constructors_present`]
+    /// to fill out.
+    fn analyze_foreign_fn_and_subclasses(
+        &mut self,
+        name: ApiName,
+        fun: Box<FuncToConvert>,
+    ) -> Result<Box<dyn Iterator<Item = Api<FnPrePhase1>>>, ConvertErrorWithContext> {
+        let (analysis, name) =
+            self.analyze_foreign_fn(name, &fun, TypeConversionSophistication::Regular, None);
+        let mut results = ApiVec::new();
 
-                    if !is_pure_virtual {
-                        let maybe_wrap = create_subclass_fn_wrapper(sub, &super_fn_name, &fun);
-                        let super_fn_name = ApiName::new_from_qualified_name(super_fn_name);
-                        self.analyze_and_add_if_necessary(super_fn_name, maybe_wrap, &mut results);
+        // Consider whether we need to synthesize subclass items.
+        if let FnKind::Method {
+            impl_for: sup,
+            method_kind:
+                MethodKind::Virtual(receiver_mutability) | MethodKind::PureVirtual(receiver_mutability),
+            ..
+        } = &analysis.kind
+        {
+            let (simpler_analysis, _) = self.analyze_foreign_fn(
+                name.clone(),
+                &fun,
+                TypeConversionSophistication::SimpleForSubclasses,
+                Some(analysis.rust_name.clone()),
+            );
+            for sub in self.subclasses_by_superclass(sup) {
+                // For each subclass, we need to create a plain-C++ method to call its superclass
+                // and a Rust/C++ bridge API to call _that_.
+                // What we're generating here is entirely about the subclass, so the
+                // superclass's namespace is irrelevant. We generate
+                // all subclasses in the root namespace.
+                let is_pure_virtual = matches!(
+                    &simpler_analysis.kind,
+                    FnKind::Method {
+                        method_kind: MethodKind::PureVirtual(..),
+                        ..
                     }
+                );
+
+                let super_fn_call_name =
+                    SubclassName::get_super_fn_name(&Namespace::new(), &analysis.rust_name);
+                let super_fn_api_name = SubclassName::get_super_fn_name(
+                    &Namespace::new(),
+                    &analysis.cxxbridge_name.to_string(),
+                );
+                let trait_api_name = SubclassName::get_trait_api_name(sup, &analysis.rust_name);
+
+                let mut subclass_fn_deps = vec![trait_api_name.clone()];
+                if !is_pure_virtual {
+                    // Create a C++ API representing the superclass implementation (allowing
+                    // calls from Rust->C++)
+                    let maybe_wrap = create_subclass_fn_wrapper(&sub, &super_fn_call_name, &fun);
+                    let super_fn_name = ApiName::new_from_qualified_name(super_fn_api_name);
+                    let super_fn_call_api_name = self.analyze_and_add(
+                        super_fn_name,
+                        maybe_wrap,
+                        &mut results,
+                        TypeConversionSophistication::SimpleForSubclasses,
+                    );
+                    subclass_fn_deps.push(super_fn_call_api_name);
+                }
+
+                // Create the Rust API representing the subclass implementation (allowing calls
+                // from C++ -> Rust)
+                results.push(create_subclass_function(
+                    // RustSubclassFn
+                    &sub,
+                    &simpler_analysis,
+                    &name,
+                    receiver_mutability,
+                    sup,
+                    subclass_fn_deps,
+                ));
+
+                // Create the trait item for the <superclass>_methods and <superclass>_supers
+                // traits. This is required per-superclass, not per-subclass, so don't
+                // create it if it already exists.
+                if !self
+                    .existing_superclass_trait_api_names
+                    .contains(&trait_api_name)
+                {
+                    self.existing_superclass_trait_api_names
+                        .insert(trait_api_name.clone());
+                    results.push(create_subclass_trait_item(
+                        ApiName::new_from_qualified_name(trait_api_name),
+                        &simpler_analysis,
+                        receiver_mutability,
+                        sup.clone(),
+                        is_pure_virtual,
+                    ));
                 }
             }
-            _ => {}
         }
 
         results.push(Api::Function {
             fun,
             analysis,
             name,
-            name_for_gc: None,
         });
 
         Ok(Box::new(results.into_iter()))
     }
 
-    fn analyze_and_add_if_necessary(
+    /// Adds an API, usually a synthesized API. Returns the final calculated API name, which can be used
+    /// for others to depend on this.
+    fn analyze_and_add<P: AnalysisPhase<FunAnalysis = FnAnalysis>>(
         &mut self,
         name: ApiName,
         new_func: Box<FuncToConvert>,
-        results: &mut Vec<Api<FnPhase>>,
-    ) {
-        let maybe_another_api = self.analyze_foreign_fn(name, &new_func);
-        if let Some((analysis, name)) = maybe_another_api {
-            results.push(Api::Function {
-                fun: new_func,
-                analysis,
-                name,
-                name_for_gc: None,
-            });
-        }
+        results: &mut ApiVec<P>,
+        sophistication: TypeConversionSophistication,
+    ) -> QualifiedName {
+        let (analysis, name) = self.analyze_foreign_fn(name, &new_func, sophistication, None);
+        results.push(Api::Function {
+            fun: new_func,
+            analysis,
+            name: name.clone(),
+        });
+        name.name
     }
 
     /// Take a constructor e.g. pub fn A_A(this: *mut root::A);
     /// and synthesize a make_unique e.g. pub fn make_unique() -> cxx::UniquePtr<A>
-    fn create_make_unique(&mut self, fun: &FuncToConvert) -> Box<FuncToConvert> {
+    fn create_make_unique(
+        &mut self,
+        fun: &FuncToConvert,
+        initial_name: ApiName,
+        results: &mut ApiVec<FnPrePhase2>,
+    ) {
         let mut new_fun = fun.clone();
-        new_fun.synthesis = Some(Synthesis::MakeUnique);
-        Box::new(new_fun)
+        new_fun.provenance = Provenance::SynthesizedMakeUnique;
+        let make_unique_func = Box::new(new_fun);
+        self.analyze_and_add(
+            initial_name,
+            make_unique_func,
+            results,
+            TypeConversionSophistication::Regular,
+        );
     }
 
     /// Determine how to materialize a function.
@@ -504,7 +693,9 @@
         &mut self,
         name: ApiName,
         fun: &FuncToConvert,
-    ) -> Option<(FnAnalysis, ApiName)> {
+        sophistication: TypeConversionSophistication,
+        predetermined_rust_name: Option<String>,
+    ) -> (FnAnalysis, ApiName) {
         let mut cpp_name = name.cpp_name_if_present().cloned();
         let ns = name.name.get_namespace();
 
@@ -529,6 +720,7 @@
                     &fun.references,
                     true,
                     None,
+                    sophistication,
                 )
             })
             .partition(Result::is_ok);
@@ -589,7 +781,7 @@
         // Part two, work out if this is a function, or method, or whatever.
         // First determine if this is actually a trait implementation.
         let trait_details = self.trait_creation_details_for_synthetic_function(
-            &fun.synthesis,
+            &fun.add_to_trait,
             ns,
             &ideal_rust_name,
             &self_ty,
@@ -597,16 +789,7 @@
         let (kind, error_context, rust_name) = if let Some(trait_details) = trait_details {
             trait_details
         } else if let Some(self_ty) = self_ty {
-            // Some kind of method.
-            if !self.is_on_allowlist(&self_ty) {
-                // Bindgen will output methods for types which have been encountered
-                // virally as arguments on other allowlisted types. But we don't want
-                // to generate methods unless the user has specifically asked us to.
-                // It may, for instance, be a private type.
-                return None;
-            }
-
-            // Method or static method.
+            // Some kind of method or static method.
             let type_ident = self_ty.get_final_item();
             // bindgen generates methods with the name:
             // {class}_{method name}
@@ -626,73 +809,106 @@
             ) {
                 let is_move =
                     matches!(fun.special_member, Some(SpecialMemberKind::MoveConstructor));
-                let (kind, method_name, trait_id) = if is_move {
-                    (
-                        TraitMethodKind::MoveConstructor,
-                        "move_new",
-                        quote! { MoveNew },
-                    )
-                } else {
-                    (
-                        TraitMethodKind::CopyConstructor,
-                        "copy_new",
-                        quote! { CopyNew },
-                    )
-                };
                 if let Some(constructor_suffix) = rust_name.strip_prefix(nested_type_ident) {
                     rust_name = format!("new{}", constructor_suffix);
                 }
-                rust_name = self.get_overload_name(ns, type_ident, rust_name);
+                rust_name = predetermined_rust_name
+                    .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
                 let error_context = error_context_for_method(&self_ty, &rust_name);
-                let impl_for_specifics = Type::Path(self_ty.to_type_path()).to_token_stream();
-                (
-                    FnKind::TraitMethod {
-                        kind,
-                        impl_for: self_ty,
-                        details: TraitMethodDetails {
-                            impl_for_specifics,
-                            trait_signature: quote! {
-                                autocxx::moveit::new:: #trait_id
-                            },
-                            trait_unsafety: Some(parse_quote! { unsafe }),
-                            avoid_self: true,
-                            method_name: make_ident(method_name),
-                            parameter_reordering: Some(vec![1, 0]),
-                            trait_call_is_unsafe: false,
+
+                // If this is 'None', then something weird is going on. We'll check for that
+                // later when we have enough context to generate useful errors.
+                let arg_is_reference = matches!(
+                    param_details
+                        .get(1)
+                        .map(|param| &param.conversion.unwrapped_type),
+                    Some(Type::Reference(_))
+                );
+                // Some exotic forms of copy constructor have const and/or volatile qualifiers.
+                // These are not sufficient to implement CopyNew, so we just treat them as regular
+                // constructors. We detect them by their argument being translated to Pin at this
+                // point.
+                if is_move || arg_is_reference {
+                    let (kind, method_name, trait_id) = if is_move {
+                        (
+                            TraitMethodKind::MoveConstructor,
+                            "move_new",
+                            quote! { MoveNew },
+                        )
+                    } else {
+                        (
+                            TraitMethodKind::CopyConstructor,
+                            "copy_new",
+                            quote! { CopyNew },
+                        )
+                    };
+                    let ty = Type::Path(self_ty.to_type_path());
+                    (
+                        FnKind::TraitMethod {
+                            kind,
+                            impl_for: self_ty,
+                            details: Box::new(TraitMethodDetails {
+                                trt: TraitImplSignature {
+                                    ty,
+                                    trait_signature: parse_quote! {
+                                        autocxx::moveit::new:: #trait_id
+                                    },
+                                    unsafety: Some(parse_quote! { unsafe }),
+                                },
+                                avoid_self: true,
+                                method_name: make_ident(method_name),
+                                parameter_reordering: Some(vec![1, 0]),
+                                trait_call_is_unsafe: false,
+                            }),
                         },
-                    },
-                    error_context,
-                    rust_name,
-                )
+                        error_context,
+                        rust_name,
+                    )
+                } else {
+                    (
+                        FnKind::Method {
+                            impl_for: self_ty,
+                            method_kind: MethodKind::Constructor { is_default: false },
+                        },
+                        error_context,
+                        rust_name,
+                    )
+                }
             } else if matches!(fun.special_member, Some(SpecialMemberKind::Destructor)) {
-                rust_name = self.get_overload_name(ns, type_ident, rust_name);
+                rust_name = predetermined_rust_name
+                    .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
                 let error_context = error_context_for_method(&self_ty, &rust_name);
-                let impl_for_specifics = Type::Path(self_ty.to_type_path()).to_token_stream();
+                let ty = Type::Path(self_ty.to_type_path());
                 (
                     FnKind::TraitMethod {
                         kind: TraitMethodKind::Destructor,
                         impl_for: self_ty,
-                        details: TraitMethodDetails {
-                            impl_for_specifics,
-                            trait_signature: quote! {
-                                Drop
+                        details: Box::new(TraitMethodDetails {
+                            trt: TraitImplSignature {
+                                ty,
+                                trait_signature: parse_quote! {
+                                    Drop
+                                },
+                                unsafety: None,
                             },
-                            trait_unsafety: None,
                             avoid_self: false,
                             method_name: make_ident("drop"),
                             parameter_reordering: None,
                             trait_call_is_unsafe: true,
-                        },
+                        }),
                     },
                     error_context,
                     rust_name,
                 )
             } else {
-                let method_kind = if matches!(fun.synthesis, Some(Synthesis::MakeUnique)) {
+                let method_kind = if matches!(fun.provenance, Provenance::SynthesizedMakeUnique) {
                     // We're re-running this routine for a function we already analyzed.
                     // Previously we made a placement "new" (MethodKind::Constructor).
                     // This time we've asked ourselves to synthesize a make_unique.
-                    let constructor_suffix = rust_name.strip_prefix(nested_type_ident).unwrap();
+                    let constructor_suffix = rust_name
+                        .strip_prefix(nested_type_ident)
+                        .or_else(|| rust_name.strip_prefix("new"))
+                        .unwrap();
                     rust_name = format!("make_unique{}", constructor_suffix);
                     // Strip off the 'this' arg.
                     params = params.into_iter().skip(1).collect();
@@ -711,7 +927,12 @@
                     // If there are multiple constructors, bindgen generates
                     // new, new1, new2 etc. and we'll keep those suffixes.
                     rust_name = format!("new{}", constructor_suffix);
-                    MethodKind::Constructor
+                    MethodKind::Constructor {
+                        is_default: matches!(
+                            fun.special_member,
+                            Some(SpecialMemberKind::DefaultConstructor)
+                        ),
+                    }
                 } else if is_static_method {
                     MethodKind::Static
                 } else {
@@ -724,10 +945,14 @@
                     }
                 };
                 // Disambiguate overloads.
-                let rust_name = self.get_overload_name(ns, type_ident, rust_name);
+                let rust_name = predetermined_rust_name
+                    .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
                 let error_context = error_context_for_method(&self_ty, &rust_name);
                 (
-                    FnKind::Method(self_ty, method_kind),
+                    FnKind::Method {
+                        impl_for: self_ty,
+                        method_kind,
+                    },
                     error_context,
                     rust_name,
                 )
@@ -738,7 +963,7 @@
             let rust_name = self.get_function_overload_name(ns, ideal_rust_name);
             (
                 FnKind::Function,
-                ErrorContext::Item(make_ident(&rust_name)),
+                ErrorContext::new_for_item(make_ident(&rust_name)),
                 rust_name,
             )
         };
@@ -758,7 +983,10 @@
         // pointer not a reference. For copy + move constructors, we also
         // enforce Rust-side conversions to comply with moveit traits.
         match kind {
-            FnKind::Method(_, MethodKind::Constructor) => {
+            FnKind::Method {
+                method_kind: MethodKind::Constructor { .. },
+                ..
+            } => {
                 self.reanalyze_parameter(
                     0,
                     fun,
@@ -767,6 +995,7 @@
                     &mut params,
                     &mut param_details,
                     None,
+                    sophistication,
                 )
                 .unwrap_or_else(&mut set_ignore_reason);
             }
@@ -783,6 +1012,7 @@
                     &mut params,
                     &mut param_details,
                     Some(RustConversionType::FromTypeToPtr),
+                    sophistication,
                 )
                 .unwrap_or_else(&mut set_ignore_reason);
             }
@@ -790,6 +1020,9 @@
                 kind: TraitMethodKind::CopyConstructor,
                 ..
             } => {
+                if param_details.len() < 2 {
+                    set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+                }
                 self.reanalyze_parameter(
                     0,
                     fun,
@@ -798,6 +1031,7 @@
                     &mut params,
                     &mut param_details,
                     Some(RustConversionType::FromPinMaybeUninitToPtr),
+                    sophistication,
                 )
                 .unwrap_or_else(&mut set_ignore_reason);
             }
@@ -806,6 +1040,9 @@
                 kind: TraitMethodKind::MoveConstructor,
                 ..
             } => {
+                if param_details.len() < 2 {
+                    set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+                }
                 self.reanalyze_parameter(
                     0,
                     fun,
@@ -814,6 +1051,7 @@
                     &mut params,
                     &mut param_details,
                     Some(RustConversionType::FromPinMaybeUninitToPtr),
+                    sophistication,
                 )
                 .unwrap_or_else(&mut set_ignore_reason);
                 self.reanalyze_parameter(
@@ -824,6 +1062,7 @@
                     &mut params,
                     &mut param_details,
                     Some(RustConversionType::FromPinMoveRefToPtr),
+                    sophistication,
                 )
                 .unwrap_or_else(&mut set_ignore_reason);
             }
@@ -874,11 +1113,29 @@
             set_ignore_reason(ConvertError::UnusedTemplateParam)
         } else {
             match kind {
-                FnKind::Method(_, MethodKind::Static) => {}
-                FnKind::Method(ref self_ty, _)
-                    if !known_types().is_cxx_acceptable_receiver(self_ty) =>
+                FnKind::Method {
+                    ref impl_for,
+                    method_kind:
+                        MethodKind::Constructor { .. }
+                        | MethodKind::MakeUnique
+                        | MethodKind::Normal(..)
+                        | MethodKind::PureVirtual(..)
+                        | MethodKind::Virtual(..),
+                    ..
+                } if !known_types().is_cxx_acceptable_receiver(impl_for) => {
+                    set_ignore_reason(ConvertError::UnsupportedReceiver);
+                }
+                FnKind::Method { ref impl_for, .. } if !self.is_on_allowlist(impl_for) => {
+                    // Bindgen will output methods for types which have been encountered
+                    // virally as arguments on other allowlisted types. But we don't want
+                    // to generate methods unless the user has specifically asked us to.
+                    // It may, for instance, be a private type.
+                    set_ignore_reason(ConvertError::MethodOfNonAllowlistedType);
+                }
+                FnKind::Method { ref impl_for, .. } | FnKind::TraitMethod { ref impl_for, .. }
+                    if self.is_generic_type(impl_for) =>
                 {
-                    set_ignore_reason(ConvertError::UnsupportedReceiver)
+                    set_ignore_reason(ConvertError::MethodOfGenericType);
                 }
                 _ => {}
             }
@@ -889,7 +1146,7 @@
         // namespace so we might need to prepend some stuff to make it unique.
         let cxxbridge_name = self.get_cxx_bridge_name(
             match kind {
-                FnKind::Method(ref self_ty, ..) => Some(self_ty.get_final_item()),
+                FnKind::Method { ref impl_for, .. } => Some(impl_for.get_final_item()),
                 FnKind::Function => None,
                 FnKind::TraitMethod { ref impl_for, .. } => Some(impl_for.get_final_item()),
             },
@@ -903,9 +1160,13 @@
 
         // Analyze the return type, just as we previously did for the
         // parameters.
-        let mut return_analysis = if let FnKind::Method(ref self_ty, MethodKind::MakeUnique) = kind
+        let mut return_analysis = if let FnKind::Method {
+            ref impl_for,
+            method_kind: MethodKind::MakeUnique,
+            ..
+        } = kind
         {
-            let constructed_type = self_ty.to_type_path();
+            let constructed_type = impl_for.to_type_path();
             ReturnTypeAnalysis {
                 rt: parse_quote! {
                     -> #constructed_type
@@ -914,7 +1175,7 @@
                     #constructed_type
                 })),
                 was_reference: false,
-                deps: std::iter::once(self_ty).cloned().collect(),
+                deps: std::iter::once(impl_for).cloned().collect(),
             }
         } else {
             self.convert_return_type(&fun.output, ns, &fun.references)
@@ -944,17 +1205,20 @@
         let effective_cpp_name = cpp_name.as_ref().unwrap_or(&rust_name);
         let cpp_name_incompatible_with_cxx =
             validate_ident_ok_for_rust(effective_cpp_name).is_err();
-        let synthetic_cpp_function_contents = synthesic_cpp_need(fun);
         // If possible, we'll put knowledge of the C++ API directly into the cxx::bridge
         // mod. However, there are various circumstances where cxx can't work with the existing
         // C++ API and we need to create a C++ wrapper function which is more cxx-compliant.
         // That wrapper function is included in the cxx::bridge, and calls through to the
         // original function.
         let wrapper_function_needed = match kind {
-            FnKind::Method(_, MethodKind::Static)
-            | FnKind::Method(_, MethodKind::Constructor)
-            | FnKind::Method(_, MethodKind::Virtual(_))
-            | FnKind::Method(_, MethodKind::PureVirtual(_))
+            FnKind::Method {
+                method_kind:
+                    MethodKind::Static
+                    | MethodKind::Constructor { .. }
+                    | MethodKind::Virtual(_)
+                    | MethodKind::PureVirtual(_),
+                ..
+            }
             | FnKind::TraitMethod {
                 kind:
                     TraitMethodKind::CopyConstructor
@@ -962,11 +1226,11 @@
                     | TraitMethodKind::Destructor,
                 ..
             } => true,
-            FnKind::Method(..) if cxxbridge_name != rust_name => true,
+            FnKind::Method { .. } if cxxbridge_name != rust_name => true,
             _ if param_conversion_needed => true,
             _ if ret_type_conversion_needed => true,
             _ if cpp_name_incompatible_with_cxx => true,
-            _ if synthetic_cpp_function_contents.is_some() => true,
+            _ if fun.synthetic_cpp.is_some() => true,
             _ => false,
         };
 
@@ -980,19 +1244,24 @@
                 "_"
             };
             cxxbridge_name = make_ident(&format!("{}{}autocxx_wrapper", cxxbridge_name, joiner));
-            let (payload, cpp_function_kind) = match synthetic_cpp_function_contents {
+            let (payload, cpp_function_kind) = match fun.synthetic_cpp.as_ref().cloned() {
                 Some((payload, cpp_function_kind)) => (payload, cpp_function_kind),
                 None => match kind {
-                    FnKind::Method(_, MethodKind::MakeUnique) => {
-                        (CppFunctionBody::MakeUnique, CppFunctionKind::Function)
+                    FnKind::Method {
+                        method_kind: MethodKind::MakeUnique,
+                        ..
+                    } => (CppFunctionBody::MakeUnique, CppFunctionKind::Function),
+                    FnKind::Method {
+                        ref impl_for,
+                        method_kind: MethodKind::Constructor { .. },
+                        ..
                     }
-                    FnKind::Method(ref self_ty, MethodKind::Constructor)
                     | FnKind::TraitMethod {
                         kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor,
-                        impl_for: ref self_ty,
+                        ref impl_for,
                         ..
                     } => (
-                        CppFunctionBody::PlacementNew(ns.clone(), self_ty.get_final_ident()),
+                        CppFunctionBody::PlacementNew(ns.clone(), impl_for.get_final_ident()),
                         CppFunctionKind::Constructor,
                     ),
                     FnKind::TraitMethod {
@@ -1003,15 +1272,19 @@
                         CppFunctionBody::Destructor(ns.clone(), impl_for.get_final_ident()),
                         CppFunctionKind::Function,
                     ),
-                    FnKind::Method(ref self_ty, MethodKind::Static) => (
+                    FnKind::Method {
+                        ref impl_for,
+                        method_kind: MethodKind::Static,
+                        ..
+                    } => (
                         CppFunctionBody::StaticMethodCall(
                             ns.clone(),
-                            self_ty.get_final_ident(),
+                            impl_for.get_final_ident(),
                             cpp_construction_ident,
                         ),
                         CppFunctionKind::Function,
                     ),
-                    FnKind::Method(..) => (
+                    FnKind::Method { .. } => (
                         CppFunctionBody::FunctionCall(ns.clone(), cpp_construction_ident),
                         CppFunctionKind::Method,
                     ),
@@ -1034,8 +1307,13 @@
             for pd in &param_details {
                 let type_name = pd.conversion.converted_rust_type();
                 let arg_name = if pd.self_type.is_some()
-                    && !matches!(kind, FnKind::Method(_, MethodKind::MakeUnique))
-                {
+                    && !matches!(
+                        kind,
+                        FnKind::Method {
+                            method_kind: MethodKind::MakeUnique,
+                            ..
+                        }
+                    ) {
                     parse_quote!(autocxx_gen_this)
                 } else {
                     pd.name.clone()
@@ -1064,35 +1342,30 @@
 
         let vis = fun.vis.clone();
 
+        let any_param_needs_rust_conversion = param_details
+            .iter()
+            .any(|pd| pd.conversion.rust_work_needed());
+
+        let rust_wrapper_needed = match kind {
+            FnKind::TraitMethod { .. } => true,
+            FnKind::Method { .. } => any_param_needs_rust_conversion || cxxbridge_name != rust_name,
+            _ => any_param_needs_rust_conversion,
+        };
+
         // Naming, part two.
         // Work out our final naming strategy.
         validate_ident_ok_for_cxx(&cxxbridge_name.to_string()).unwrap_or_else(set_ignore_reason);
         let rust_name_ident = make_ident(&rust_name);
-        let (id, rust_rename_strategy) = match kind {
-            FnKind::Method(..) | FnKind::TraitMethod { .. } => {
-                (rust_name_ident, RustRenameStrategy::None)
+        let rust_rename_strategy = match kind {
+            _ if rust_wrapper_needed => RustRenameStrategy::RenameUsingWrapperFunction,
+            FnKind::Function if cxxbridge_name != rust_name => {
+                RustRenameStrategy::RenameInOutputMod(rust_name_ident)
             }
-            FnKind::Function => {
-                // Keep the original Rust name the same so callers don't
-                // need to know about all of these shenanigans.
-                // There is a global space of rust_names even if they're in
-                // different namespaces.
-                let rust_name_ok = self.ok_to_use_rust_name(&rust_name);
-                if cxxbridge_name == rust_name {
-                    (rust_name_ident, RustRenameStrategy::None)
-                } else if rust_name_ok {
-                    (rust_name_ident, RustRenameStrategy::RenameUsingRustAttr)
-                } else {
-                    (
-                        cxxbridge_name.clone(),
-                        RustRenameStrategy::RenameInOutputMod(rust_name_ident),
-                    )
-                }
-            }
+            _ => RustRenameStrategy::None,
         };
 
         let analysis = FnAnalysis {
-            cxxbridge_name,
+            cxxbridge_name: cxxbridge_name.clone(),
             rust_name: rust_name.clone(),
             rust_rename_strategy,
             params,
@@ -1106,9 +1379,10 @@
             deps,
             ignore_reason,
             externally_callable,
+            rust_wrapper_needed,
         };
-        let name = ApiName::new_with_cpp_name(ns, id, cpp_name);
-        Some((analysis, name))
+        let name = ApiName::new_with_cpp_name(ns, cxxbridge_name, cpp_name);
+        (analysis, name)
     }
 
     /// Applies a specific `force_rust_conversion` to the parameter at index
@@ -1121,8 +1395,9 @@
         ns: &Namespace,
         rust_name: &str,
         params: &mut Punctuated<FnArg, Comma>,
-        param_details: &mut Vec<ArgumentAnalysis>,
+        param_details: &mut [ArgumentAnalysis],
         force_rust_conversion: Option<RustConversionType>,
+        sophistication: TypeConversionSophistication,
     ) -> Result<(), ConvertError> {
         self.convert_fn_arg(
             fun.inputs.iter().nth(param_idx).unwrap(),
@@ -1132,6 +1407,7 @@
             &fun.references,
             false,
             force_rust_conversion,
+            sophistication,
         )
         .map(|(new_arg, new_analysis)| {
             param_details[param_idx] = new_analysis;
@@ -1158,39 +1434,39 @@
     /// of a trait, rather than a function/method.
     fn trait_creation_details_for_synthetic_function(
         &mut self,
-        synthesis: &Option<Synthesis>,
+        synthesis: &Option<TraitSynthesis>,
         ns: &Namespace,
         ideal_rust_name: &str,
         self_ty: &Option<QualifiedName>,
     ) -> Option<(FnKind, ErrorContext, String)> {
         synthesis.as_ref().and_then(|synthesis| match synthesis {
-            Synthesis::Cast { to_type, mutable } => {
+            TraitSynthesis::Cast { to_type, mutable } => {
                 let rust_name = self.get_function_overload_name(ns, ideal_rust_name.to_string());
                 let from_type = self_ty.as_ref().unwrap();
                 let from_type_path = from_type.to_type_path();
                 let to_type = to_type.to_type_path();
-                let (trait_signature, impl_for_specifics, method_name) = match *mutable {
+                let (trait_signature, ty, method_name) = match *mutable {
                     CastMutability::ConstToConst => (
-                        quote! {
+                        parse_quote! {
                             AsRef < #to_type >
                         },
-                        from_type_path.to_token_stream(),
+                        Type::Path(from_type_path),
                         "as_ref",
                     ),
                     CastMutability::MutToConst => (
-                        quote! {
+                        parse_quote! {
                             AsRef < #to_type >
                         },
-                        quote! {
+                        parse_quote! {
                             &'a mut ::std::pin::Pin < &'a mut #from_type_path >
                         },
                         "as_ref",
                     ),
                     CastMutability::MutToMut => (
-                        quote! {
+                        parse_quote! {
                             autocxx::PinMut < #to_type >
                         },
-                        quote! {
+                        parse_quote! {
                             ::std::pin::Pin < &'a mut #from_type_path >
                         },
                         "pin_mut",
@@ -1201,24 +1477,68 @@
                     FnKind::TraitMethod {
                         kind: TraitMethodKind::Cast,
                         impl_for: from_type.clone(),
-                        details: TraitMethodDetails {
-                            impl_for_specifics,
-                            trait_signature,
-                            trait_unsafety: None,
+                        details: Box::new(TraitMethodDetails {
+                            trt: TraitImplSignature {
+                                ty,
+                                trait_signature,
+                                unsafety: None,
+                            },
                             avoid_self: false,
                             method_name,
                             parameter_reordering: None,
                             trait_call_is_unsafe: false,
-                        },
+                        }),
                     },
-                    ErrorContext::Item(make_ident(&rust_name)),
+                    ErrorContext::new_for_item(make_ident(&rust_name)),
                     rust_name,
                 ))
             }
-            _ => None,
+            TraitSynthesis::AllocUninitialized(ty) => self.generate_alloc_or_deallocate(
+                ideal_rust_name,
+                ty,
+                "allocate_uninitialized_cpp_storage",
+                TraitMethodKind::Alloc,
+            ),
+            TraitSynthesis::FreeUninitialized(ty) => self.generate_alloc_or_deallocate(
+                ideal_rust_name,
+                ty,
+                "free_uninitialized_cpp_storage",
+                TraitMethodKind::Dealloc,
+            ),
         })
     }
 
+    fn generate_alloc_or_deallocate(
+        &mut self,
+        ideal_rust_name: &str,
+        ty: &QualifiedName,
+        method_name: &str,
+        kind: TraitMethodKind,
+    ) -> Option<(FnKind, ErrorContext, String)> {
+        let rust_name =
+            self.get_function_overload_name(ty.get_namespace(), ideal_rust_name.to_string());
+        let typ = ty.to_type_path();
+        Some((
+            FnKind::TraitMethod {
+                impl_for: ty.clone(),
+                details: Box::new(TraitMethodDetails {
+                    trt: TraitImplSignature {
+                        ty: Type::Path(typ),
+                        trait_signature: parse_quote! { autocxx::moveit::MakeCppStorage },
+                        unsafety: Some(parse_quote! { unsafe }),
+                    },
+                    avoid_self: false,
+                    method_name: make_ident(method_name),
+                    parameter_reordering: None,
+                    trait_call_is_unsafe: false,
+                }),
+                kind,
+            },
+            ErrorContext::new_for_item(make_ident(&rust_name)),
+            rust_name,
+        ))
+    }
+
     fn get_function_overload_name(&mut self, ns: &Namespace, ideal_rust_name: String) -> String {
         let overload_tracker = self.overload_trackers_by_mod.entry(ns.clone()).or_default();
         overload_tracker.get_function_real_name(ideal_rust_name)
@@ -1241,6 +1561,7 @@
         references: &References,
         treat_this_as_reference: bool,
         force_rust_conversion: Option<RustConversionType>,
+        sophistication: TypeConversionSophistication,
     ) -> Result<(FnArg, ArgumentAnalysis), ConvertError> {
         Ok(match arg {
             FnArg::Typed(pt) => {
@@ -1312,9 +1633,18 @@
                     &subclass_holder.cloned(),
                     treat_as_rvalue_reference,
                     force_rust_conversion,
+                    sophistication,
                 );
                 pt.pat = Box::new(new_pat.clone());
                 pt.ty = new_ty;
+                let requires_unsafe =
+                    if matches!(annotated_type.kind, type_converter::TypeKind::Pointer) {
+                        UnsafetyNeeded::Always
+                    } else if conversion.bridge_unsafe_needed() {
+                        UnsafetyNeeded::JustBridge
+                    } else {
+                        UnsafetyNeeded::None
+                    };
                 (
                     FnArg::Typed(pt),
                     ArgumentAnalysis {
@@ -1327,10 +1657,7 @@
                                 | type_converter::TypeKind::MutableReference
                         ),
                         deps: annotated_type.types_encountered,
-                        requires_unsafe: matches!(
-                            annotated_type.kind,
-                            type_converter::TypeKind::Pointer
-                        ),
+                        requires_unsafe,
                     },
                 )
             }
@@ -1344,6 +1671,7 @@
         is_subclass_holder: &Option<Ident>,
         is_rvalue_ref: bool,
         force_rust_conversion: Option<RustConversionType>,
+        sophistication: TypeConversionSophistication,
     ) -> TypeConversionPolicy {
         if let Some(holder_id) = is_subclass_holder {
             let subclass = SubclassName::from_holder_name(holder_id);
@@ -1353,7 +1681,7 @@
                 };
                 TypeConversionPolicy {
                     unwrapped_type: ty,
-                    cpp_conversion: CppConversionType::None,
+                    cpp_conversion: CppConversionType::Move,
                     rust_conversion: RustConversionType::ToBoxedUpHolder(subclass),
                 }
             };
@@ -1363,7 +1691,15 @@
                 let ty = ty.clone();
                 let tn = QualifiedName::from_type_path(p);
                 if self.pod_safe_types.contains(&tn) {
-                    TypeConversionPolicy::new_unconverted(ty)
+                    if known_types().lacks_copy_constructor(&tn) {
+                        TypeConversionPolicy {
+                            unwrapped_type: ty,
+                            cpp_conversion: CppConversionType::Move,
+                            rust_conversion: RustConversionType::None,
+                        }
+                    } else {
+                        TypeConversionPolicy::new_unconverted(ty)
+                    }
                 } else if known_types().convertible_from_strs(&tn)
                     && !self.config.exclude_utilities()
                 {
@@ -1372,12 +1708,21 @@
                         cpp_conversion: CppConversionType::FromUniquePtrToValue,
                         rust_conversion: RustConversionType::FromStr,
                     }
-                } else {
+                } else if matches!(
+                    sophistication,
+                    TypeConversionSophistication::SimpleForSubclasses
+                ) {
                     TypeConversionPolicy {
                         unwrapped_type: ty,
                         cpp_conversion: CppConversionType::FromUniquePtrToValue,
                         rust_conversion: RustConversionType::None,
                     }
+                } else {
+                    TypeConversionPolicy {
+                        unwrapped_type: ty,
+                        cpp_conversion: CppConversionType::FromPtrToValue,
+                        rust_conversion: RustConversionType::FromValueParamToPtr,
+                    }
                 }
             }
             _ => {
@@ -1444,18 +1789,24 @@
     /// If a type has explicit constructors, bindgen will generate corresponding
     /// constructor functions, which we'll have already converted to make_unique methods.
     /// C++ mandates the synthesis of certain implicit constructors, to which we
-    /// need ro create bindings too. We do that here.
+    /// need to create bindings too. We do that here.
     /// It is tempting to make this a separate analysis phase, to be run later than
     /// the function analysis; but that would make the code much more complex as it
     /// would need to output a `FnAnalysisBody`. By running it as part of this phase
     /// we can simply generate the sort of thing bindgen generates, then ask
     /// the existing code in this phase to figure out what to do with it.
-    fn add_missing_constructors(&mut self, apis: &mut Vec<Api<FnPhase>>) {
-        if self.config.exclude_impls {
-            return;
-        }
-        let implicit_constructors_needed = find_missing_constructors(apis);
-        for (self_ty, implicit_constructors_needed) in implicit_constructors_needed {
+    ///
+    /// Also fills out the [`PodAndConstructorAnalysis::constructors`] fields with information useful
+    /// for further analysis phases.
+    fn add_constructors_present(&mut self, mut apis: ApiVec<FnPrePhase1>) -> ApiVec<FnPrePhase2> {
+        let all_items_found = find_constructors_present(&apis);
+        for (self_ty, items_found) in all_items_found.iter() {
+            if self.config.exclude_impls {
+                // Remember that `find_constructors_present` mutates `apis`, so we always have to
+                // call that, even if we don't do anything with the return value. This is kind of
+                // messy, see the comment on this function for why.
+                continue;
+            }
             if self
                 .config
                 .is_on_constructor_blocklist(&self_ty.to_cpp_name())
@@ -1463,21 +1814,21 @@
                 continue;
             }
             let path = self_ty.to_type_path();
-            if implicit_constructors_needed.default_constructor {
-                self.synthesize_constructor(
-                    self_ty.clone(),
+            if items_found.implicit_default_constructor_needed() {
+                self.synthesize_special_member(
+                    items_found,
                     None,
-                    apis,
+                    &mut apis,
                     SpecialMemberKind::DefaultConstructor,
                     parse_quote! { this: *mut #path },
                     References::default(),
                 );
             }
-            if implicit_constructors_needed.move_constructor {
-                self.synthesize_constructor(
-                    self_ty.clone(),
+            if items_found.implicit_move_constructor_needed() {
+                self.synthesize_special_member(
+                    items_found,
                     Some("move"),
-                    apis,
+                    &mut apis,
                     SpecialMemberKind::MoveConstructor,
                     parse_quote! { this: *mut #path, other: *mut #path },
                     References {
@@ -1486,15 +1837,11 @@
                     },
                 )
             }
-            // C++ synthesizes two different implicit copy constructors, but moveit
-            // supports only one, so we'll always synthesize that one.
-            if implicit_constructors_needed.copy_constructor_taking_const_t
-                || implicit_constructors_needed.copy_constructor_taking_t
-            {
-                self.synthesize_constructor(
-                    self_ty.clone(),
+            if items_found.implicit_copy_constructor_needed() {
+                self.synthesize_special_member(
+                    items_found,
                     Some("const_copy"),
-                    apis,
+                    &mut apis,
                     SpecialMemberKind::CopyConstructor,
                     parse_quote! { this: *mut #path, other: *const #path },
                     References {
@@ -1503,83 +1850,139 @@
                     },
                 )
             }
+            if items_found.implicit_destructor_needed() {
+                self.synthesize_special_member(
+                    items_found,
+                    None,
+                    &mut apis,
+                    SpecialMemberKind::Destructor,
+                    parse_quote! { this: *mut #path },
+                    References::default(),
+                );
+            }
         }
+
+        // Also, annotate each type with the constructors we found.
+        let mut results = ApiVec::new();
+        convert_apis(
+            apis,
+            &mut results,
+            Api::fun_unchanged,
+            |name, details, analysis| {
+                let items_found = all_items_found.get(&name.name);
+                Ok(Box::new(std::iter::once(Api::Struct {
+                    name,
+                    details,
+                    analysis: PodAndConstructorAnalysis {
+                        pod: analysis,
+                        constructors: if let Some(items_found) = items_found {
+                            PublicConstructors::from_items_found(items_found)
+                        } else {
+                            PublicConstructors::default()
+                        },
+                    },
+                })))
+            },
+            Api::enum_unchanged,
+            Api::typedef_unchanged,
+        );
+        results
     }
 
-    fn synthesize_constructor(
+    #[allow(clippy::too_many_arguments)] // it's true, but sticking with it for now
+    fn synthesize_special_member(
         &mut self,
-        self_ty: QualifiedName,
+        items_found: &ItemsFound,
         label: Option<&str>,
-        apis: &mut Vec<Api<FnPhase>>,
+        apis: &mut ApiVec<FnPrePhase1>,
         special_member: SpecialMemberKind,
         inputs: Punctuated<FnArg, Comma>,
         references: References,
     ) {
+        let self_ty = items_found.name.as_ref().unwrap();
         let ident = match label {
-            Some(label) => make_ident(format!(
+            Some(label) => make_ident(self.config.uniquify_name_per_mod(&format!(
                 "{}_synthetic_{}_ctor",
-                self_ty.get_final_item(),
+                self_ty.name.get_final_item(),
                 label
-            )),
-            None => self_ty.get_final_ident(),
+            ))),
+            None => self_ty.name.get_final_ident(),
         };
-        let fake_api_name = ApiName::new(self_ty.get_namespace(), ident.clone());
+        let cpp_name = if matches!(special_member, SpecialMemberKind::DefaultConstructor) {
+            // Constructors (other than move or copy) are identified in `analyze_foreign_fn` by
+            // being suffixed with the cpp_name, so we have to produce that.
+            self.nested_type_name_map
+                .get(&self_ty.name)
+                .cloned()
+                .or_else(|| Some(self_ty.name.get_final_item().to_string()))
+        } else {
+            None
+        };
+        let fake_api_name =
+            ApiName::new_with_cpp_name(self_ty.name.get_namespace(), ident.clone(), cpp_name);
+        let self_ty = &self_ty.name;
         let ns = self_ty.get_namespace().clone();
-        let items = report_any_error(&ns, apis, || {
-            self.analyze_foreign_fn_and_subclasses(
-                fake_api_name,
-                Box::new(FuncToConvert {
-                    self_ty: Some(self_ty),
-                    ident,
-                    doc_attr: None,
-                    inputs,
-                    output: ReturnType::Default,
-                    vis: parse_quote! { pub },
-                    virtualness: Virtualness::None,
-                    cpp_vis: CppVisibility::Public,
-                    special_member: Some(special_member),
-                    unused_template_param: false,
-                    references,
-                    original_name: None,
-                    synthesized_this_type: None,
-                    synthesis: None,
-                    is_deleted: false,
-                }),
-            )
-        });
-        apis.extend(items.into_iter().flatten());
+        let mut any_errors = ApiVec::new();
+        apis.extend(
+            report_any_error(&ns, &mut any_errors, || {
+                self.analyze_foreign_fn_and_subclasses(
+                    fake_api_name,
+                    Box::new(FuncToConvert {
+                        self_ty: Some(self_ty.clone()),
+                        ident,
+                        doc_attr: None,
+                        inputs,
+                        output: ReturnType::Default,
+                        vis: parse_quote! { pub },
+                        virtualness: Virtualness::None,
+                        cpp_vis: CppVisibility::Public,
+                        special_member: Some(special_member),
+                        unused_template_param: false,
+                        references,
+                        original_name: None,
+                        synthesized_this_type: None,
+                        is_deleted: false,
+                        add_to_trait: None,
+                        synthetic_cpp: None,
+                        provenance: Provenance::SynthesizedOther,
+                    }),
+                )
+            })
+            .into_iter()
+            .flatten(),
+        );
+        apis.append(&mut any_errors);
     }
 }
 
 fn error_context_for_method(self_ty: &QualifiedName, rust_name: &str) -> ErrorContext {
-    ErrorContext::Method {
-        self_ty: self_ty.get_final_ident(),
-        method: make_ident(rust_name),
-    }
-}
-
-fn synthesic_cpp_need(fun: &FuncToConvert) -> Option<(CppFunctionBody, CppFunctionKind)> {
-    match fun.synthesis {
-        Some(Synthesis::Cast { .. }) => Some((CppFunctionBody::Cast, CppFunctionKind::Function)),
-        _ => None,
-    }
+    ErrorContext::new_for_method(self_ty.get_final_ident(), make_ident(rust_name))
 }
 
 impl Api<FnPhase> {
-    pub(crate) fn typename_for_allowlist(&self) -> QualifiedName {
+    pub(crate) fn name_for_allowlist(&self) -> QualifiedName {
         match &self {
-            Api::Function {
-                name_for_gc: Some(name),
-                ..
-            } => name.clone(),
             Api::Function { analysis, .. } => match analysis.kind {
-                FnKind::Method(ref self_ty, _) => self_ty.clone(),
+                FnKind::Method { ref impl_for, .. } => impl_for.clone(),
                 FnKind::TraitMethod { ref impl_for, .. } => impl_for.clone(),
                 FnKind::Function => {
                     QualifiedName::new(self.name().get_namespace(), make_ident(&analysis.rust_name))
                 }
             },
             Api::RustSubclassFn { subclass, .. } => subclass.0.name.clone(),
+            Api::IgnoredItem {
+                name,
+                ctx: Some(ctx),
+                ..
+            } => match ctx.get_type() {
+                ErrorContextType::Method { self_ty, .. } => {
+                    QualifiedName::new(name.name.get_namespace(), self_ty.clone())
+                }
+                ErrorContextType::Item(id) => {
+                    QualifiedName::new(name.name.get_namespace(), id.clone())
+                }
+                _ => name.name.clone(),
+            },
             _ => self.name().clone(),
         }
     }
@@ -1605,6 +2008,16 @@
                 | Api::CType { .. }
                 | Api::RustSubclassFn { .. }
                 | Api::Subclass { .. }
+                | Api::Struct {
+                    analysis: PodAndDepAnalysis {
+                        pod: PodAnalysis {
+                            kind: TypeKind::Pod,
+                            ..
+                        },
+                        ..
+                    },
+                    ..
+                }
         )
     }
 
@@ -1618,35 +2031,4 @@
             _ => Some(self.name().get_final_ident()),
         }
     }
-
-    /// Any dependencies on other APIs which this API has.
-    pub(crate) fn deps(&self) -> Box<dyn Iterator<Item = QualifiedName> + '_> {
-        match self {
-            Api::Typedef {
-                old_tyname,
-                analysis: TypedefAnalysis { deps, .. },
-                ..
-            } => Box::new(old_tyname.iter().chain(deps.iter()).cloned()),
-            Api::Struct {
-                analysis:
-                    PodAnalysis {
-                        kind: TypeKind::Pod,
-                        field_types,
-                        ..
-                    },
-                ..
-            } => Box::new(field_types.iter().cloned()),
-            Api::Function { analysis, .. } => Box::new(analysis.deps.iter().cloned()),
-            Api::Subclass {
-                name: _,
-                superclass,
-            } => Box::new(std::iter::once(superclass.clone())),
-            Api::RustSubclassFn { details, .. } => Box::new(details.dependency.iter().cloned()),
-            _ => Box::new(std::iter::empty()),
-        }
-    }
-
-    pub(crate) fn format_deps(&self) -> String {
-        self.deps().join(",")
-    }
 }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/overload_tracker.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/overload_tracker.rs
similarity index 81%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/overload_tracker.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/overload_tracker.rs
index 46f4d2f..6fc532c 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/overload_tracker.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/overload_tracker.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashMap;
 
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/subclass.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/subclass.rs
similarity index 61%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/subclass.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/subclass.rs
index 1d84851e..3bde56a 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/fun/subclass.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/fun/subclass.rs
@@ -1,16 +1,10 @@
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashMap;
 
@@ -19,8 +13,10 @@
 use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability};
 use crate::conversion::analysis::pod::PodPhase;
 use crate::conversion::api::{
-    CppVisibility, FuncToConvert, RustSubclassFnDetails, SubclassName, Synthesis, Virtualness,
+    CppVisibility, FuncToConvert, Provenance, RustSubclassFnDetails, SubclassConstructorDetails,
+    SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness,
 };
+use crate::conversion::apivec::ApiVec;
 use crate::{
     conversion::{
         analysis::fun::function_wrapper::{
@@ -31,10 +27,10 @@
     types::{make_ident, Namespace, QualifiedName},
 };
 
-use super::{FnAnalysis, FnPhase};
+use super::{FnAnalysis, FnPrePhase1};
 
 pub(super) fn subclasses_by_superclass(
-    apis: &[Api<PodPhase>],
+    apis: &ApiVec<PodPhase>,
 ) -> HashMap<QualifiedName, Vec<SubclassName>> {
     let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new();
 
@@ -50,7 +46,7 @@
 }
 
 pub(super) fn create_subclass_fn_wrapper(
-    sub: SubclassName,
+    sub: &SubclassName,
     super_fn_name: &QualifiedName,
     fun: &FuncToConvert,
 ) -> Box<FuncToConvert> {
@@ -69,19 +65,48 @@
         unused_template_param: fun.unused_template_param,
         original_name: None,
         references: fun.references.clone(),
-        synthesis: fun.synthesis.clone(),
+        add_to_trait: fun.add_to_trait.clone(),
         is_deleted: fun.is_deleted,
+        synthetic_cpp: None,
+        provenance: Provenance::SynthesizedOther,
     })
 }
 
+pub(super) fn create_subclass_trait_item(
+    name: ApiName,
+    analysis: &FnAnalysis,
+    receiver_mutability: &ReceiverMutability,
+    receiver: QualifiedName,
+    is_pure_virtual: bool,
+) -> Api<FnPrePhase1> {
+    let param_names = analysis
+        .param_details
+        .iter()
+        .map(|pd| pd.name.clone())
+        .collect();
+    Api::SubclassTraitItem {
+        name,
+        details: SuperclassMethod {
+            name: make_ident(&analysis.rust_name),
+            params: analysis.params.clone(),
+            ret_type: analysis.ret_type.clone(),
+            param_names,
+            receiver_mutability: receiver_mutability.clone(),
+            requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false),
+            is_pure_virtual,
+            receiver,
+        },
+    }
+}
+
 pub(super) fn create_subclass_function(
     sub: &SubclassName,
     analysis: &super::FnAnalysis,
     name: &ApiName,
     receiver_mutability: &ReceiverMutability,
     superclass: &QualifiedName,
-    dependency: Option<&QualifiedName>,
-) -> Api<FnPhase> {
+    dependencies: Vec<QualifiedName>,
+) -> Api<FnPrePhase1> {
     let cpp = sub.cpp();
     let holder_name = sub.holder();
     let rust_call_name = make_ident(format!(
@@ -99,7 +124,13 @@
     } else {
         CppFunctionKind::ConstMethod
     };
-    let subclass_function: Api<FnPhase> = Api::RustSubclassFn {
+    let argument_conversion = analysis
+        .param_details
+        .iter()
+        .skip(1)
+        .map(|p| p.conversion.clone())
+        .collect();
+    Api::RustSubclassFn {
         name: ApiName::new_in_root_namespace(rust_call_name.clone()),
         subclass: sub.clone(),
         details: Box::new(RustSubclassFnDetails {
@@ -108,30 +139,27 @@
             method_name: make_ident(&analysis.rust_name),
             cpp_impl: CppFunction {
                 payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name),
-                wrapper_function_name: name.name.get_final_ident(),
+                wrapper_function_name: make_ident(&analysis.rust_name),
                 original_cpp_name: name.cpp_name(),
                 return_conversion: analysis.ret_conversion.clone(),
-                argument_conversion: analysis
-                    .param_details
-                    .iter()
-                    .skip(1)
-                    .map(|p| p.conversion.clone())
-                    .collect(),
+                argument_conversion,
                 kind,
                 pass_obs_field: true,
                 qualification: Some(cpp),
             },
             superclass: superclass.clone(),
             receiver_mutability: receiver_mutability.clone(),
-            dependency: dependency.cloned(),
-            requires_unsafe: analysis.param_details.iter().any(|pd| pd.requires_unsafe),
+            dependencies,
+            requires_unsafe: UnsafetyNeeded::from_param_details(&analysis.param_details, false),
             is_pure_virtual: matches!(
                 analysis.kind,
-                FnKind::Method(_, MethodKind::PureVirtual(..))
+                FnKind::Method {
+                    method_kind: MethodKind::PureVirtual(..),
+                    ..
+                }
             ),
         }),
-    };
-    subclass_function
+    }
 }
 
 pub(super) fn create_subclass_constructor(
@@ -142,36 +170,33 @@
 ) -> (Box<FuncToConvert>, ApiName) {
     let holder = sub.holder();
     let cpp = sub.cpp();
-    let synthesis = Some({
-        let wrapper_function_name = cpp.get_final_ident();
-        let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! {
-            rust::Box< #holder >
-        });
-        let args = std::iter::once(initial_arg).chain(
-            analysis
-                .param_details
-                .iter()
-                .skip(1) // skip placement new destination
-                .map(|aa| aa.conversion.clone()),
-        );
-        let cpp_impl = CppFunction {
-            payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()),
-            wrapper_function_name,
-            return_conversion: None,
-            argument_conversion: args.collect(),
-            kind: CppFunctionKind::SynthesizedConstructor,
-            pass_obs_field: false,
-            qualification: Some(cpp.clone()),
-            original_cpp_name: cpp.to_cpp_name(),
-        };
-        Synthesis::SubclassConstructor {
-            subclass: sub.clone(),
-            cpp_impl: Box::new(cpp_impl),
-            is_trivial: analysis.param_details.len() == 1, // just placement new
-                                                           // destination, no other parameters
-        }
+    let wrapper_function_name = cpp.get_final_ident();
+    let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! {
+        rust::Box< #holder >
     });
-
+    let args = std::iter::once(initial_arg).chain(
+        analysis
+            .param_details
+            .iter()
+            .skip(1) // skip placement new destination
+            .map(|aa| aa.conversion.clone()),
+    );
+    let cpp_impl = CppFunction {
+        payload: CppFunctionBody::ConstructSuperclass(sup.to_cpp_name()),
+        wrapper_function_name,
+        return_conversion: None,
+        argument_conversion: args.collect(),
+        kind: CppFunctionKind::SynthesizedConstructor,
+        pass_obs_field: false,
+        qualification: Some(cpp.clone()),
+        original_cpp_name: cpp.to_cpp_name(),
+    };
+    let subclass_constructor_details = Box::new(SubclassConstructorDetails {
+        subclass: sub.clone(),
+        is_trivial: analysis.param_details.len() == 1, // just placement new
+        // destination, no other parameters
+        cpp_impl,
+    });
     let subclass_constructor_name =
         make_ident(format!("{}_{}", cpp.get_final_item(), cpp.get_final_item()));
     let mut existing_params = fun.inputs.clone();
@@ -208,8 +233,10 @@
         references: fun.references.clone(),
         synthesized_this_type: Some(cpp.clone()),
         self_ty: Some(cpp),
-        synthesis,
+        add_to_trait: None,
         is_deleted: fun.is_deleted,
+        synthetic_cpp: None,
+        provenance: Provenance::SynthesizedSubclassConstructor(subclass_constructor_details),
     });
     let subclass_constructor_name = ApiName::new_with_cpp_name(
         &Namespace::new(),
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/gc.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/gc.rs
similarity index 68%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/gc.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/gc.rs
index dc1e807..0393459 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/gc.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/gc.rs
@@ -1,24 +1,21 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::{HashMap, HashSet};
 
 use autocxx_parser::IncludeCppConfig;
 
-use crate::{conversion::api::Api, types::QualifiedName};
+use crate::{
+    conversion::{api::Api, apivec::ApiVec},
+    types::QualifiedName,
+};
 
-use super::fun::FnPhase;
+use super::{deps::HasDependencies, fun::FnPhase};
 
 /// This is essentially mark-and-sweep garbage collection of the
 /// [Api]s that we've discovered. Why do we do this, you might wonder?
@@ -38,32 +35,32 @@
 ///    don't care about the other parameter types passed into those
 ///    APIs either.
 pub(crate) fn filter_apis_by_following_edges_from_allowlist(
-    mut apis: Vec<Api<FnPhase>>,
+    apis: ApiVec<FnPhase>,
     config: &IncludeCppConfig,
-) -> Vec<Api<FnPhase>> {
+) -> ApiVec<FnPhase> {
     let mut todos: Vec<QualifiedName> = apis
         .iter()
         .filter(|api| {
-            let tnforal = api.typename_for_allowlist();
+            let tnforal = api.name_for_allowlist();
             config.is_on_allowlist(&tnforal.to_cpp_name())
         })
         .map(Api::name)
         .cloned()
         .collect();
-    let mut by_typename: HashMap<QualifiedName, Vec<Api<FnPhase>>> = HashMap::new();
-    for api in apis.drain(..) {
+    let mut by_typename: HashMap<QualifiedName, ApiVec<FnPhase>> = HashMap::new();
+    for api in apis.into_iter() {
         let tn = api.name().clone();
         by_typename.entry(tn).or_default().push(api);
     }
     let mut done = HashSet::new();
-    let mut output = Vec::new();
+    let mut output = ApiVec::new();
     while !todos.is_empty() {
         let todo = todos.remove(0);
         if done.contains(&todo) {
             continue;
         }
         if let Some(mut these_apis) = by_typename.remove(&todo) {
-            todos.extend(these_apis.iter().flat_map(|api| api.deps()));
+            todos.extend(these_apis.iter().flat_map(|api| api.deps().cloned()));
             output.append(&mut these_apis);
         } // otherwise, probably an intrinsic e.g. uint32_t.
         done.insert(todo);
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/mod.rs
new file mode 100644
index 0000000..a4abe099
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/mod.rs
@@ -0,0 +1,24 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub(crate) mod abstract_types;
+pub(crate) mod allocators;
+pub(crate) mod casts;
+pub(crate) mod constructor_deps;
+pub(crate) mod ctypes;
+pub(crate) mod deps;
+mod depth_first;
+pub(crate) mod fun;
+pub(crate) mod gc;
+mod name_check;
+pub(crate) mod pod; // hey, that rhymes
+pub(crate) mod remove_ignored;
+pub(crate) mod tdef;
+mod type_converter;
+
+pub(crate) use name_check::check_names;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/name_check.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/name_check.rs
similarity index 83%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/name_check.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/name_check.rs
index 85ef8a9..0167438 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/name_check.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/name_check.rs
@@ -1,16 +1,10 @@
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashMap;
 
@@ -19,6 +13,7 @@
 use crate::{
     conversion::{
         api::{Api, SubclassName},
+        apivec::ApiVec,
         error_reporter::convert_item_apis,
         ConvertError,
     },
@@ -29,14 +24,14 @@
 
 /// Do some final checks that the names we've come up with can be represented
 /// within cxx.
-pub(crate) fn check_names(apis: Vec<Api<FnPhase>>) -> Vec<Api<FnPhase>> {
+pub(crate) fn check_names(apis: ApiVec<FnPhase>) -> ApiVec<FnPhase> {
     // If any items have names which can't be represented by cxx,
     // abort. This check should ideally be done at the times we fill in the
     // `name` field of each `api` in the first place, at parse time, though
     // as the `name` field of each API may change during various analysis phases,
     // currently it seems better to do it here to ensure we respect
     // the output of any such changes.
-    let mut intermediate = Vec::new();
+    let mut intermediate = ApiVec::new();
     convert_item_apis(apis, &mut intermediate, |api| match api {
         Api::Typedef { ref name, .. }
         | Api::ForwardDeclaration { ref name, .. }
@@ -75,20 +70,21 @@
         | Api::RustType { .. }
         | Api::RustSubclassFn { .. }
         | Api::RustFn { .. }
+        | Api::SubclassTraitItem { .. }
         | Api::IgnoredItem { .. } => Ok(Box::new(std::iter::once(api))),
     });
 
     // Reject any names which are duplicates within the cxx bridge mod,
     // that has a flat namespace.
     let mut names_found: HashMap<Ident, usize> = HashMap::new();
-    for api in &intermediate {
+    for api in intermediate.iter() {
         let my_name = api.cxxbridge_name();
         if let Some(name) = my_name {
             let e = names_found.entry(name).or_default();
             *e += 1usize;
         }
     }
-    let mut results = Vec::new();
+    let mut results = ApiVec::new();
     convert_item_apis(intermediate, &mut results, |api| {
         let my_name = api.cxxbridge_name();
         if let Some(name) = my_name {
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/byvalue_checker.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/byvalue_checker.rs
similarity index 94%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/byvalue_checker.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/byvalue_checker.rs
index f1499cf..b6d1c6d 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/byvalue_checker.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/byvalue_checker.rs
@@ -1,17 +1,12 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
+use crate::conversion::apivec::ApiVec;
 use crate::{conversion::ConvertError, known_types::known_types};
 use crate::{
     conversion::{
@@ -75,7 +70,7 @@
     /// Scan APIs to work out which are by-value safe. Constructs a [ByValueChecker]
     /// that others can use to query the results.
     pub(crate) fn new_from_apis(
-        apis: &[Api<TypedefPhase>],
+        apis: &ApiVec<TypedefPhase>,
         config: &IncludeCppConfig,
     ) -> Result<ByValueChecker, ConvertError> {
         let mut byvalue_checker = ByValueChecker::new();
@@ -86,8 +81,8 @@
                 .results
                 .insert(tn, StructDetails::new(safety));
         }
-        for api in apis {
-            match &api {
+        for api in apis.iter() {
+            match api {
                 Api::Typedef { analysis, .. } => {
                     let name = api.name();
                     let typedef_type = match analysis.kind {
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/mod.rs
similarity index 62%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/mod.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/mod.rs
index 735e9c3..4b5cd72 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/pod/mod.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/pod/mod.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 mod byvalue_checker;
 
@@ -18,15 +12,13 @@
 
 use autocxx_parser::IncludeCppConfig;
 use byvalue_checker::ByValueChecker;
-use syn::{FnArg, ItemEnum, ItemStruct, Type, TypePtr, Visibility};
+use syn::{ItemEnum, ItemStruct, Type, Visibility};
 
 use crate::{
     conversion::{
-        analysis::type_converter::{add_analysis, TypeConversionContext, TypeConverter},
-        api::{
-            AnalysisPhase, Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind,
-            StructDetails, TypeKind, UnanalyzedApi,
-        },
+        analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
+        api::{AnalysisPhase, Api, ApiName, CppVisibility, NullPhase, StructDetails, TypeKind},
+        apivec::ApiVec,
         convert_error::{ConvertErrorWithContext, ErrorContext},
         error_reporter::convert_apis,
         parse::BindgenSemanticAttributes,
@@ -37,6 +29,11 @@
 
 use super::tdef::{TypedefAnalysis, TypedefPhase};
 
+pub(crate) struct FieldInfo {
+    pub(crate) ty: Type,
+    pub(crate) type_kind: type_converter::TypeKind,
+}
+
 pub(crate) struct PodAnalysis {
     pub(crate) kind: TypeKind,
     pub(crate) bases: HashSet<QualifiedName>,
@@ -45,8 +42,9 @@
     /// because otherwise we don't know whether they're
     /// abstract or not.
     pub(crate) castable_bases: HashSet<QualifiedName>,
-    pub(crate) field_types: HashSet<QualifiedName>,
-    pub(crate) movable: bool,
+    pub(crate) field_deps: HashSet<QualifiedName>,
+    pub(crate) field_info: Vec<FieldInfo>,
+    pub(crate) is_generic: bool,
 }
 
 pub(crate) struct PodPhase;
@@ -63,19 +61,17 @@
 /// and an object which can be used to query the POD status of any
 /// type whether or not it's one of the [Api]s.
 pub(crate) fn analyze_pod_apis(
-    apis: Vec<Api<TypedefPhase>>,
+    apis: ApiVec<TypedefPhase>,
     config: &IncludeCppConfig,
-) -> Result<Vec<Api<PodPhase>>, ConvertError> {
+) -> Result<ApiVec<PodPhase>, ConvertError> {
     // This next line will return an error if any of the 'generate_pod'
     // directives from the user can't be met because, for instance,
     // a type contains a std::string or some other type which can't be
     // held safely by value in Rust.
     let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
-    // We'll also note which types have deleted move constructors.
-    let deleted_move_constructors = find_deleted_move_and_copy_constructors(&apis);
-    let mut extra_apis = Vec::new();
+    let mut extra_apis = ApiVec::new();
     let mut type_converter = TypeConverter::new(config, &apis);
-    let mut results = Vec::new();
+    let mut results = ApiVec::new();
     convert_apis(
         apis,
         &mut results,
@@ -88,7 +84,6 @@
                 name,
                 details,
                 config,
-                &deleted_move_constructors,
             )
         },
         analyze_enum,
@@ -96,8 +91,8 @@
     );
     // Conceivably, the process of POD-analysing the first set of APIs could result
     // in us creating new APIs to concretize generic types.
-    let extra_apis: Vec<Api<PodPhase>> = extra_apis.into_iter().map(add_analysis).collect();
-    let mut more_extra_apis = Vec::new();
+    let extra_apis: ApiVec<PodPhase> = extra_apis.into_iter().map(add_analysis).collect();
+    let mut more_extra_apis = ApiVec::new();
     convert_apis(
         extra_apis,
         &mut results,
@@ -110,7 +105,6 @@
                 name,
                 details,
                 config,
-                &deleted_move_constructors,
             )
         },
         analyze_enum,
@@ -132,29 +126,29 @@
 fn analyze_struct(
     byvalue_checker: &ByValueChecker,
     type_converter: &mut TypeConverter,
-    extra_apis: &mut Vec<UnanalyzedApi>,
+    extra_apis: &mut ApiVec<NullPhase>,
     name: ApiName,
     mut details: Box<StructDetails>,
     config: &IncludeCppConfig,
-    deleted_move_constructors: &HashSet<QualifiedName>,
 ) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
-    let movable = !deleted_move_constructors.contains(&name.name);
     let id = name.name.get_final_ident();
     if details.vis != CppVisibility::Public {
         return Err(ConvertErrorWithContext(
             ConvertError::NonPublicNestedType,
-            Some(ErrorContext::Item(id)),
+            Some(ErrorContext::new_for_item(id)),
         ));
     }
     let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
     metadata.check_for_fatal_attrs(&id)?;
     let bases = get_bases(&details.item);
-    let mut field_types = HashSet::new();
+    let mut field_deps = HashSet::new();
+    let mut field_info = Vec::new();
     let field_conversion_errors = get_struct_field_types(
         type_converter,
         name.name.get_namespace(),
         &details.item,
-        &mut field_types,
+        &mut field_deps,
+        &mut field_info,
         extra_apis,
     );
     let type_kind = if byvalue_checker.is_pod(&name.name) {
@@ -163,11 +157,14 @@
         if details.has_rvalue_reference_fields {
             return Err(ConvertErrorWithContext(
                 ConvertError::RValueReferenceField,
-                Some(ErrorContext::Item(id)),
+                Some(ErrorContext::new_for_item(id)),
             ));
         }
         if let Some(err) = field_conversion_errors.into_iter().next() {
-            return Err(ConvertErrorWithContext(err, Some(ErrorContext::Item(id))));
+            return Err(ConvertErrorWithContext(
+                err,
+                Some(ErrorContext::new_for_item(id)),
+            ));
         }
         TypeKind::Pod
     } else {
@@ -180,6 +177,7 @@
         .filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
         .cloned()
         .collect();
+    let is_generic = !details.item.generics.params.is_empty();
     Ok(Box::new(std::iter::once(Api::Struct {
         name,
         details,
@@ -187,8 +185,9 @@
             kind: type_kind,
             bases: bases.into_keys().collect(),
             castable_bases,
-            field_types,
-            movable,
+            field_deps,
+            field_info,
+            is_generic,
         },
     })))
 }
@@ -197,17 +196,31 @@
     type_converter: &mut TypeConverter,
     ns: &Namespace,
     s: &ItemStruct,
-    deps: &mut HashSet<QualifiedName>,
-    extra_apis: &mut Vec<UnanalyzedApi>,
+    field_deps: &mut HashSet<QualifiedName>,
+    field_info: &mut Vec<FieldInfo>,
+    extra_apis: &mut ApiVec<NullPhase>,
 ) -> Vec<ConvertError> {
     let mut convert_errors = Vec::new();
     for f in &s.fields {
         let annotated =
             type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::CxxInnerType);
         match annotated {
-            Ok(r) => {
-                extra_apis.extend(r.extra_apis);
-                deps.extend(r.types_encountered);
+            Ok(mut r) => {
+                extra_apis.append(&mut r.extra_apis);
+                // Skip base classes represented as fields. Anything which wants to include bases can chain
+                // those to the list we're building.
+                if !f
+                    .ident
+                    .as_ref()
+                    .map(|id| id.to_string().starts_with("_base"))
+                    .unwrap_or(false)
+                {
+                    field_deps.extend(r.types_encountered);
+                    field_info.push(FieldInfo {
+                        ty: r.ty,
+                        type_kind: r.kind,
+                    });
+                }
             }
             Err(e) => convert_errors.push(e),
         };
@@ -232,40 +245,3 @@
         })
         .collect()
 }
-
-fn find_deleted_move_and_copy_constructors(apis: &[Api<TypedefPhase>]) -> HashSet<QualifiedName> {
-    // Remove any deleted move + copy constructors from the API list and list the types
-    // that they construct.
-    apis.iter().filter_map(|api| match api {
-        Api::Function { ref fun, .. } => match &**fun {
-            FuncToConvert {
-                special_member:
-                    Some(SpecialMemberKind::MoveConstructor | SpecialMemberKind::CopyConstructor),
-                is_deleted: true,
-                inputs,
-                ..
-             } => match is_a_pointer_arg(inputs.iter().next()) {
-                    Some(ty) => Some(ty),
-                    _ => panic!("found special constructor member with something other than a pointer first arg"),
-                },
-            _ => None
-        },
-        _ => None,
-    }).collect()
-}
-
-/// Determine if a function argument is a pointer, and if so, to what.
-/// It's unfortunate that we need to do this during the POD analysis but
-/// for now, it's the best way to identify special constructor members.
-fn is_a_pointer_arg(arg: Option<&FnArg>) -> Option<QualifiedName> {
-    arg.and_then(|arg| match arg {
-        FnArg::Receiver(..) => None,
-        FnArg::Typed(pt) => match &*pt.ty {
-            Type::Ptr(TypePtr { elem, .. }) => match &**elem {
-                Type::Path(typ) => Some(QualifiedName::from_type_path(typ)),
-                _ => None,
-            },
-            _ => None,
-        },
-    })
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/remove_ignored.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/remove_ignored.rs
similarity index 60%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/remove_ignored.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/remove_ignored.rs
index 6d9670a..ef20390 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/remove_ignored.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/remove_ignored.rs
@@ -1,20 +1,16 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashSet;
 
+use super::deps::HasDependencies;
 use super::fun::{FnAnalysis, FnKind, FnPhase};
+use crate::conversion::apivec::ApiVec;
 use crate::conversion::{convert_error::ErrorContext, ConvertError};
 use crate::{conversion::api::Api, known_types};
 
@@ -22,16 +18,10 @@
 /// We also eliminate any APIs that depend on some type that we just don't
 /// know about at all. In either case, we don't simply remove the type, but instead
 /// replace it with an error marker.
-pub(crate) fn filter_apis_by_ignored_dependents(mut apis: Vec<Api<FnPhase>>) -> Vec<Api<FnPhase>> {
-    let (ignored_items, valid_items): (Vec<&Api<_>>, Vec<&Api<_>>) = apis.iter().partition(|api| {
-        matches!(
-            api,
-            Api::IgnoredItem {
-                ctx: ErrorContext::Item(..),
-                ..
-            }
-        )
-    });
+pub(crate) fn filter_apis_by_ignored_dependents(mut apis: ApiVec<FnPhase>) -> ApiVec<FnPhase> {
+    let (ignored_items, valid_items): (Vec<&Api<_>>, Vec<&Api<_>>) = apis
+        .iter()
+        .partition(|api| matches!(api, Api::IgnoredItem { .. }));
     let mut ignored_items: HashSet<_> = ignored_items
         .into_iter()
         .map(|api| api.name().clone())
@@ -46,17 +36,22 @@
         apis = apis
             .into_iter()
             .map(|api| {
-                if api.deps().any(|dep| ignored_items.contains(&dep)) {
+                let ignored_dependents: HashSet<_> = api
+                    .deps()
+                    .filter(|dep| ignored_items.contains(dep))
+                    .cloned()
+                    .collect();
+                if !ignored_dependents.is_empty() {
                     iterate_again = true;
                     ignored_items.insert(api.name().clone());
-                    create_ignore_item(api, ConvertError::IgnoredDependent)
+                    create_ignore_item(api, ConvertError::IgnoredDependent(ignored_dependents))
                 } else {
                     let mut missing_deps = api.deps().filter(|dep| {
                         !valid_types.contains(dep) && !known_types().is_known_type(dep)
                     });
                     let first = missing_deps.next();
                     std::mem::drop(missing_deps);
-                    if let Some(missing_dep) = first {
+                    if let Some(missing_dep) = first.cloned() {
                         create_ignore_item(api, ConvertError::UnknownDependentType(missing_dep))
                     } else {
                         api
@@ -78,15 +73,23 @@
             Api::Function {
                 analysis:
                     FnAnalysis {
-                        kind: FnKind::Method(self_ty, _),
+                        kind: FnKind::TraitMethod { .. },
                         ..
                     },
                 ..
-            } => ErrorContext::Method {
-                self_ty: self_ty.get_final_ident(),
-                method: id,
-            },
-            _ => ErrorContext::Item(id),
+            } => None,
+            Api::Function {
+                analysis:
+                    FnAnalysis {
+                        kind:
+                            FnKind::Method {
+                                impl_for: self_ty, ..
+                            },
+                        ..
+                    },
+                ..
+            } => Some(ErrorContext::new_for_method(self_ty.get_final_ident(), id)),
+            _ => Some(ErrorContext::new_for_item(id)),
         },
     }
 }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/tdef.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/tdef.rs
similarity index 78%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/tdef.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/tdef.rs
index 1cef4fa4..941ddcc 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/tdef.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/tdef.rs
@@ -1,16 +1,10 @@
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashSet;
 
@@ -20,7 +14,8 @@
 use crate::{
     conversion::{
         analysis::type_converter::{add_analysis, Annotated, TypeConversionContext, TypeConverter},
-        api::{AnalysisPhase, Api, ApiName, TypedefKind, UnanalyzedApi},
+        api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind},
+        apivec::ApiVec,
         convert_error::{ConvertErrorWithContext, ErrorContext},
         error_reporter::convert_apis,
         parse::BindgenSemanticAttributes,
@@ -47,11 +42,11 @@
 #[allow(clippy::needless_collect)] // we need the extra collect because the closure borrows extra_apis
 pub(crate) fn convert_typedef_targets(
     config: &IncludeCppConfig,
-    apis: Vec<UnanalyzedApi>,
-) -> Vec<Api<TypedefPhase>> {
+    apis: ApiVec<NullPhase>,
+) -> ApiVec<TypedefPhase> {
     let mut type_converter = TypeConverter::new(config, &apis);
-    let mut extra_apis = Vec::new();
-    let mut results = Vec::new();
+    let mut extra_apis = ApiVec::new();
+    let mut results = ApiVec::new();
     convert_apis(
         apis,
         &mut results,
@@ -88,7 +83,7 @@
     ity: ItemType,
     old_tyname: Option<QualifiedName>,
     type_converter: &mut TypeConverter,
-    extra_apis: &mut Vec<UnanalyzedApi>,
+    extra_apis: &mut ApiVec<NullPhase>,
 ) -> Result<Api<TypedefPhase>, ConvertErrorWithContext> {
     let mut converted_type = ity.clone();
     let metadata = BindgenSemanticAttributes::new_retaining_others(&mut converted_type.attrs);
@@ -101,14 +96,14 @@
     match type_conversion_results {
         Err(err) => Err(ConvertErrorWithContext(
             err,
-            Some(ErrorContext::Item(name.name.get_final_ident())),
+            Some(ErrorContext::new_for_item(name.name.get_final_ident())),
         )),
         Ok(Annotated {
             ty: syn::Type::Path(ref typ),
             ..
         }) if QualifiedName::from_type_path(typ) == name.name => Err(ConvertErrorWithContext(
             ConvertError::InfinitelyRecursiveTypedef(name.name.clone()),
-            Some(ErrorContext::Item(name.name.get_final_ident())),
+            Some(ErrorContext::new_for_item(name.name.get_final_ident())),
         )),
         Ok(mut final_type) => {
             converted_type.ty = Box::new(final_type.ty.clone());
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/type_converter.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/type_converter.rs
similarity index 91%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/type_converter.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/type_converter.rs
index d661a1b..1112a51 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/analysis/type_converter.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/analysis/type_converter.rs
@@ -1,20 +1,15 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::{
     conversion::{
-        api::{AnalysisPhase, Api, ApiName, TypedefKind, UnanalyzedApi},
+        api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind, UnanalyzedApi},
+        apivec::ApiVec,
         codegen_cpp::type_to_cpp::type_to_cpp,
         ConvertError,
     },
@@ -34,6 +29,7 @@
 use super::tdef::TypedefAnalysis;
 
 /// Certain kinds of type may require special handling by callers.
+#[derive(Debug)]
 pub(crate) enum TypeKind {
     Regular,
     Pointer,
@@ -47,7 +43,7 @@
 pub(crate) struct Annotated<T> {
     pub(crate) ty: T,
     pub(crate) types_encountered: HashSet<QualifiedName>,
-    pub(crate) extra_apis: Vec<UnanalyzedApi>,
+    pub(crate) extra_apis: ApiVec<NullPhase>,
     pub(crate) kind: TypeKind,
 }
 
@@ -55,7 +51,7 @@
     fn new(
         ty: T,
         types_encountered: HashSet<QualifiedName>,
-        extra_apis: Vec<UnanalyzedApi>,
+        extra_apis: ApiVec<NullPhase>,
         kind: TypeKind,
     ) -> Self {
         Self {
@@ -120,12 +116,12 @@
 }
 
 impl<'a> TypeConverter<'a> {
-    pub(crate) fn new<A: AnalysisPhase>(config: &'a IncludeCppConfig, apis: &[Api<A>]) -> Self
+    pub(crate) fn new<A: AnalysisPhase>(config: &'a IncludeCppConfig, apis: &ApiVec<A>) -> Self
     where
         A::TypedefAnalysis: TypedefTarget,
     {
         Self {
-            types_found: Self::find_types(apis),
+            types_found: find_types(apis),
             typedefs: Self::find_typedefs(apis),
             concrete_templates: Self::find_concrete_templates(apis),
             forward_declarations: Self::find_incomplete_types(apis),
@@ -261,7 +257,7 @@
                 return Ok(Annotated::new(
                     Type::Ptr(resolved_tp.clone()),
                     deps,
-                    Vec::new(),
+                    ApiVec::new(),
                     TypeKind::Pointer,
                 ))
             }
@@ -269,7 +265,7 @@
                 return Ok(Annotated::new(
                     other.clone(),
                     deps,
-                    Vec::new(),
+                    ApiVec::new(),
                     TypeKind::Regular,
                 ))
             }
@@ -290,7 +286,7 @@
             None => typ,
         };
 
-        let mut extra_apis = Vec::new();
+        let mut extra_apis = ApiVec::new();
         let mut kind = TypeKind::Regular;
 
         // Finally let's see if it's generic.
@@ -343,7 +339,7 @@
     {
         let mut new_pun = Punctuated::new();
         let mut types_encountered = HashSet::new();
-        let mut extra_apis = Vec::new();
+        let mut extra_apis = ApiVec::new();
         for arg in pun.into_iter() {
             new_pun.push(match arg {
                 GenericArgument::Type(t) => {
@@ -524,29 +520,7 @@
         }
     }
 
-    fn find_types<A: AnalysisPhase>(apis: &[Api<A>]) -> HashSet<QualifiedName> {
-        apis.iter()
-            .filter_map(|api| match api {
-                Api::ForwardDeclaration { .. }
-                | Api::ConcreteType { .. }
-                | Api::Typedef { .. }
-                | Api::Enum { .. }
-                | Api::Struct { .. }
-                | Api::Subclass { .. }
-                | Api::RustType { .. } => Some(api.name()),
-                Api::StringConstructor { .. }
-                | Api::Function { .. }
-                | Api::Const { .. }
-                | Api::CType { .. }
-                | Api::RustSubclassFn { .. }
-                | Api::IgnoredItem { .. }
-                | Api::RustFn { .. } => None,
-            })
-            .cloned()
-            .collect()
-    }
-
-    fn find_typedefs<A: AnalysisPhase>(apis: &[Api<A>]) -> HashMap<QualifiedName, Type>
+    fn find_typedefs<A: AnalysisPhase>(apis: &ApiVec<A>) -> HashMap<QualifiedName, Type>
     where
         A::TypedefAnalysis: TypedefTarget,
     {
@@ -562,7 +536,7 @@
     }
 
     fn find_concrete_templates<A: AnalysisPhase>(
-        apis: &[Api<A>],
+        apis: &ApiVec<A>,
     ) -> HashMap<String, QualifiedName> {
         apis.iter()
             .filter_map(|api| match &api {
@@ -574,7 +548,7 @@
             .collect()
     }
 
-    fn find_incomplete_types<A: AnalysisPhase>(apis: &[Api<A>]) -> HashSet<QualifiedName> {
+    fn find_incomplete_types<A: AnalysisPhase>(apis: &ApiVec<A>) -> HashSet<QualifiedName> {
         apis.iter()
             .filter_map(|api| match api {
                 Api::ForwardDeclaration { .. } => Some(api.name()),
@@ -623,3 +597,26 @@
         }
     }
 }
+
+pub(crate) fn find_types<A: AnalysisPhase>(apis: &ApiVec<A>) -> HashSet<QualifiedName> {
+    apis.iter()
+        .filter_map(|api| match api {
+            Api::ForwardDeclaration { .. }
+            | Api::ConcreteType { .. }
+            | Api::Typedef { .. }
+            | Api::Enum { .. }
+            | Api::Struct { .. }
+            | Api::Subclass { .. }
+            | Api::RustType { .. } => Some(api.name()),
+            Api::StringConstructor { .. }
+            | Api::Function { .. }
+            | Api::Const { .. }
+            | Api::CType { .. }
+            | Api::RustSubclassFn { .. }
+            | Api::IgnoredItem { .. }
+            | Api::SubclassTraitItem { .. }
+            | Api::RustFn { .. } => None,
+        })
+        .cloned()
+        .collect()
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/api.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/api.rs
similarity index 77%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/api.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/api.rs
index 798c03f..7eb8ac67 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/api.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/api.rs
@@ -1,29 +1,30 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashSet;
 
 use crate::types::{make_ident, Namespace, QualifiedName};
 use autocxx_parser::RustPath;
+use itertools::Itertools;
+use quote::ToTokens;
 use syn::{
-    parse::Parse, punctuated::Punctuated, token::Comma, Attribute, FnArg, Ident, ImplItem,
-    ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt, ReturnType, Signature,
-    Type, Visibility,
+    parse::Parse,
+    punctuated::Punctuated,
+    token::{Comma, Unsafe},
+    Attribute, FnArg, Ident, ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt,
+    Pat, ReturnType, Signature, Type, Visibility,
 };
 
 use super::{
-    analysis::fun::{function_wrapper::CppFunction, ReceiverMutability},
+    analysis::fun::{
+        function_wrapper::{CppFunction, CppFunctionBody, CppFunctionKind},
+        ReceiverMutability,
+    },
     convert_error::{ConvertErrorWithContext, ErrorContext},
     ConvertError,
 };
@@ -39,12 +40,6 @@
             // in which case we'll err on the side of caution.
 }
 
-/// An entry which needs to go into an `impl` block for a given type.
-pub(crate) struct ImplBlockDetails {
-    pub(crate) item: ImplItem,
-    pub(crate) ty: Ident,
-}
-
 /// C++ visibility.
 #[derive(Debug, Clone, PartialEq, Eq, Copy)]
 pub(crate) enum CppVisibility {
@@ -101,20 +96,41 @@
     MutToMut,
 }
 
-/// Indicates that this function didn't exist originally in the C++
-/// but we've created it afresh.
+/// Indicates that this function (which is synthetic) should
+/// be a trait implementation rather than a method or free function.
 #[derive(Clone)]
-pub(crate) enum Synthesis {
-    MakeUnique,
-    SubclassConstructor {
-        subclass: SubclassName,
-        cpp_impl: Box<CppFunction>,
-        is_trivial: bool,
-    },
+pub(crate) enum TraitSynthesis {
     Cast {
         to_type: QualifiedName,
         mutable: CastMutability,
     },
+    AllocUninitialized(QualifiedName),
+    FreeUninitialized(QualifiedName),
+}
+
+/// Details of a subclass constructor.
+/// TODO: zap this; replace with an extra API.
+#[derive(Clone)]
+pub(crate) struct SubclassConstructorDetails {
+    pub(crate) subclass: SubclassName,
+    pub(crate) is_trivial: bool,
+    /// Implementation of the constructor _itself_ as distinct
+    /// from any wrapper function we create to call it.
+    pub(crate) cpp_impl: CppFunction,
+}
+
+/// Contributions to traits representing C++ superclasses that
+/// we may implement as Rust subclasses.
+#[derive(Clone)]
+pub(crate) struct SuperclassMethod {
+    pub(crate) name: Ident,
+    pub(crate) receiver: QualifiedName,
+    pub(crate) params: Punctuated<FnArg, Comma>,
+    pub(crate) param_names: Vec<Pat>,
+    pub(crate) ret_type: ReturnType,
+    pub(crate) receiver_mutability: ReceiverMutability,
+    pub(crate) requires_unsafe: UnsafetyNeeded,
+    pub(crate) is_pure_virtual: bool,
 }
 
 /// Information about references (as opposed to pointers) to be found
@@ -138,7 +154,46 @@
     }
 }
 
-#[derive(Clone, Hash, Eq, PartialEq)]
+#[derive(Clone)]
+pub(crate) struct TraitImplSignature {
+    pub(crate) ty: Type,
+    pub(crate) trait_signature: Type,
+    /// The trait is 'unsafe' itself
+    pub(crate) unsafety: Option<Unsafe>,
+}
+
+impl Eq for TraitImplSignature {}
+
+impl PartialEq for TraitImplSignature {
+    fn eq(&self, other: &Self) -> bool {
+        totokens_equal(&self.unsafety, &other.unsafety)
+            && totokens_equal(&self.ty, &other.ty)
+            && totokens_equal(&self.trait_signature, &other.trait_signature)
+    }
+}
+
+fn totokens_to_string<T: ToTokens>(a: &T) -> String {
+    a.to_token_stream().to_string()
+}
+
+fn totokens_equal<T: ToTokens>(a: &T, b: &T) -> bool {
+    totokens_to_string(a) == totokens_to_string(b)
+}
+
+fn hash_totokens<T: ToTokens, H: std::hash::Hasher>(a: &T, state: &mut H) {
+    use std::hash::Hash;
+    totokens_to_string(a).hash(state)
+}
+
+impl std::hash::Hash for TraitImplSignature {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        hash_totokens(&self.ty, state);
+        hash_totokens(&self.trait_signature, state);
+        hash_totokens(&self.unsafety, state);
+    }
+}
+
+#[derive(Clone, Debug)]
 pub(crate) enum SpecialMemberKind {
     DefaultConstructor,
     CopyConstructor,
@@ -147,6 +202,14 @@
     AssignmentOperator,
 }
 
+#[derive(Clone)]
+pub(crate) enum Provenance {
+    Bindgen,
+    SynthesizedOther,
+    SynthesizedMakeUnique,
+    SynthesizedSubclassConstructor(Box<SubclassConstructorDetails>),
+}
+
 /// A C++ function for which we need to generate bindings, but haven't
 /// yet analyzed in depth. This is little more than a `ForeignItemFn`
 /// broken down into its constituent parts, plus some metadata from the
@@ -159,6 +222,7 @@
 /// `synthesized_*` members which are not filled in from bindgen.
 #[derive(Clone)]
 pub(crate) struct FuncToConvert {
+    pub(crate) provenance: Provenance,
     pub(crate) ident: Ident,
     pub(crate) doc_attr: Option<Attribute>,
     pub(crate) inputs: Punctuated<FnArg, Comma>,
@@ -177,9 +241,11 @@
     /// method receiver, e.g. because we're making a subclass
     /// constructor, fill it in here.
     pub(crate) synthesized_this_type: Option<QualifiedName>,
+    /// If this function should actually belong to a trait.
+    pub(crate) add_to_trait: Option<TraitSynthesis>,
     /// If Some, this function didn't really exist in the original
     /// C++ and instead we're synthesizing it.
-    pub(crate) synthesis: Option<Synthesis>,
+    pub(crate) synthetic_cpp: Option<(CppFunctionBody, CppFunctionKind)>,
     pub(crate) is_deleted: bool,
 }
 
@@ -244,6 +310,15 @@
             .unwrap_or_else(|| self.name.get_final_item().to_string())
     }
 
+    pub(crate) fn qualified_cpp_name(&self) -> String {
+        let cpp_name = self.cpp_name();
+        self.name
+            .ns_segment_iter()
+            .cloned()
+            .chain(std::iter::once(cpp_name))
+            .join("::")
+    }
+
     pub(crate) fn cpp_name_if_present(&self) -> Option<&String> {
         self.cpp_name.as_ref()
     }
@@ -294,6 +369,17 @@
     fn with_suffix(&self, suffix: &str) -> Ident {
         make_ident(format!("{}{}", self.0.name.get_final_item(), suffix))
     }
+    pub(crate) fn get_trait_api_name(sup: &QualifiedName, method_name: &str) -> QualifiedName {
+        QualifiedName::new(
+            sup.get_namespace(),
+            make_ident(format!(
+                "{}_{}_trait_item",
+                sup.get_final_item(),
+                method_name
+            )),
+        )
+    }
+    // TODO this and the following should probably include both class name and method name
     pub(crate) fn get_super_fn_name(superclass_namespace: &Namespace, id: &str) -> QualifiedName {
         let id = make_ident(format!("{}_super", id));
         QualifiedName::new(superclass_namespace, id)
@@ -343,7 +429,6 @@
     /// A function. May include some analysis.
     Function {
         name: ApiName,
-        name_for_gc: Option<QualifiedName>,
         fun: Box<FuncToConvert>,
         analysis: T::FunAnalysis,
     },
@@ -382,7 +467,7 @@
     IgnoredItem {
         name: ApiName,
         err: ConvertError,
-        ctx: ErrorContext,
+        ctx: Option<ErrorContext>,
     },
     /// A Rust type which is not a C++ type.
     RustType { name: ApiName, path: RustPath },
@@ -403,6 +488,12 @@
         name: SubclassName,
         superclass: QualifiedName,
     },
+    /// Contributions to the traits representing superclass methods that we might
+    /// subclass in Rust.
+    SubclassTraitItem {
+        name: ApiName,
+        details: SuperclassMethod,
+    },
 }
 
 pub(crate) struct RustSubclassFnDetails {
@@ -412,11 +503,18 @@
     pub(crate) method_name: Ident,
     pub(crate) superclass: QualifiedName,
     pub(crate) receiver_mutability: ReceiverMutability,
-    pub(crate) dependency: Option<QualifiedName>,
-    pub(crate) requires_unsafe: bool,
+    pub(crate) dependencies: Vec<QualifiedName>,
+    pub(crate) requires_unsafe: UnsafetyNeeded,
     pub(crate) is_pure_virtual: bool,
 }
 
+#[derive(Clone, Debug)]
+pub(crate) enum UnsafetyNeeded {
+    None,
+    JustBridge,
+    Always,
+}
+
 impl<T: AnalysisPhase> Api<T> {
     pub(crate) fn name_info(&self) -> &ApiName {
         match self {
@@ -434,6 +532,7 @@
             Api::RustFn { name, .. } => name,
             Api::RustSubclassFn { name, .. } => name,
             Api::Subclass { name, .. } => &name.0,
+            Api::SubclassTraitItem { name, .. } => name,
         }
     }
 
@@ -461,6 +560,12 @@
             .unwrap_or_else(|| self.name().get_final_item())
     }
 
+    /// If this API turns out to have the same QualifiedName as another,
+    /// whether it's OK to just discard it?
+    pub(crate) fn discard_duplicates(&self) -> bool {
+        matches!(self, Api::IgnoredItem { .. })
+    }
+
     pub(crate) fn valid_types(&self) -> Box<dyn Iterator<Item = QualifiedName>> {
         match self {
             Api::Subclass { name, .. } => Box::new(
@@ -521,7 +626,6 @@
         name: ApiName,
         fun: Box<FuncToConvert>,
         analysis: T::FunAnalysis,
-        name_for_gc: Option<QualifiedName>,
     ) -> Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext>
     where
         T: 'static,
@@ -530,7 +634,6 @@
             name,
             fun,
             analysis,
-            name_for_gc,
         })))
     }
 
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/apivec.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/apivec.rs
new file mode 100644
index 0000000..9f234ef
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/apivec.rs
@@ -0,0 +1,137 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::collections::HashSet;
+
+use crate::{
+    conversion::{api::ApiName, convert_error::ErrorContext, ConvertError},
+    types::QualifiedName,
+};
+
+use super::api::{AnalysisPhase, Api};
+
+/// Newtype wrapper for a list of APIs, which enforces the invariant
+/// that each API has a unique name.
+///
+/// Specifically, each API should have a unique [`QualifiedName`] which is kept
+/// within an [`ApiName`]. The [`QualifiedName`] is used to refer to this API
+/// from others, e.g. to represent edges in the graph used for garbage collection,
+/// so that's why this uniqueness is so important.
+///
+/// At present, this type also refuses to allow mutation of an API once it
+/// has been added to a set. This is because the autocxx engine is
+/// fundamentally organized into lots of analysis phases, each one _adding_
+/// fields rather than mutating earlier fields. The idea here is that it's
+/// impossible for stupid future maintainers (i.e. me) to make errors by
+/// referring to fields before they're filled in. If a field exists, it's
+/// correct.
+///
+/// While this is currently the case, it's possible that in future we could
+/// see legitimate reasons to break this latter invariant and allow mutation
+/// of APIs within an existing `ApiVec`. But it's extremely important that
+/// the naming-uniqueness-invariant remains, so any such mutation should
+/// allow mutation only of other fields, not the name.
+pub(crate) struct ApiVec<P: AnalysisPhase> {
+    apis: Vec<Api<P>>,
+    names: HashSet<QualifiedName>,
+}
+
+impl<P: AnalysisPhase> ApiVec<P> {
+    pub(crate) fn push(&mut self, api: Api<P>) {
+        let name = api.name();
+        let already_contains = self.already_contains(name);
+        if already_contains {
+            if api.discard_duplicates() {
+                // This is already an IgnoredItem or something else where
+                // we can silently drop it.
+                log::info!("Discarding duplicate API for {}", name);
+            } else {
+                log::info!(
+                    "Duplicate API for {} - removing all of them and replacing with an IgnoredItem.",
+                    name
+                );
+                self.retain(|api| api.name() != name);
+                self.push(Api::IgnoredItem {
+                    name: ApiName::new_from_qualified_name(name.clone()),
+                    err: ConvertError::DuplicateItemsFoundInParsing,
+                    ctx: Some(ErrorContext::new_for_item(name.get_final_ident())),
+                })
+            }
+        } else {
+            self.names.insert(name.clone());
+            self.apis.push(api)
+        }
+    }
+
+    fn already_contains(&self, name: &QualifiedName) -> bool {
+        self.names.contains(name)
+    }
+
+    pub(crate) fn new() -> Self {
+        Self::default()
+    }
+
+    pub(crate) fn append(&mut self, more: &mut ApiVec<P>) {
+        self.extend(more.apis.drain(..))
+    }
+
+    pub(crate) fn extend(&mut self, it: impl Iterator<Item = Api<P>>) {
+        // Could be optimized in future
+        for api in it {
+            self.push(api)
+        }
+    }
+
+    pub(crate) fn iter(&self) -> impl Iterator<Item = &Api<P>> {
+        self.apis.iter()
+    }
+
+    pub(crate) fn into_iter(self) -> impl Iterator<Item = Api<P>> {
+        self.apis.into_iter()
+    }
+
+    pub(crate) fn is_empty(&self) -> bool {
+        self.apis.is_empty()
+    }
+
+    pub fn retain<F>(&mut self, f: F)
+    where
+        F: FnMut(&Api<P>) -> bool,
+    {
+        self.apis.retain(f);
+        self.names.clear();
+        self.names
+            .extend(self.apis.iter().map(|api| api.name()).cloned());
+    }
+}
+
+impl<P: AnalysisPhase> Default for ApiVec<P> {
+    fn default() -> Self {
+        Self {
+            apis: Default::default(),
+            names: Default::default(),
+        }
+    }
+}
+
+impl<P: AnalysisPhase> FromIterator<Api<P>> for ApiVec<P> {
+    fn from_iter<I: IntoIterator<Item = Api<P>>>(iter: I) -> Self {
+        let mut this = ApiVec::new();
+        for i in iter {
+            // Could be optimized in future
+            this.push(i);
+        }
+        this
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs
new file mode 100644
index 0000000..a2ea8bf
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/function_wrapper_cpp.rs
@@ -0,0 +1,82 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::conversion::{
+    analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy},
+    ConvertError,
+};
+
+use super::type_to_cpp::{type_to_cpp, CppNameMap};
+
+impl TypeConversionPolicy {
+    pub(super) fn unconverted_type(
+        &self,
+        cpp_name_map: &CppNameMap,
+    ) -> Result<String, ConvertError> {
+        match self.cpp_conversion {
+            CppConversionType::FromUniquePtrToValue => self.unique_ptr_wrapped_type(cpp_name_map),
+            CppConversionType::FromPtrToValue => {
+                Ok(format!("{}*", self.unwrapped_type_as_string(cpp_name_map)?))
+            }
+            _ => self.unwrapped_type_as_string(cpp_name_map),
+        }
+    }
+
+    pub(super) fn converted_type(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
+        match self.cpp_conversion {
+            CppConversionType::FromValueToUniquePtr => self.unique_ptr_wrapped_type(cpp_name_map),
+            _ => self.unwrapped_type_as_string(cpp_name_map),
+        }
+    }
+
+    fn unwrapped_type_as_string(&self, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
+        type_to_cpp(&self.unwrapped_type, cpp_name_map)
+    }
+
+    fn unique_ptr_wrapped_type(
+        &self,
+        original_name_map: &CppNameMap,
+    ) -> Result<String, ConvertError> {
+        Ok(format!(
+            "std::unique_ptr<{}>",
+            self.unwrapped_type_as_string(original_name_map)?
+        ))
+    }
+
+    pub(super) fn cpp_conversion(
+        &self,
+        var_name: &str,
+        cpp_name_map: &CppNameMap,
+        is_return: bool,
+    ) -> Result<String, ConvertError> {
+        // If is_return we want to avoid unnecessary std::moves because they
+        // make RVO less effective
+        Ok(match self.cpp_conversion {
+            CppConversionType::None => var_name.to_string(),
+            CppConversionType::Move => {
+                format!("std::move({})", var_name)
+            }
+            CppConversionType::FromUniquePtrToValue | CppConversionType::FromPtrToMove => {
+                format!("std::move(*{})", var_name)
+            }
+            CppConversionType::FromValueToUniquePtr => format!(
+                "std::make_unique<{}>({})",
+                self.unconverted_type(cpp_name_map)?,
+                var_name
+            ),
+            CppConversionType::FromPtrToValue => {
+                let dereference = format!("*{}", var_name);
+                if is_return {
+                    dereference
+                } else {
+                    format!("std::move({})", dereference)
+                }
+            }
+        })
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/mod.rs
similarity index 82%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/mod.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/mod.rs
index b05859a2..a60adc7 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/mod.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/mod.rs
@@ -1,18 +1,13 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 mod function_wrapper_cpp;
+mod new_and_delete_prelude;
 pub(crate) mod type_to_cpp;
 
 use crate::{
@@ -26,15 +21,19 @@
 use type_to_cpp::{original_name_map_from_apis, type_to_cpp, CppNameMap};
 
 use self::type_to_cpp::{
-    identifier_using_original_name_map, namespaced_name_using_original_name_map,
+    final_ident_using_original_name_map, namespaced_name_using_original_name_map,
 };
 
 use super::{
-    analysis::fun::{
-        function_wrapper::{CppFunction, CppFunctionBody},
-        FnPhase,
+    analysis::{
+        fun::{
+            function_wrapper::{CppFunction, CppFunctionBody},
+            FnPhase, PodAndDepAnalysis,
+        },
+        pod::PodAnalysis,
     },
-    api::{Api, SubclassName, Synthesis},
+    api::{Api, Provenance, SubclassName, TypeKind},
+    apivec::ApiVec,
     ConvertError,
 };
 
@@ -43,28 +42,27 @@
     System(&'static str),
     CxxH,
     CxxgenH,
+    NewDeletePrelude,
 }
 
 impl Header {
     fn include_stmt(&self, cpp_codegen_options: &CppCodegenOptions) -> String {
         let blank = "".to_string();
-        format!(
-            "#include {}",
-            match self {
-                Self::System(name) => format!("<{}>", name),
-                Self::CxxH => {
-                    let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank);
-                    format!("\"{}cxx.h\"", prefix)
-                }
-                Self::CxxgenH => {
-                    let prefix = cpp_codegen_options
-                        .path_to_cxxgen_h
-                        .as_ref()
-                        .unwrap_or(&blank);
-                    format!("\"{}cxxgen.h\"", prefix)
-                }
+        match self {
+            Self::System(name) => format!("#include <{}>", name),
+            Self::CxxH => {
+                let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank);
+                format!("#include \"{}cxx.h\"", prefix)
             }
-        )
+            Self::CxxgenH => {
+                let prefix = cpp_codegen_options
+                    .path_to_cxxgen_h
+                    .as_ref()
+                    .unwrap_or(&blank);
+                format!("#include \"{}cxxgen.h\"", prefix)
+            }
+            Header::NewDeletePrelude => new_and_delete_prelude::NEW_AND_DELETE_PRELUDE.to_string(),
+        }
     }
 
     fn is_system(&self) -> bool {
@@ -97,7 +95,7 @@
     inclusions: String,
     original_name_map: CppNameMap,
     config: &'a IncludeCppConfig,
-    cpp_codegen_options: &'a CppCodegenOptions,
+    cpp_codegen_options: &'a CppCodegenOptions<'a>,
 }
 
 struct SubclassFunction<'a> {
@@ -108,7 +106,7 @@
 impl<'a> CppCodeGenerator<'a> {
     pub(crate) fn generate_cpp_code(
         inclusions: String,
-        apis: &[Api<FnPhase>],
+        apis: &ApiVec<FnPhase>,
         config: &'a IncludeCppConfig,
         cpp_codegen_options: &CppCodegenOptions,
     ) -> Result<Option<CppFilePair>, ConvertError> {
@@ -161,14 +159,11 @@
                     fun,
                     ..
                 } => {
-                    if let Some(Synthesis::SubclassConstructor {
-                        subclass, cpp_impl, ..
-                    }) = &fun.synthesis
-                    {
+                    if let Provenance::SynthesizedSubclassConstructor(details) = &fun.provenance {
                         constructors_by_subclass
-                            .entry(subclass.clone())
+                            .entry(details.subclass.clone())
                             .or_default()
-                            .push(cpp_impl);
+                            .push(&details.cpp_impl);
                     }
                     self.generate_cpp_function(cpp_wrapper)?
                 }
@@ -189,6 +184,21 @@
                             is_pure_virtual: details.is_pure_virtual,
                         });
                 }
+                Api::Struct {
+                    name,
+                    analysis:
+                        PodAndDepAnalysis {
+                            pod:
+                                PodAnalysis {
+                                    kind: TypeKind::Pod,
+                                    ..
+                                },
+                            ..
+                        },
+                    ..
+                } => {
+                    self.generate_pod_assertion(name.qualified_cpp_name());
+                }
                 _ => panic!("Should have filtered on needs_cpp_codegen"),
             }
         }
@@ -220,7 +230,10 @@
                 headers, self.inclusions, type_definitions, declarations
             );
             log::info!("Additional C++ decls:\n{}", declarations);
-            let header_name = format!("autocxxgen_{}.h", self.config.get_mod_name());
+            let header_name = self
+                .cpp_codegen_options
+                .header_namer
+                .name_header(self.config.get_mod_name().to_string());
             let implementation = if self
                 .additional_functions
                 .iter()
@@ -273,6 +286,24 @@
         s
     }
 
+    fn generate_pod_assertion(&mut self, name: String) {
+        // These assertions are generated by cxx for trivial ExternTypes but
+        // *only if* such types are used as trivial types in the cxx::bridge.
+        // It's possible for types which we generate to be used even without
+        // passing through the cxx::bridge, and as we generate Drop impls, that
+        // can result in destructors for nested types being called multiple times
+        // if we represent them as trivial types. So generate an extra
+        // assertion to make sure.
+        let declaration = Some(format!("static_assert(::rust::IsRelocatable<{}>::value, \"type {} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");", name, name));
+        self.additional_functions.push(AdditionalFunction {
+            type_definition: None,
+            declaration,
+            definition: None,
+            headers: vec![Header::CxxH],
+            cpp_headers: Vec::new(),
+        })
+    }
+
     fn generate_string_constructor(&mut self) {
         let makestring_name = self.config.get_makestring_name();
         let declaration = Some(format!("inline std::unique_ptr<std::string> {}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}", makestring_name));
@@ -418,24 +449,32 @@
         } else {
             arg_list.join(", ")
         };
-        let (mut underlying_function_call, field_assignments) = match &details.payload {
-            CppFunctionBody::MakeUnique | CppFunctionBody::Cast => (arg_list, "".to_string()),
+        let (mut underlying_function_call, field_assignments, need_allocators) = match &details
+            .payload
+        {
+            CppFunctionBody::MakeUnique | CppFunctionBody::Cast => {
+                (arg_list, "".to_string(), false)
+            }
             CppFunctionBody::PlacementNew(ns, id) => {
                 let ty_id = QualifiedName::new(ns, id.clone());
-                let ty_id =
-                    namespaced_name_using_original_name_map(&ty_id, &self.original_name_map);
+                let ty_id = self.namespaced_name(&ty_id);
                 (
                     format!("new ({}) {}({})", receiver.unwrap(), ty_id, arg_list),
                     "".to_string(),
+                    false,
                 )
             }
             CppFunctionBody::Destructor(ns, id) => {
                 let ty_id = QualifiedName::new(ns, id.clone());
-                let ty_id = identifier_using_original_name_map(&ty_id, &self.original_name_map);
-                (format!("{}->~{}()", arg_list, ty_id), "".to_string())
+                let ty_id = final_ident_using_original_name_map(&ty_id, &self.original_name_map);
+                (format!("{}->~{}()", arg_list, ty_id), "".to_string(), false)
             }
             CppFunctionBody::FunctionCall(ns, id) => match receiver {
-                Some(receiver) => (format!("{}.{}({})", receiver, id, arg_list), "".to_string()),
+                Some(receiver) => (
+                    format!("{}.{}({})", receiver, id, arg_list),
+                    "".to_string(),
+                    false,
+                ),
                 None => {
                     let underlying_function_call = ns
                         .into_iter()
@@ -445,6 +484,7 @@
                     (
                         format!("{}({})", underlying_function_call, arg_list),
                         "".to_string(),
+                        false,
                     )
                 }
             },
@@ -457,9 +497,23 @@
                 (
                     format!("{}({})", underlying_function_call, arg_list),
                     "".to_string(),
+                    false,
                 )
             }
-            CppFunctionBody::ConstructSuperclass(_) => ("".to_string(), arg_list),
+            CppFunctionBody::ConstructSuperclass(_) => ("".to_string(), arg_list, false),
+            CppFunctionBody::AllocUninitialized(ty) => {
+                let namespaced_ty = self.namespaced_name(ty);
+                (
+                    format!("new_appropriately<{}>(1);", namespaced_ty,),
+                    "".to_string(),
+                    true,
+                )
+            }
+            CppFunctionBody::FreeUninitialized(ty) => (
+                format!("delete_appropriately<{}>(arg0);", self.namespaced_name(ty)),
+                "".to_string(),
+                true,
+            ),
         };
         if let Some(ret) = &details.return_conversion {
             underlying_function_call = format!(
@@ -509,15 +563,23 @@
                 None,
             )
         };
+        let mut headers = vec![Header::System("memory")];
+        if need_allocators {
+            headers.push(Header::NewDeletePrelude);
+        }
         Ok(AdditionalFunction {
             type_definition: None,
             declaration,
             definition,
-            headers: vec![Header::System("memory")],
+            headers,
             cpp_headers: Vec::new(),
         })
     }
 
+    fn namespaced_name(&self, name: &QualifiedName) -> String {
+        namespaced_name_using_original_name_map(name, &self.original_name_map)
+    }
+
     fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
         let cpp_name = tn.to_cpp_name();
         self.generate_typedef(tn, cpp_name)
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/new_and_delete_prelude.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/new_and_delete_prelude.rs
new file mode 100644
index 0000000..67e9e8cc
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/new_and_delete_prelude.rs
@@ -0,0 +1,44 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use indoc::indoc;
+
+/// This is logic to call either an overloaded operator new/delete
+/// or the standard one.
+/// The SFINAE magic here is: int is a better match than long,
+/// and so the versions which match class-specific operator new/delete
+/// will be used in preference to the general global ::operator new/delete.
+pub(super) static NEW_AND_DELETE_PRELUDE: &str = indoc! {"
+    #include <stddef.h>
+    #ifndef AUTOCXX_NEW_AND_DELETE_PRELUDE
+    #define AUTOCXX_NEW_AND_DELETE_PRELUDE
+    // Mechanics to call custom operator new and delete
+    template <typename T>
+    auto delete_imp(T *ptr, int) -> decltype((void)T::operator delete(ptr)) {
+      T::operator delete(ptr);
+    }
+    template <typename T> void delete_imp(T *ptr, long) { ::operator delete(ptr); }
+    template <typename T> void delete_appropriately(T *obj) {
+      // 0 is a better match for the first 'delete_imp' so will match
+      // preferentially.
+      delete_imp(obj, 0);
+    }
+    template <typename T>
+    auto new_imp(size_t count, int) -> decltype(T::operator new(count)) {
+      return T::operator new(count);
+    }
+    template <typename T> void *new_imp(size_t count, long) {
+      return ::operator new(count);
+    }
+    template <typename T> T *new_appropriately(size_t count) {
+      // 0 is a better match for the first 'delete_imp' so will match
+      // preferentially.
+      return static_cast<T *>(new_imp<T>(count, 0));
+    }
+    #endif // AUTOCXX_NEW_AND_DELETE_PRELUDE
+"};
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/type_to_cpp.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/type_to_cpp.rs
similarity index 81%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/type_to_cpp.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/type_to_cpp.rs
index a132869..526609d 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_cpp/type_to_cpp.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_cpp/type_to_cpp.rs
@@ -1,19 +1,13 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::{
-    conversion::{api::Api, AnalysisPhase, ConvertError},
+    conversion::{apivec::ApiVec, AnalysisPhase, ConvertError},
     types::QualifiedName,
 };
 use itertools::Itertools;
@@ -27,7 +21,7 @@
 /// in the QualifiedName.
 pub(crate) type CppNameMap = HashMap<QualifiedName, String>;
 
-pub(crate) fn original_name_map_from_apis<T: AnalysisPhase>(apis: &[Api<T>]) -> CppNameMap {
+pub(crate) fn original_name_map_from_apis<T: AnalysisPhase>(apis: &ApiVec<T>) -> CppNameMap {
     apis.iter()
         .filter_map(|api| {
             api.cpp_name()
@@ -52,14 +46,22 @@
     }
 }
 
-pub(crate) fn identifier_using_original_name_map(
+pub(crate) fn final_ident_using_original_name_map(
     qual_name: &QualifiedName,
     original_name_map: &CppNameMap,
 ) -> String {
-    original_name_map
-        .get(qual_name)
-        .cloned()
-        .unwrap_or_else(|| qual_name.get_final_cpp_item())
+    match original_name_map.get(qual_name) {
+        Some(original_name) => {
+            // If we have an original name, this may be a nested struct
+            // (e.g. A::B). The final ident here is just 'B' so...
+            original_name
+                .rsplit_once("::")
+                .map_or(original_name.clone(), |(_, original_name)| {
+                    original_name.to_string()
+                })
+        }
+        None => qual_name.get_final_cpp_item(),
+    }
 }
 
 pub(crate) fn type_to_cpp(ty: &Type, cpp_name_map: &CppNameMap) -> Result<String, ConvertError> {
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/fun_codegen.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/fun_codegen.rs
new file mode 100644
index 0000000..8ce6166
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/fun_codegen.rs
@@ -0,0 +1,413 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashSet;
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{
+    parse::Parser,
+    parse_quote,
+    punctuated::Punctuated,
+    token::{Comma, Unsafe},
+    Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType,
+};
+
+use super::{
+    unqualify::{unqualify_params, unqualify_ret_type},
+    ImplBlockDetails, RsCodegenResult, TraitImplBlockDetails, Use,
+};
+use crate::{
+    conversion::{
+        analysis::fun::{
+            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy,
+            TraitMethodDetails,
+        },
+        api::UnsafetyNeeded,
+        codegen_rs::lifetime::add_lifetime_to_all_params,
+    },
+    types::{Namespace, QualifiedName},
+};
+use crate::{
+    conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary},
+    types::make_ident,
+};
+
+impl UnsafetyNeeded {
+    pub(crate) fn bridge_token(&self) -> Option<Unsafe> {
+        match self {
+            UnsafetyNeeded::None => None,
+            _ => Some(parse_quote! { unsafe }),
+        }
+    }
+
+    pub(crate) fn wrapper_token(&self) -> Option<Unsafe> {
+        match self {
+            UnsafetyNeeded::Always => Some(parse_quote! { unsafe }),
+            _ => None,
+        }
+    }
+
+    pub(crate) fn from_param_details(params: &[ArgumentAnalysis], ignore_receiver: bool) -> Self {
+        params.iter().fold(UnsafetyNeeded::None, |accumulator, pd| {
+            if matches!(accumulator, UnsafetyNeeded::Always) {
+                UnsafetyNeeded::Always
+            } else if pd.self_type.is_some() && ignore_receiver {
+                if matches!(
+                    pd.requires_unsafe,
+                    UnsafetyNeeded::Always | UnsafetyNeeded::JustBridge
+                ) {
+                    UnsafetyNeeded::JustBridge
+                } else {
+                    accumulator
+                }
+            } else if matches!(pd.requires_unsafe, UnsafetyNeeded::Always) {
+                UnsafetyNeeded::Always
+            } else if matches!(accumulator, UnsafetyNeeded::JustBridge)
+                || matches!(pd.requires_unsafe, UnsafetyNeeded::JustBridge)
+            {
+                UnsafetyNeeded::JustBridge
+            } else {
+                UnsafetyNeeded::None
+            }
+        })
+    }
+}
+
+pub(super) fn gen_function(
+    ns: &Namespace,
+    fun: FuncToConvert,
+    analysis: FnAnalysis,
+    cpp_call_name: String,
+    non_pod_types: &HashSet<QualifiedName>,
+) -> RsCodegenResult {
+    if analysis.ignore_reason.is_err() || !analysis.externally_callable {
+        return RsCodegenResult::default();
+    }
+    let cxxbridge_name = analysis.cxxbridge_name;
+    let rust_name = &analysis.rust_name;
+    let ret_type = analysis.ret_type;
+    let param_details = analysis.param_details;
+    let wrapper_function_needed = analysis.cpp_wrapper.is_some();
+    let params = analysis.params;
+    let vis = analysis.vis;
+    let kind = analysis.kind;
+    let doc_attr = fun.doc_attr;
+
+    let mut cpp_name_attr = Vec::new();
+    let mut impl_entry = None;
+    let mut trait_impl_entry = None;
+    let mut bindgen_mod_items = Vec::new();
+    let always_unsafe_due_to_trait_definition = match kind {
+        FnKind::TraitMethod { ref details, .. } => details.trait_call_is_unsafe,
+        _ => false,
+    };
+    let fn_generator = FnGenerator {
+        param_details: &param_details,
+        cxxbridge_name: &cxxbridge_name,
+        rust_name,
+        unsafety: &analysis.requires_unsafe,
+        always_unsafe_due_to_trait_definition,
+        doc_attr: &doc_attr,
+        non_pod_types,
+    };
+    // In rare occasions, we might need to give an explicit lifetime.
+    let (lifetime_tokens, params, ret_type) =
+        add_explicit_lifetime_if_necessary(&param_details, params, &ret_type, non_pod_types);
+
+    if analysis.rust_wrapper_needed {
+        match kind {
+            FnKind::Method {
+                ref impl_for,
+                method_kind: MethodKind::Constructor { .. },
+                ..
+            } => {
+                // Constructor.
+                impl_entry = Some(fn_generator.generate_constructor_impl(impl_for));
+            }
+            FnKind::Method {
+                ref impl_for,
+                ref method_kind,
+                ..
+            } => {
+                // Method, or static method.
+                impl_entry = Some(fn_generator.generate_method_impl(
+                    matches!(
+                        method_kind,
+                        MethodKind::MakeUnique | MethodKind::Constructor { .. }
+                    ),
+                    impl_for,
+                    &ret_type,
+                ));
+            }
+            FnKind::TraitMethod { ref details, .. } => {
+                trait_impl_entry = Some(fn_generator.generate_trait_impl(details, &ret_type));
+            }
+            _ => {
+                // Generate plain old function
+                bindgen_mod_items.push(fn_generator.generate_function_impl(&ret_type));
+            }
+        }
+    }
+
+    let materialization = match kind {
+        FnKind::Method { .. } | FnKind::TraitMethod { .. } => None,
+        FnKind::Function => match analysis.rust_rename_strategy {
+            _ if analysis.rust_wrapper_needed => {
+                Some(Use::SpecificNameFromBindgen(make_ident(rust_name)))
+            }
+            RustRenameStrategy::RenameInOutputMod(ref alias) => {
+                Some(Use::UsedFromCxxBridgeWithAlias(alias.clone()))
+            }
+            _ => Some(Use::UsedFromCxxBridge),
+        },
+    };
+    if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
+        cpp_name_attr = Attribute::parse_outer
+            .parse2(quote!(
+                #[cxx_name = #cpp_call_name]
+            ))
+            .unwrap();
+    }
+
+    // Finally - namespace support. All the Types in everything
+    // above this point are fully qualified. We need to unqualify them.
+    // We need to do that _after_ the above wrapper_function_needed
+    // work, because it relies upon spotting fully qualified names like
+    // std::unique_ptr. However, after it's done its job, all such
+    // well-known types should be unqualified already (e.g. just UniquePtr)
+    // and the following code will act to unqualify only those types
+    // which the user has declared.
+    let params = unqualify_params(params);
+    let ret_type = unqualify_ret_type(ret_type.into_owned());
+    // And we need to make an attribute for the namespace that the function
+    // itself is in.
+    let namespace_attr = if ns.is_empty() || wrapper_function_needed {
+        Vec::new()
+    } else {
+        let namespace_string = ns.to_string();
+        Attribute::parse_outer
+            .parse2(quote!(
+                #[namespace = #namespace_string]
+            ))
+            .unwrap()
+    };
+    // At last, actually generate the cxx::bridge entry.
+    let bridge_unsafety = analysis.requires_unsafe.bridge_token();
+    let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
+        #(#namespace_attr)*
+        #(#cpp_name_attr)*
+        #doc_attr
+        #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type;
+    ));
+    RsCodegenResult {
+        extern_c_mod_items: vec![extern_c_mod_item],
+        bindgen_mod_items,
+        impl_entry,
+        trait_impl_entry,
+        materializations: materialization.into_iter().collect(),
+        ..Default::default()
+    }
+}
+
+/// Knows how to generate a given function.
+#[derive(Clone)]
+struct FnGenerator<'a> {
+    param_details: &'a [ArgumentAnalysis],
+    cxxbridge_name: &'a Ident,
+    rust_name: &'a str,
+    unsafety: &'a UnsafetyNeeded,
+    always_unsafe_due_to_trait_definition: bool,
+    doc_attr: &'a Option<Attribute>,
+    non_pod_types: &'a HashSet<QualifiedName>,
+}
+
+impl<'a> FnGenerator<'a> {
+    fn generate_arg_lists(
+        &self,
+        avoid_self: bool,
+    ) -> (Punctuated<FnArg, Comma>, Vec<TokenStream>, Vec<TokenStream>) {
+        let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new();
+        let mut local_variables = Vec::new();
+        let mut arg_list = Vec::new();
+        let wrap_unsafe_calls = self.should_wrap_unsafe_calls();
+        for pd in self.param_details {
+            let type_name = pd.conversion.rust_wrapper_unconverted_type();
+            let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
+                parse_quote!(self)
+            } else {
+                pd.name.clone()
+            };
+            let param_mutability = pd.conversion.rust_conversion.requires_mutability();
+            wrapper_params.push(parse_quote!(
+                #param_mutability #wrapper_arg_name: #type_name
+            ));
+            let (local_variable, actual_arg) = pd
+                .conversion
+                .rust_conversion(wrapper_arg_name, wrap_unsafe_calls);
+            arg_list.push(actual_arg);
+            local_variables.extend(local_variable.into_iter());
+        }
+        (wrapper_params, local_variables, arg_list)
+    }
+
+    /// Generate an 'impl Type { methods-go-here }' item
+    fn generate_method_impl(
+        &self,
+        avoid_self: bool,
+        impl_block_type_name: &QualifiedName,
+        ret_type: &ReturnType,
+    ) -> Box<ImplBlockDetails> {
+        let (wrapper_params, local_variables, arg_list) = self.generate_arg_lists(avoid_self);
+        let (lifetime_tokens, wrapper_params, ret_type) = add_explicit_lifetime_if_necessary(
+            self.param_details,
+            wrapper_params,
+            ret_type,
+            self.non_pod_types,
+        );
+        let rust_name = make_ident(self.rust_name);
+        let unsafety = self.unsafety.wrapper_token();
+        let doc_attr = self.doc_attr;
+        let cxxbridge_name = self.cxxbridge_name;
+        let call_body = self.wrap_call_with_unsafe(quote! {
+            cxxbridge::#cxxbridge_name ( #(#arg_list),* )
+        });
+        Box::new(ImplBlockDetails {
+            item: ImplItem::Method(parse_quote! {
+                #doc_attr
+                pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
+                    #(#local_variables),*
+                    #call_body
+                }
+            }),
+            ty: impl_block_type_name.get_final_ident(),
+        })
+    }
+
+    /// Generate an 'impl Trait for Type { methods-go-here }' in its entrety.
+    fn generate_trait_impl(
+        &self,
+        details: &TraitMethodDetails,
+        ret_type: &ReturnType,
+    ) -> Box<TraitImplBlockDetails> {
+        let (mut wrapper_params, local_variables, arg_list) =
+            self.generate_arg_lists(details.avoid_self);
+        if let Some(parameter_reordering) = &details.parameter_reordering {
+            wrapper_params = Self::reorder_parameters(wrapper_params, parameter_reordering);
+        }
+        let (lifetime_tokens, wrapper_params, ret_type) = add_explicit_lifetime_if_necessary(
+            self.param_details,
+            wrapper_params,
+            ret_type,
+            self.non_pod_types,
+        );
+        let doc_attr = self.doc_attr;
+        let unsafety = self.unsafety.wrapper_token();
+        let cxxbridge_name = self.cxxbridge_name;
+        let key = details.trt.clone();
+        let method_name = &details.method_name;
+        let call_body = self.wrap_call_with_unsafe(quote! {
+            cxxbridge::#cxxbridge_name ( #(#arg_list),* )
+        });
+        let item = parse_quote! {
+            #doc_attr
+            #unsafety fn #method_name #lifetime_tokens ( #wrapper_params ) #ret_type {
+                #(#local_variables),*
+                #call_body
+            }
+        };
+        Box::new(TraitImplBlockDetails { item, key })
+    }
+
+    fn should_wrap_unsafe_calls(&self) -> bool {
+        matches!(self.unsafety, UnsafetyNeeded::JustBridge)
+            || self.always_unsafe_due_to_trait_definition
+    }
+
+    fn wrap_call_with_unsafe(&self, call: TokenStream) -> TokenStream {
+        if self.should_wrap_unsafe_calls() {
+            quote! {
+                unsafe {
+                    #call
+                }
+            }
+        } else {
+            call
+        }
+    }
+
+    /// Generate a 'impl Type { methods-go-here }' item which is a constructor
+    /// for use with moveit traits.
+    fn generate_constructor_impl(
+        &self,
+        impl_block_type_name: &QualifiedName,
+    ) -> Box<ImplBlockDetails> {
+        let (wrapper_params, local_variables, arg_list) = self.generate_arg_lists(true);
+        let mut wrapper_params: Punctuated<FnArg, Comma> =
+            wrapper_params.into_iter().skip(1).collect();
+        let ptr_arg_name = &arg_list[0];
+        let rust_name = make_ident(&self.rust_name);
+        let any_references = self.param_details.iter().any(|pd| pd.was_reference);
+        let (lifetime_param, lifetime_addition) = if any_references {
+            add_lifetime_to_all_params(&mut wrapper_params);
+            (quote! { <'a> }, quote! { + 'a })
+        } else {
+            (quote! {}, quote! {})
+        };
+        let cxxbridge_name = self.cxxbridge_name;
+        let body = quote! {
+            #(#local_variables),*
+            autocxx::moveit::new::by_raw(move |#ptr_arg_name| {
+                let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr();
+                cxxbridge::#cxxbridge_name(#(#arg_list),* )
+            })
+        };
+        let body = self.wrap_call_with_unsafe(body);
+        let doc_attr = self.doc_attr;
+        let unsafety = self.unsafety.wrapper_token();
+        Box::new(ImplBlockDetails {
+            item: ImplItem::Method(parse_quote! {
+                #doc_attr
+                pub #unsafety fn #rust_name #lifetime_param ( #wrapper_params ) -> impl autocxx::moveit::new::New<Output=Self> #lifetime_addition {
+                    #body
+                }
+            }),
+            ty: impl_block_type_name.get_final_ident(),
+        })
+    }
+
+    /// Generate a function call wrapper
+    fn generate_function_impl(&self, ret_type: &ReturnType) -> Item {
+        let (wrapper_params, local_variables, arg_list) = self.generate_arg_lists(false);
+        let rust_name = make_ident(self.rust_name);
+        let doc_attr = self.doc_attr;
+        let unsafety = self.unsafety.wrapper_token();
+        let cxxbridge_name = self.cxxbridge_name;
+        let body = self.wrap_call_with_unsafe(quote! {
+            cxxbridge::#cxxbridge_name ( #(#arg_list),* )
+        });
+        Item::Fn(parse_quote! {
+            #doc_attr
+            pub #unsafety fn #rust_name ( #wrapper_params ) #ret_type {
+                #(#local_variables),*
+                #body
+            }
+        })
+    }
+
+    fn reorder_parameters(
+        params: Punctuated<FnArg, Comma>,
+        parameter_ordering: &[usize],
+    ) -> Punctuated<FnArg, Comma> {
+        let old_params = params.into_iter().collect::<Vec<_>>();
+        parameter_ordering
+            .iter()
+            .map(|n| old_params.get(*n).unwrap().clone())
+            .collect()
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/function_wrapper_rs.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/function_wrapper_rs.rs
new file mode 100644
index 0000000..882300c
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/function_wrapper_rs.rs
@@ -0,0 +1,133 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use proc_macro2::TokenStream;
+use syn::{Pat, Type, TypePtr};
+
+use crate::{
+    conversion::analysis::fun::function_wrapper::{RustConversionType, TypeConversionPolicy},
+    types::make_ident,
+};
+use quote::quote;
+use syn::parse_quote;
+
+impl TypeConversionPolicy {
+    pub(super) fn rust_wrapper_unconverted_type(&self) -> Type {
+        match self.rust_conversion {
+            RustConversionType::None => self.converted_rust_type(),
+            RustConversionType::ToBoxedUpHolder(ref sub) => {
+                let id = sub.id();
+                parse_quote! { autocxx::subclass::CppSubclassRustPeerHolder<
+                    super::super::super:: #id>
+                }
+            }
+            RustConversionType::FromStr => parse_quote! { impl ToCppString },
+            RustConversionType::FromPinMaybeUninitToPtr => {
+                let ty = match &self.unwrapped_type {
+                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    _ => panic!("Not a ptr"),
+                };
+                parse_quote! {
+                    ::std::pin::Pin<&mut ::std::mem::MaybeUninit< #ty >>
+                }
+            }
+            RustConversionType::FromPinMoveRefToPtr => {
+                let ty = match &self.unwrapped_type {
+                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    _ => panic!("Not a ptr"),
+                };
+                parse_quote! {
+                    ::std::pin::Pin<autocxx::moveit::MoveRef< '_, #ty >>
+                }
+            }
+            RustConversionType::FromTypeToPtr => {
+                let ty = match &self.unwrapped_type {
+                    Type::Ptr(TypePtr { elem, .. }) => &*elem,
+                    _ => panic!("Not a ptr"),
+                };
+                parse_quote! { &mut #ty }
+            }
+            RustConversionType::FromValueParamToPtr => {
+                let ty = &self.unwrapped_type;
+                parse_quote! { impl autocxx::ValueParam<#ty> }
+            }
+        }
+    }
+
+    pub(super) fn rust_conversion(
+        &self,
+        var: Pat,
+        wrap_in_unsafe: bool,
+    ) -> (Option<TokenStream>, TokenStream) {
+        match self.rust_conversion {
+            RustConversionType::None => (None, quote! { #var }),
+            RustConversionType::FromStr => (None, quote! ( #var .into_cpp() )),
+            RustConversionType::ToBoxedUpHolder(ref sub) => {
+                let holder_type = sub.holder();
+                (
+                    None,
+                    quote! {
+                        Box::new(#holder_type(#var))
+                    },
+                )
+            }
+            RustConversionType::FromPinMaybeUninitToPtr => (
+                None,
+                quote! {
+                    #var.get_unchecked_mut().as_mut_ptr()
+                },
+            ),
+            RustConversionType::FromPinMoveRefToPtr => (
+                None,
+                quote! {
+                    { let r: &mut _ = ::std::pin::Pin::into_inner_unchecked(#var.as_mut());
+                    r
+                    }
+                },
+            ),
+            RustConversionType::FromTypeToPtr => (
+                None,
+                quote! {
+                    #var
+                },
+            ),
+            RustConversionType::FromValueParamToPtr => {
+                let var_name = if let Pat::Ident(pti) = &var {
+                    &pti.ident
+                } else {
+                    panic!("Unexpected non-ident parameter name");
+                };
+                let space_var_name = make_ident(format!("{}_space", var_name));
+                let call = quote! { #space_var_name.as_mut().populate(#var_name);  };
+                let call = if wrap_in_unsafe {
+                    quote! {
+                        unsafe {
+                            #call
+                        }
+                    }
+                } else {
+                    call
+                };
+                // This is the usual trick to put something on the stack, then
+                // immediately shadow the variable name so it can't be accessed or moved.
+                (
+                    Some(quote! {
+                        let mut #space_var_name = autocxx::ValueParamHandler::default();
+                        let mut #space_var_name = unsafe {
+                            std::pin::Pin::new_unchecked(&mut #space_var_name)
+                        };
+                        #call
+                    }),
+                    quote! {
+                        #space_var_name.get_ptr()
+                    },
+                )
+            }
+        }
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/impl_item_creator.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/impl_item_creator.rs
new file mode 100644
index 0000000..89ef896
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/impl_item_creator.rs
@@ -0,0 +1,41 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx_parser::IncludeCppConfig;
+use syn::{parse_quote, Ident, Item};
+
+pub(crate) fn create_impl_items(
+    id: &Ident,
+    movable: bool,
+    destroyable: bool,
+    config: &IncludeCppConfig,
+) -> Vec<Item> {
+    if config.exclude_impls {
+        return vec![];
+    }
+    let mut results = Vec::new();
+    if destroyable {
+        results.extend([
+            Item::Impl(parse_quote! {
+                impl UniquePtr<#id> {}
+            }),
+            Item::Impl(parse_quote! {
+                impl SharedPtr<#id> {}
+            }),
+            Item::Impl(parse_quote! {
+                impl WeakPtr<#id> {}
+            }),
+        ]);
+    }
+    if movable {
+        results.push(Item::Impl(parse_quote! {
+            impl CxxVector<#id> {}
+        }))
+    }
+    results
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/lifetime.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/lifetime.rs
similarity index 61%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/lifetime.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/lifetime.rs
index ffca951..1693d04 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/lifetime.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/lifetime.rs
@@ -1,34 +1,36 @@
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-use crate::conversion::analysis::fun::{ArgumentAnalysis, ReceiverMutability};
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use crate::{
+    conversion::analysis::fun::{ArgumentAnalysis, ReceiverMutability},
+    types::QualifiedName,
+};
 use proc_macro2::TokenStream;
 use quote::quote;
-use std::borrow::Cow;
+use std::{borrow::Cow, collections::HashSet};
 use syn::{
     parse_quote, punctuated::Punctuated, token::Comma, FnArg, GenericArgument, PatType, Path,
-    PathSegment, ReturnType, Type, TypePath,
+    PathSegment, ReturnType, Type, TypePath, TypeReference,
 };
 
 /// Function which can add explicit lifetime parameters to function signatures
 /// where necessary, based on analysis of parameters and return types.
-/// This is necessary only in one case - where the parameter is a Pin<&mut T>
-/// and the return type is some kind of reference - because lifetime elision
-/// is not smart enough to see inside a Pin.
+/// This is necessary only in two cases:
+/// 1) where the parameter is a Pin<&mut T>
+///    and the return type is some kind of reference - because lifetime elision
+///    is not smart enough to see inside a Pin.
+/// 2) as a workaround for https://github.com/dtolnay/cxx/issues/1024, where the
+///    input parameter is a non-POD type but the output reference is a POD or
+///    built-in type
 pub(crate) fn add_explicit_lifetime_if_necessary<'r>(
     param_details: &[ArgumentAnalysis],
     mut params: Punctuated<FnArg, Comma>,
     ret_type: &'r ReturnType,
+    non_pod_types: &HashSet<QualifiedName>,
 ) -> (
     Option<TokenStream>,
     Punctuated<FnArg, Comma>,
@@ -37,7 +39,11 @@
     let has_mutable_receiver = param_details
         .iter()
         .any(|pd| matches!(pd.self_type, Some((_, ReceiverMutability::Mutable))));
-    if !has_mutable_receiver {
+
+    let non_pod_ref_param = reference_parameter_is_non_pod_reference(&params, non_pod_types);
+    let ret_type_pod = return_type_is_pod_or_known_type_reference(ret_type, non_pod_types);
+    let hits_1024_bug = non_pod_ref_param && ret_type_pod;
+    if !(has_mutable_receiver || hits_1024_bug) {
         return (None, params, Cow::Borrowed(ret_type));
     }
     let new_return_type = match ret_type {
@@ -71,7 +77,8 @@
                             path: Path { segments, .. },
                             ..
                         }) => add_lifetime_to_pinned_reference(segments).unwrap(),
-                        _ => panic!("Expected Pin<T>"),
+                        Type::Reference(tyr) => add_lifetime_to_reference(tyr),
+                        _ => panic!("Expected Pin<&mut T> or &T"),
                     },
                     _ => panic!("Unexpected fnarg"),
                 }
@@ -82,6 +89,44 @@
     }
 }
 
+fn reference_parameter_is_non_pod_reference(
+    params: &Punctuated<FnArg, Comma>,
+    non_pod_types: &HashSet<QualifiedName>,
+) -> bool {
+    params.iter().any(|param| match param {
+        FnArg::Typed(PatType { ty, .. }) => match ty.as_ref() {
+            Type::Reference(TypeReference { elem, .. }) => match elem.as_ref() {
+                Type::Path(typ) => {
+                    let qn = QualifiedName::from_type_path(typ);
+                    non_pod_types.contains(&qn)
+                }
+                _ => false,
+            },
+            _ => false,
+        },
+        _ => false,
+    })
+}
+
+fn return_type_is_pod_or_known_type_reference(
+    ret_type: &ReturnType,
+    non_pod_types: &HashSet<QualifiedName>,
+) -> bool {
+    match ret_type {
+        ReturnType::Type(_, boxed_type) => match boxed_type.as_ref() {
+            Type::Reference(rtr) => match rtr.elem.as_ref() {
+                Type::Path(typ) => {
+                    let qn = QualifiedName::from_type_path(typ);
+                    !non_pod_types.contains(&qn)
+                }
+                _ => false,
+            },
+            _ => false,
+        },
+        _ => false,
+    }
+}
+
 #[derive(Debug)]
 enum AddLifetimeError {
     WasNotPin,
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/mod.rs
similarity index 85%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/mod.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/mod.rs
index 321db2b..78c48cf 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/mod.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/mod.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 mod fun_codegen;
 mod function_wrapper_rs;
@@ -24,22 +18,21 @@
 
 use autocxx_parser::IncludeCppConfig;
 
+use itertools::Itertools;
 use proc_macro2::{Span, TokenStream};
 use syn::{
     parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem,
-    ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, Pat, ReturnType, TraitItem,
+    ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, TraitItem,
 };
 
 use crate::{
     conversion::{
-        analysis::fun::MethodKind,
         codegen_rs::{
             non_pod_struct::{make_non_pod, new_non_pod_struct},
             unqualify::{unqualify_params, unqualify_ret_type},
         },
         doc_attr::get_doc_attr,
     },
-    known_types::known_types,
     types::{make_ident, Namespace, QualifiedName},
 };
 use impl_item_creator::create_impl_items;
@@ -50,20 +43,33 @@
 };
 
 use super::{
-    analysis::fun::{FnAnalysis, FnKind},
-    api::{Layout, RustSubclassFnDetails, Synthesis},
+    analysis::{
+        fun::{FnPhase, PodAndDepAnalysis, ReceiverMutability},
+        pod::PodAnalysis,
+    },
+    api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind},
+    convert_error::ErrorContextType,
+};
+use super::{
+    api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature},
+    apivec::ApiVec,
     codegen_cpp::type_to_cpp::{
         namespaced_name_using_original_name_map, original_name_map_from_apis, CppNameMap,
     },
 };
-use super::{
-    analysis::fun::{FnPhase, ReceiverMutability},
-    api::{AnalysisPhase, Api, ImplBlockDetails, SubclassName, TypeKind, TypedefKind},
-};
 use super::{convert_error::ErrorContext, ConvertError};
 use quote::{quote, ToTokens};
 
-unzip_n::unzip_n!(pub 4);
+/// An entry which needs to go into an `impl` block for a given type.
+struct ImplBlockDetails {
+    item: ImplItem,
+    ty: Ident,
+}
+
+struct TraitImplBlockDetails {
+    item: TraitItem,
+    key: TraitImplSignature,
+}
 
 /// Whether and how this item should be exposed in the mods constructed
 /// for actual end-user use.
@@ -123,16 +129,6 @@
     .to_vec()
 }
 
-struct SuperclassMethod {
-    name: Ident,
-    params: Punctuated<FnArg, Comma>,
-    param_names: Vec<Pat>,
-    ret_type: ReturnType,
-    receiver_mutability: ReceiverMutability,
-    requires_unsafe: bool,
-    is_pure_virtual: bool,
-}
-
 /// Type which handles generation of Rust code.
 /// In practice, much of the "generation" involves connecting together
 /// existing lumps of code within the Api structures.
@@ -141,32 +137,36 @@
     bindgen_mod: ItemMod,
     original_name_map: CppNameMap,
     config: &'a IncludeCppConfig,
+    header_name: Option<String>,
 }
 
 impl<'a> RsCodeGenerator<'a> {
     /// Generate code for a set of APIs that was discovered during parsing.
     pub(crate) fn generate_rs_code(
-        all_apis: Vec<Api<FnPhase>>,
+        all_apis: ApiVec<FnPhase>,
         include_list: &'a [String],
         bindgen_mod: ItemMod,
         config: &'a IncludeCppConfig,
+        header_name: Option<String>,
     ) -> Vec<Item> {
         let c = Self {
             include_list,
             bindgen_mod,
             original_name_map: original_name_map_from_apis(&all_apis),
             config,
+            header_name,
         };
         c.rs_codegen(all_apis)
     }
 
-    fn rs_codegen(mut self, all_apis: Vec<Api<FnPhase>>) -> Vec<Item> {
+    fn rs_codegen(mut self, all_apis: ApiVec<FnPhase>) -> Vec<Item> {
         // ... and now let's start to generate the output code.
         // First off, when we generate structs we may need to add some methods
         // if they're superclasses.
         let methods_by_superclass = self.accumulate_superclass_methods(&all_apis);
         let subclasses_with_a_single_trivial_constructor =
             find_trivially_constructed_subclasses(&all_apis);
+        let non_pod_types = find_non_pod_types(&all_apis);
         // Now let's generate the Rust code.
         let (rs_codegen_results_and_namespaces, additional_cpp_needs): (Vec<_>, Vec<_>) = all_apis
             .into_iter()
@@ -177,6 +177,7 @@
                     api,
                     &methods_by_superclass,
                     &subclasses_with_a_single_trivial_constructor,
+                    &non_pod_types,
                 );
                 ((name, gen), more_cpp_needed)
             })
@@ -192,18 +193,22 @@
         // sub-mods by namespace. From here on, things are flat.
         let (_, rs_codegen_results): (Vec<_>, Vec<_>) =
             rs_codegen_results_and_namespaces.into_iter().unzip();
-        let (extern_c_mod_items, extern_rust_mod_items, all_items, bridge_items) =
-            rs_codegen_results
-                .into_iter()
-                .map(|api| {
-                    (
-                        api.extern_c_mod_items,
-                        api.extern_rust_mod_items,
-                        api.global_items,
-                        api.bridge_items,
-                    )
-                })
-                .unzip_n_vec();
+        let (extern_c_mod_items, extern_rust_mod_items, all_items, bridge_items): (
+            Vec<_>,
+            Vec<_>,
+            Vec<_>,
+            Vec<_>,
+        ) = rs_codegen_results
+            .into_iter()
+            .map(|api| {
+                (
+                    api.extern_c_mod_items,
+                    api.extern_rust_mod_items,
+                    api.global_items,
+                    api.bridge_items,
+                )
+            })
+            .multiunzip();
         // Items for the [cxx::bridge] mod...
         let mut bridge_items: Vec<Item> = bridge_items.into_iter().flatten().collect();
         // Things to include in the "extern "C"" mod passed within the cxx::bridge
@@ -259,7 +264,7 @@
 
     fn accumulate_superclass_methods(
         &self,
-        apis: &[Api<FnPhase>],
+        apis: &ApiVec<FnPhase>,
     ) -> HashMap<QualifiedName, Vec<SuperclassMethod>> {
         let mut results = HashMap::new();
         results.extend(
@@ -267,39 +272,11 @@
                 .superclasses()
                 .map(|sc| (QualifiedName::new_from_cpp_name(sc), Vec::new())),
         );
-        for api in apis {
-            if let Api::Function {
-                name,
-                analysis:
-                    FnAnalysis {
-                        kind: FnKind::Method(receiver, method_kind),
-                        params,
-                        ret_type,
-                        param_details,
-                        ..
-                    },
-                ..
-            } = api
-            {
-                match method_kind {
-                    MethodKind::Virtual(receiver_mutability)
-                    | MethodKind::PureVirtual(receiver_mutability) => {
-                        let list = results.get_mut(receiver);
-                        if let Some(list) = list {
-                            let param_names =
-                                param_details.iter().map(|pd| pd.name.clone()).collect();
-                            list.push(SuperclassMethod {
-                                name: name.name.get_final_ident(),
-                                params: params.clone(),
-                                ret_type: ret_type.clone(),
-                                param_names,
-                                receiver_mutability: receiver_mutability.clone(),
-                                requires_unsafe: param_details.iter().any(|pd| pd.requires_unsafe),
-                                is_pure_virtual: matches!(method_kind, MethodKind::PureVirtual(..)),
-                            })
-                        }
-                    }
-                    _ => {} // otherwise, this is not a superclass of anything specified as a subclass!(..)
+        for api in apis.iter() {
+            if let Api::SubclassTraitItem { details, .. } = api {
+                let list = results.get_mut(&details.receiver);
+                if let Some(list) = list {
+                    list.push(details.clone());
                 }
             }
         }
@@ -316,7 +293,7 @@
 
     fn build_include_foreign_items(&self, has_additional_cpp_needs: bool) -> Vec<ForeignItem> {
         let extra_inclusion = if has_additional_cpp_needs {
-            Some(format!("autocxxgen_{}.h", self.config.get_mod_name()))
+            Some(self.header_name.clone().unwrap())
         } else {
             None
         };
@@ -412,6 +389,7 @@
         ns: &Namespace,
     ) {
         let mut impl_entries_by_type: HashMap<_, Vec<_>> = HashMap::new();
+        let mut trait_impl_entries_by_trait_and_ty: HashMap<_, Vec<_>> = HashMap::new();
         for item in ns_entries.entries() {
             output_items.extend(item.1.bindgen_mod_items.iter().cloned());
             if let Some(impl_entry) = &item.1.impl_entry {
@@ -420,6 +398,12 @@
                     .or_default()
                     .push(&impl_entry.item);
             }
+            if let Some(trait_impl_entry) = &item.1.trait_impl_entry {
+                trait_impl_entries_by_trait_and_ty
+                    .entry(trait_impl_entry.key.clone())
+                    .or_default()
+                    .push(&trait_impl_entry.item);
+            }
         }
         for (ty, entries) in impl_entries_by_type.into_iter() {
             output_items.push(Item::Impl(parse_quote! {
@@ -428,6 +412,16 @@
                 }
             }))
         }
+        for (key, entries) in trait_impl_entries_by_trait_and_ty.into_iter() {
+            let unsafety = key.unsafety;
+            let ty = key.ty;
+            let trt = key.trait_signature;
+            output_items.push(Item::Impl(parse_quote! {
+                #unsafety impl #trt for #ty {
+                    #(#entries)*
+                }
+            }))
+        }
         for (child_name, child_ns_entries) in ns_entries.children() {
             let new_ns = ns.push((*child_name).clone());
             let child_id = make_ident(child_name);
@@ -467,6 +461,7 @@
         api: Api<FnPhase>,
         associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
         subclasses_with_a_single_trivial_constructor: &HashSet<QualifiedName>,
+        non_pod_types: &HashSet<QualifiedName>,
     ) -> RsCodegenResult {
         let name = api.name().clone();
         let id = name.get_final_ident();
@@ -485,9 +480,13 @@
                     ..Default::default()
                 }
             }
-            Api::Function { fun, analysis, .. } => {
-                gen_function(name.get_namespace(), *fun, analysis, cpp_call_name)
-            }
+            Api::Function { fun, analysis, .. } => gen_function(
+                name.get_namespace(),
+                *fun,
+                analysis,
+                cpp_call_name,
+                non_pod_types,
+            ),
             Api::Const { const_item, .. } => RsCodegenResult {
                 bindgen_mod_items: vec![Item::Const(const_item)],
                 materializations: vec![Use::UsedFromBindgen],
@@ -509,8 +508,9 @@
                 self.generate_type(
                     &name,
                     id,
-                    analysis.kind,
-                    analysis.movable,
+                    analysis.pod.kind,
+                    analysis.constructors.move_constructor,
+                    analysis.constructors.destructor,
                     || Some((Item::Struct(details.item), doc_attr)),
                     associated_methods,
                     layout,
@@ -523,6 +523,7 @@
                     id,
                     TypeKind::Pod,
                     true,
+                    true,
                     || Some((Item::Enum(item), doc_attr)),
                     associated_methods,
                     None,
@@ -533,6 +534,7 @@
                 id,
                 TypeKind::Abstract,
                 false, // assume for now that these types can't be kept in a Vector
+                true,  // assume for now that these types can be put in a smart pointer
                 || None,
                 associated_methods,
                 None,
@@ -572,7 +574,12 @@
                     subclasses_with_a_single_trivial_constructor.contains(&name.0.name);
                 self.generate_subclass(name, &superclass, methods, generate_peer_constructor)
             }
-            Api::IgnoredItem { err, ctx, .. } => Self::generate_error_entry(err, ctx),
+            Api::IgnoredItem {
+                err,
+                ctx: Some(ctx),
+                ..
+            } => Self::generate_error_entry(err, ctx),
+            Api::IgnoredItem { .. } | Api::SubclassTraitItem { .. } => RsCodegenResult::default(),
         }
     }
 
@@ -635,7 +642,7 @@
                     let peer_fn = make_ident(peer_fn);
                     *(params.iter_mut().next().unwrap()) = first_param;
                     let param_names = m.param_names.iter().skip(1);
-                    let unsafe_token = get_unsafe_token(m.requires_unsafe);
+                    let unsafe_token = m.requires_unsafe.wrapper_token();
                     parse_quote! {
                         #unsafe_token fn #cpp_super_method_name(#params) #ret {
                             use autocxx::subclass::CppSubclass;
@@ -698,9 +705,10 @@
         });
         RsCodegenResult {
             extern_c_mod_items,
-            // For now we just assume we can't keep subclasses in vectors.
-            // That's the reason for the 'false'
-            bridge_items: create_impl_items(&cpp_id, false, self.config),
+            // For now we just assume we can't keep subclasses in vectors, but we can put them in
+            // smart pointers.
+            // That's the reason for the 'false' and 'true'
+            bridge_items: create_impl_items(&cpp_id, false, true, self.config),
             bindgen_mod_items,
             materializations: vec![Use::Custom(Box::new(parse_quote! {
                 pub use cxxbridge::#cpp_id;
@@ -725,7 +733,7 @@
     ) -> RsCodegenResult {
         let params = details.params;
         let ret = details.ret;
-        let unsafe_token = get_unsafe_token(details.requires_unsafe);
+        let unsafe_token = details.requires_unsafe.wrapper_token();
         let global_def = quote! { #unsafe_token fn #api_name(#params) #ret };
         let params = unqualify_params(params);
         let ret = unqualify_ret_type(ret);
@@ -789,6 +797,7 @@
         id: Ident,
         type_kind: TypeKind,
         movable: bool,
+        destroyable: bool,
         item_creator: F,
         associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
         layout: Option<Layout>,
@@ -835,7 +844,7 @@
                 bindgen_mod_items.push(item);
                 RsCodegenResult {
                     global_items: self.generate_extern_type_impl(type_kind, name),
-                    bridge_items: create_impl_items(&id, movable, self.config),
+                    bridge_items: create_impl_items(&id, movable, destroyable, self.config),
                     extern_c_mod_items: vec![self.generate_cxxbridge_type(name, true, None)],
                     bindgen_mod_items,
                     materializations,
@@ -880,7 +889,7 @@
                         ReceiverMutability::Mutable => parse_quote!(&mut self),
                     };
                     let ret_type = &method.ret_type;
-                    let unsafe_token = get_unsafe_token(method.requires_unsafe);
+                    let unsafe_token = method.requires_unsafe.wrapper_token();
                     if method.is_pure_virtual {
                         (
                             None,
@@ -935,67 +944,43 @@
     /// generated.
     fn generate_error_entry(err: ConvertError, ctx: ErrorContext) -> RsCodegenResult {
         let err = format!("autocxx bindings couldn't be generated: {}", err);
-        let (impl_entry, materialization) = match ctx {
-            ErrorContext::Item(id) => {
-                let id = Self::sanitize_error_ident(&id).unwrap_or(id);
-                (
-                    None,
-                    Some(Use::Custom(Box::new(parse_quote! {
+        let (impl_entry, bindgen_mod_item, materialization) = match ctx.into_type() {
+            ErrorContextType::Item(id) => (
+                // Populate within bindgen mod because impl blocks may attach.
+                None,
+                Some(parse_quote! {
+                    #[doc = #err]
+                    pub struct #id;
+                }),
+                Some(Use::SpecificNameFromBindgen(id)),
+            ),
+            ErrorContextType::SanitizedItem(id) => (
+                // Guaranteed to be no impl blocks - populate directly in output mod.
+                None,
+                None,
+                Some(Use::Custom(Box::new(parse_quote! {
+                    #[doc = #err]
+                    pub struct #id;
+                }))),
+            ),
+            ErrorContextType::Method { self_ty, method } => (
+                Some(Box::new(ImplBlockDetails {
+                    item: parse_quote! {
                         #[doc = #err]
-                        pub struct #id;
-                    }))),
-                )
-            }
-            ErrorContext::Method { self_ty, method }
-                if Self::sanitize_error_ident(&self_ty).is_none() =>
-            {
-                // This could go wrong if a separate error has caused
-                // us to drop an entire type as well as one of its individual items.
-                // Then we'll be applying an impl to a thing which doesn't exist. TODO.
-                let method = Self::sanitize_error_ident(&method).unwrap_or(method);
-                (
-                    Some(Box::new(ImplBlockDetails {
-                        item: parse_quote! {
-                            #[doc = #err]
-                            fn #method(_uhoh: autocxx::BindingGenerationFailure) {
-                            }
-                        },
-                        ty: self_ty,
-                    })),
-                    None,
-                )
-            }
-            ErrorContext::Method { self_ty, method } => {
-                // If the type can't be represented (e.g. u8) this would get fiddly.
-                let id = make_ident(format!("{}_method_{}", self_ty, method));
-                (
-                    None,
-                    Some(Use::Custom(Box::new(parse_quote! {
-                        #[doc = #err]
-                        pub struct #id;
-                    }))),
-                )
-            }
+                        fn #method(_uhoh: autocxx::BindingGenerationFailure) {
+                        }
+                    },
+                    ty: self_ty,
+                })),
+                None,
+                None,
+            ),
         };
         RsCodegenResult {
-            global_items: Vec::new(),
             impl_entry,
-            bridge_items: Vec::new(),
-            extern_c_mod_items: Vec::new(),
-            bindgen_mod_items: Vec::new(),
+            bindgen_mod_items: bindgen_mod_item.into_iter().collect(),
             materializations: materialization.into_iter().collect(),
-            extern_rust_mod_items: Vec::new(),
-        }
-    }
-
-    /// Because errors may be generated for invalid types or identifiers,
-    /// we may need to scrub the name
-    fn sanitize_error_ident(id: &Ident) -> Option<Ident> {
-        let qn = QualifiedName::new(&Namespace::new(), id.clone());
-        if known_types().conflicts_with_built_in_type(&qn) {
-            Some(make_ident(format!("{}_autocxx_error", qn.get_final_item())))
-        } else {
-            None
+            ..Default::default()
         }
     }
 
@@ -1101,29 +1086,19 @@
     }
 }
 
-fn get_unsafe_token(requires_unsafe: bool) -> TokenStream {
-    if requires_unsafe {
-        quote! { unsafe }
-    } else {
-        quote! {}
-    }
-}
-
-fn find_trivially_constructed_subclasses(apis: &[Api<FnPhase>]) -> HashSet<QualifiedName> {
+fn find_trivially_constructed_subclasses(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
     let (simple_constructors, complex_constructors): (Vec<_>, Vec<_>) = apis
         .iter()
         .filter_map(|api| match api {
-            Api::Function { fun, .. } => match &fun.synthesis {
-                Some(Synthesis::SubclassConstructor {
-                    subclass,
-                    is_trivial,
-                    ..
-                }) => Some((&subclass.0.name, is_trivial)),
+            Api::Function { fun, .. } => match &fun.provenance {
+                Provenance::SynthesizedSubclassConstructor(details) => {
+                    Some((&details.subclass.0.name, details.is_trivial))
+                }
                 _ => None,
             },
             _ => None,
         })
-        .partition(|(_, trivial)| **trivial);
+        .partition(|(_, trivial)| *trivial);
     let simple_constructors: HashSet<_> =
         simple_constructors.into_iter().map(|(qn, _)| qn).collect();
     let complex_constructors: HashSet<_> =
@@ -1134,6 +1109,27 @@
         .collect()
 }
 
+fn find_non_pod_types(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
+    apis.iter()
+        .filter_map(|api| match api {
+            Api::Struct {
+                name,
+                analysis:
+                    PodAndDepAnalysis {
+                        pod:
+                            PodAnalysis {
+                                kind: TypeKind::NonPod,
+                                ..
+                            },
+                        ..
+                    },
+                ..
+            } => Some(name.name.clone()),
+            _ => None,
+        })
+        .collect()
+}
+
 impl HasNs for (QualifiedName, RsCodegenResult) {
     fn get_namespace(&self) -> &Namespace {
         self.0.get_namespace()
@@ -1156,5 +1152,6 @@
     global_items: Vec<Item>,
     bindgen_mod_items: Vec<Item>,
     impl_entry: Option<Box<ImplBlockDetails>>,
+    trait_impl_entry: Option<Box<TraitImplBlockDetails>>,
     materializations: Vec<Use>,
 }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/namespace_organizer.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/namespace_organizer.rs
similarity index 87%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/namespace_organizer.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/namespace_organizer.rs
index f88a040f..886cb1c 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/namespace_organizer.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/namespace_organizer.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::types::Namespace;
 use std::collections::BTreeMap;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/non_pod_struct.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/non_pod_struct.rs
similarity index 88%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/non_pod_struct.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/non_pod_struct.rs
index d1415ee..e4bec2f 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/non_pod_struct.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/non_pod_struct.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::conversion::api::Layout;
 use crate::types::make_ident;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/unqualify.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/unqualify.rs
similarity index 82%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/unqualify.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/unqualify.rs
index b6b91667..6f245c2 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/codegen_rs/unqualify.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/codegen_rs/unqualify.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use syn::{
     parse_quote, punctuated::Punctuated, FnArg, GenericArgument, PathArguments, PathSegment,
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/conversion_tests.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/conversion_tests.rs
similarity index 66%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/conversion_tests.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/conversion_tests.rs
index 35702f4..b0474d6 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/conversion_tests.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/conversion_tests.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use autocxx_parser::UnsafePolicy;
 #[allow(unused_imports)]
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/convert_error.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/convert_error.rs
similarity index 68%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/convert_error.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/convert_error.rs
index 12e99bb..f55303f2 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/convert_error.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/convert_error.rs
@@ -1,22 +1,21 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
+use std::collections::HashSet;
 use std::fmt::Display;
 
+use itertools::Itertools;
 use syn::Ident;
 
-use crate::types::{Namespace, QualifiedName};
+use crate::{
+    known_types,
+    types::{make_ident, Namespace, QualifiedName},
+};
 
 #[derive(Debug, Clone)]
 pub enum ConvertError {
@@ -44,7 +43,7 @@
     UnusedTemplateParam,
     TooManyUnderscores,
     UnknownDependentType(QualifiedName),
-    IgnoredDependent,
+    IgnoredDependent(HashSet<QualifiedName>),
     ReservedName(String),
     DuplicateCxxBridgeName,
     UnsupportedReceiver,
@@ -58,6 +57,10 @@
     AssignmentOperator,
     Deleted,
     RValueReferenceField,
+    MethodOfNonAllowlistedType,
+    MethodOfGenericType,
+    DuplicateItemsFoundInParsing,
+    ConstructorWithOnlyOneParam,
 }
 
 fn format_maybe_identifier(id: &Option<Ident>) -> String {
@@ -94,7 +97,7 @@
             ConvertError::UnusedTemplateParam => write!(f, "This function or method uses a type where one of the template parameters was incomprehensible to bindgen/autocxx - probably because it uses template specialization.")?,
             ConvertError::TooManyUnderscores => write!(f, "Names containing __ are reserved by C++ so not acceptable to cxx")?,
             ConvertError::UnknownDependentType(qn) => write!(f, "This item relies on a type not known to autocxx ({})", qn.to_cpp_name())?,
-            ConvertError::IgnoredDependent => write!(f, "This item depends on some other type which autocxx could not generate.")?,
+            ConvertError::IgnoredDependent(qns) => write!(f, "This item depends on some other type(s) which autocxx could not generate, some of them are: {}", qns.iter().join(", "))?,
             ConvertError::ReservedName(id) => write!(f, "The item name '{}' is a reserved word in Rust.", id)?,
             ConvertError::DuplicateCxxBridgeName => write!(f, "This item name is used in multiple namespaces. At present, autocxx and cxx allow only one type of a given name. This limitation will be fixed in future.")?,
             ConvertError::UnsupportedReceiver => write!(f, "This is a method on a type which can't be used as the receiver in Rust (i.e. self/this). This is probably because some type involves template specialization.")?,
@@ -108,32 +111,86 @@
             ConvertError::AssignmentOperator => write!(f, "autocxx does not know how to generate bindings to operator=")?,
             ConvertError::Deleted => write!(f, "This function was marked =delete")?,
             ConvertError::RValueReferenceField => write!(f, "This structure has an rvalue reference field (&&) which is not yet supported.")?,
+            ConvertError::MethodOfNonAllowlistedType => write!(f, "This type was not on the allowlist, so we are not generating methods for it.")?,
+            ConvertError::MethodOfGenericType => write!(f, "This type is templated, so we can't generate bindings. We will instead generate bindings for each instantiation.")?,
+            ConvertError::DuplicateItemsFoundInParsing => write!(f, "bindgen generated multiple different APIs (functions/types) with this name. autocxx doesn't know how to diambiguate them, so we won't generate bindings for any of them.")?,
+            ConvertError::ConstructorWithOnlyOneParam => write!(f, "bindgen generated a move or copy constructor with an unexpected number of parameters.")?,
         }
         Ok(())
     }
 }
 
+/// Ensures that error contexts are always created using the constructors in this
+/// mod, therefore undergoing identifier sanitation.
 #[derive(Clone)]
-pub(crate) enum ErrorContext {
+struct PhantomSanitized;
+
+/// The context of an error, e.g. whether it applies to a function or a method.
+/// This is used to generate suitable rustdoc in the output codegen so that
+/// the errors can be revealed in rust-analyzer-based IDEs, etc.
+#[derive(Clone)]
+pub(crate) struct ErrorContext(ErrorContextType, PhantomSanitized);
+
+/// All idents in this structure are guaranteed to be something we can safely codegen for.
+#[derive(Clone)]
+pub(crate) enum ErrorContextType {
     Item(Ident),
+    SanitizedItem(Ident),
     Method { self_ty: Ident, method: Ident },
 }
 
 impl ErrorContext {
-    /// Return the ID in the output mod with which this should be associated
-    pub(crate) fn get_id(&self) -> &Ident {
-        match self {
-            ErrorContext::Item(id) => id,
-            ErrorContext::Method { self_ty, method: _ } => self_ty,
+    pub(crate) fn new_for_item(id: Ident) -> Self {
+        match Self::sanitize_error_ident(&id) {
+            None => Self(ErrorContextType::Item(id), PhantomSanitized),
+            Some(sanitized) => Self(ErrorContextType::SanitizedItem(sanitized), PhantomSanitized),
         }
     }
+
+    pub(crate) fn new_for_method(self_ty: Ident, method: Ident) -> Self {
+        // If this IgnoredItem relates to a method on a self_ty which we can't represent,
+        // e.g. u8, then forget about trying to attach this error text to something within
+        // an impl block.
+        match Self::sanitize_error_ident(&self_ty) {
+            None => Self(
+                ErrorContextType::Method {
+                    self_ty,
+                    method: Self::sanitize_error_ident(&method).unwrap_or(method),
+                },
+                PhantomSanitized,
+            ),
+            Some(_) => Self(
+                ErrorContextType::SanitizedItem(make_ident(format!("{}_{}", self_ty, method))),
+                PhantomSanitized,
+            ),
+        }
+    }
+
+    /// Because errors may be generated for invalid types or identifiers,
+    /// we may need to scrub the name
+    fn sanitize_error_ident(id: &Ident) -> Option<Ident> {
+        let qn = QualifiedName::new(&Namespace::new(), id.clone());
+        if known_types().conflicts_with_built_in_type(&qn) {
+            Some(make_ident(format!("{}_autocxx_error", qn.get_final_item())))
+        } else {
+            None
+        }
+    }
+
+    pub(crate) fn get_type(&self) -> &ErrorContextType {
+        &self.0
+    }
+
+    pub(crate) fn into_type(self) -> ErrorContextType {
+        self.0
+    }
 }
 
 impl std::fmt::Display for ErrorContext {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            ErrorContext::Item(id) => write!(f, "{}", id),
-            ErrorContext::Method { self_ty, method } => write!(f, "{}::{}", self_ty, method),
+        match &self.0 {
+            ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => write!(f, "{}", id),
+            ErrorContextType::Method { self_ty, method } => write!(f, "{}::{}", self_ty, method),
         }
     }
 }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/doc_attr.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/doc_attr.rs
new file mode 100644
index 0000000..685207e7
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/doc_attr.rs
@@ -0,0 +1,17 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use syn::Attribute;
+
+/// Returns the attribute (if any) which contains a doc comment.
+pub(super) fn get_doc_attr(attrs: &[Attribute]) -> Option<Attribute> {
+    attrs
+        .iter()
+        .find(|a| a.path.get_ident().iter().any(|p| *p == "doc"))
+        .cloned()
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/error_reporter.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/error_reporter.rs
similarity index 74%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/error_reporter.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/error_reporter.rs
index 2dbd558..c68f13da 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/error_reporter.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/error_reporter.rs
@@ -1,32 +1,30 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use syn::ItemEnum;
 
 use super::{
     api::{AnalysisPhase, Api, ApiName, FuncToConvert, StructDetails, TypedefKind},
+    apivec::ApiVec,
     convert_error::{ConvertErrorWithContext, ErrorContext},
     ConvertError,
 };
-use crate::types::{Namespace, QualifiedName};
+use crate::{
+    conversion::convert_error::ErrorContextType,
+    types::{Namespace, QualifiedName},
+};
 
 /// Run some code which may generate a ConvertError.
 /// If it does, try to note the problem in our output APIs
 /// such that users will see documentation of the error.
 pub(crate) fn report_any_error<F, T>(
     ns: &Namespace,
-    apis: &mut Vec<Api<impl AnalysisPhase>>,
+    apis: &mut ApiVec<impl AnalysisPhase>,
     fun: F,
 ) -> Option<T>
 where
@@ -40,7 +38,16 @@
         }
         Err(ConvertErrorWithContext(err, Some(ctx))) => {
             eprintln!("Ignored item {}: {}", ctx, err);
-            apis.push(ignored_item(ns, ctx, err));
+            let id = match ctx.get_type() {
+                ErrorContextType::Item(id) | ErrorContextType::SanitizedItem(id) => id,
+                ErrorContextType::Method { self_ty, .. } => self_ty,
+            };
+            let name = ApiName::new_from_qualified_name(QualifiedName::new(ns, id.clone()));
+            apis.push(Api::IgnoredItem {
+                name,
+                err,
+                ctx: Some(ctx),
+            });
             None
         }
     }
@@ -50,8 +57,8 @@
 /// anything goes wrong, instead add a note of the problem in our
 /// output API such that users will see documentation for the problem.
 pub(crate) fn convert_apis<FF, SF, EF, TF, A, B: 'static>(
-    in_apis: Vec<Api<A>>,
-    out_apis: &mut Vec<Api<B>>,
+    in_apis: ApiVec<A>,
+    out_apis: &mut ApiVec<B>,
     mut func_conversion: FF,
     mut struct_conversion: SF,
     mut enum_conversion: EF,
@@ -63,7 +70,6 @@
         ApiName,
         Box<FuncToConvert>,
         A::FunAnalysis,
-        Option<QualifiedName>,
     ) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorWithContext>,
     SF: FnMut(
         ApiName,
@@ -81,8 +87,8 @@
         A::TypedefAnalysis,
     ) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorWithContext>,
 {
-    out_apis.extend(&mut in_apis.into_iter().flat_map(|api| {
-        let tn = api.name().clone();
+    out_apis.extend(in_apis.into_iter().flat_map(|api| {
+        let fullname = api.name_info().clone();
         let result: Result<Box<dyn Iterator<Item = Api<B>>>, ConvertErrorWithContext> = match api {
             // No changes to any of these...
             Api::ConcreteType {
@@ -132,6 +138,12 @@
                     ctx,
                 })))
             }
+            Api::SubclassTraitItem { name, details } => {
+                Ok(Box::new(std::iter::once(Api::SubclassTraitItem {
+                    name,
+                    details,
+                })))
+            }
             // Apply a mapping to the following
             Api::Enum { name, item } => enum_conversion(name, item),
             Api::Typedef {
@@ -144,35 +156,26 @@
                 name,
                 fun,
                 analysis,
-                name_for_gc,
-            } => func_conversion(name, fun, analysis, name_for_gc),
+            } => func_conversion(name, fun, analysis),
             Api::Struct {
                 name,
                 details,
                 analysis,
             } => struct_conversion(name, details, analysis),
         };
-        api_or_error(tn, result)
+        api_or_error(fullname, result)
     }))
 }
 
 fn api_or_error<T: AnalysisPhase + 'static>(
-    name: QualifiedName,
+    name: ApiName,
     api_or_error: Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext>,
 ) -> Box<dyn Iterator<Item = Api<T>>> {
     match api_or_error {
         Ok(opt) => opt,
-        Err(ConvertErrorWithContext(err, None)) => {
-            eprintln!("Ignored {}: {}", name, err);
-            Box::new(std::iter::empty())
-        }
-        Err(ConvertErrorWithContext(err, Some(ctx))) => {
-            eprintln!("Ignored {}: {}", name, err);
-            Box::new(std::iter::once(ignored_item(
-                name.get_namespace(),
-                ctx,
-                err,
-            )))
+        Err(ConvertErrorWithContext(err, ctx)) => {
+            eprintln!("Ignored {}: {}", name.cpp_name(), err);
+            Box::new(std::iter::once(Api::IgnoredItem { name, err, ctx }))
         }
     }
 }
@@ -182,8 +185,8 @@
 /// anything goes wrong, instead add a note of the problem in our
 /// output API such that users will see documentation for the problem.
 pub(crate) fn convert_item_apis<F, A, B: 'static>(
-    in_apis: Vec<Api<A>>,
-    out_apis: &mut Vec<Api<B>>,
+    in_apis: ApiVec<A>,
+    out_apis: &mut ApiVec<B>,
     mut fun: F,
 ) where
     F: FnMut(Api<A>) -> Result<Box<dyn Iterator<Item = Api<B>>>, ConvertError>,
@@ -191,18 +194,11 @@
     B: AnalysisPhase,
 {
     out_apis.extend(in_apis.into_iter().flat_map(|api| {
+        let fullname = api.name_info().clone();
         let tn = api.name().clone();
         let result = fun(api).map_err(|e| {
-            ConvertErrorWithContext(e, Some(ErrorContext::Item(tn.get_final_ident())))
+            ConvertErrorWithContext(e, Some(ErrorContext::new_for_item(tn.get_final_ident())))
         });
-        api_or_error(tn, result)
+        api_or_error(fullname, result)
     }))
 }
-
-fn ignored_item<A: AnalysisPhase>(ns: &Namespace, ctx: ErrorContext, err: ConvertError) -> Api<A> {
-    Api::IgnoredItem {
-        name: ApiName::new(ns, ctx.get_id().clone()),
-        err,
-        ctx,
-    }
-}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/mod.rs
similarity index 81%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/mod.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/mod.rs
index 8745fdf..124b464a 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/mod.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/mod.rs
@@ -1,19 +1,14 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 mod analysis;
 mod api;
+mod apivec;
 mod codegen_cpp;
 mod codegen_rs;
 #[cfg(test)]
@@ -31,20 +26,25 @@
 use itertools::Itertools;
 use syn::{Item, ItemMod};
 
-use crate::{CppCodegenOptions, CppFilePair, UnsafePolicy};
+use crate::{
+    conversion::analysis::deps::HasDependencies, CppCodegenOptions, CppFilePair, UnsafePolicy,
+};
 
 use self::{
     analysis::{
         abstract_types::{discard_ignored_functions, mark_types_abstract},
+        allocators::create_alloc_and_frees,
         casts::add_casts,
         check_names,
+        constructor_deps::decorate_types_with_constructor_deps,
         fun::FnPhase,
         gc::filter_apis_by_following_edges_from_allowlist,
         pod::analyze_pod_apis,
         remove_ignored::filter_apis_by_ignored_dependents,
         tdef::convert_typedef_targets,
     },
-    api::{AnalysisPhase, Api},
+    api::AnalysisPhase,
+    apivec::ApiVec,
     codegen_rs::RsCodeGenerator,
     parse::ParseBindgen,
 };
@@ -82,23 +82,27 @@
         }
     }
 
-    fn dump_apis<T: AnalysisPhase>(label: &str, apis: &[Api<T>]) {
+    fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) {
         if LOG_APIS {
             log::info!(
                 "APIs after {}:\n{}",
                 label,
-                apis.iter().map(|api| { format!("  {:?}", api) }).join("\n")
+                apis.iter()
+                    .map(|api| { format!("  {:?}", api) })
+                    .sorted()
+                    .join("\n")
             )
         }
     }
 
-    fn dump_apis_with_deps(label: &str, apis: &[Api<FnPhase>]) {
+    fn dump_apis_with_deps(label: &str, apis: &ApiVec<FnPhase>) {
         if LOG_APIS {
             log::info!(
                 "APIs after {}:\n{}",
                 label,
                 apis.iter()
                     .map(|api| { format!("  {:?}, deps={}", api, api.format_deps()) })
+                    .sorted()
                     .join("\n")
             )
         }
@@ -142,6 +146,7 @@
                 let analyzed_apis = analyze_pod_apis(apis, self.config)?;
                 Self::dump_apis("pod analysis", &analyzed_apis);
                 let analyzed_apis = add_casts(analyzed_apis);
+                let analyzed_apis = create_alloc_and_frees(analyzed_apis);
                 // Next, figure out how we materialize different functions.
                 // Some will be simple entries in the cxx::bridge module; others will
                 // require C++ wrapper functions. This is probably the most complex
@@ -153,9 +158,13 @@
                 // If any of those functions turned out to be pure virtual, don't attempt
                 // to generate UniquePtr implementations for the type, since it can't
                 // be instantiated.
-                Self::dump_apis_with_deps("analyze fns", &analyzed_apis);
-                let analyzed_apis = mark_types_abstract(self.config, analyzed_apis);
-                Self::dump_apis_with_deps("marking abstract", &analyzed_apis);
+                Self::dump_apis("analyze fns", &analyzed_apis);
+                let analyzed_apis = mark_types_abstract(analyzed_apis);
+                Self::dump_apis("marking abstract", &analyzed_apis);
+                // Annotate structs with a note of any copy/move constructors which
+                // we may want to retain to avoid garbage collecting them later.
+                let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
+                Self::dump_apis_with_deps("adding constructor deps", &analyzed_apis);
                 let analyzed_apis = discard_ignored_functions(analyzed_apis);
                 Self::dump_apis_with_deps("ignoring ignorable fns", &analyzed_apis);
                 // Remove any APIs whose names are not compatible with cxx.
@@ -186,6 +195,7 @@
                     self.include_list,
                     bindgen_mod,
                     self.config,
+                    cpp.as_ref().map(|file_pair| file_pair.header_name.clone()),
                 );
                 Ok(CodegenResults { rs, cpp })
             }
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/bindgen_semantic_attributes.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/bindgen_semantic_attributes.rs
similarity index 90%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/bindgen_semantic_attributes.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/bindgen_semantic_attributes.rs
index d2e739f..8b789ae 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/bindgen_semantic_attributes.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/bindgen_semantic_attributes.rs
@@ -1,16 +1,10 @@
 // Copyright 2022 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use proc_macro2::{Ident, TokenStream};
 use syn::{
@@ -65,7 +59,7 @@
         if self.has_attr("unused_template_param") {
             Err(ConvertErrorWithContext(
                 ConvertError::UnusedTemplateParam,
-                Some(ErrorContext::Item(id_for_context.clone())),
+                Some(ErrorContext::new_for_item(id_for_context.clone())),
             ))
         } else {
             Ok(())
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/mod.rs
new file mode 100644
index 0000000..3f42ce425
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/mod.rs
@@ -0,0 +1,14 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod bindgen_semantic_attributes;
+mod parse_bindgen;
+mod parse_foreign_mod;
+
+pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes;
+pub(crate) use parse_bindgen::ParseBindgen;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_bindgen.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_bindgen.rs
similarity index 92%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_bindgen.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_bindgen.rs
index 3692cb05..c56e4ae 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_bindgen.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_bindgen.rs
@@ -1,22 +1,17 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::collections::HashSet;
 
 use crate::{
     conversion::{
-        api::{Api, ApiName, StructDetails, SubclassName, TypedefKind, UnanalyzedApi},
+        api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi},
+        apivec::ApiVec,
         ConvertError,
     },
     types::Namespace,
@@ -41,7 +36,7 @@
 /// Parses a bindgen mod in order to understand the APIs within it.
 pub(crate) struct ParseBindgen<'a> {
     config: &'a IncludeCppConfig,
-    apis: Vec<UnanalyzedApi>,
+    apis: ApiVec<NullPhase>,
 }
 
 fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName {
@@ -55,7 +50,7 @@
 ) -> Result<ApiName, ConvertErrorWithContext> {
     match validate_ident_ok_for_cxx(&id.to_string()) {
         Err(e) => {
-            let ctx = ErrorContext::Item(id);
+            let ctx = ErrorContext::new_for_item(id);
             Err(ConvertErrorWithContext(e, Some(ctx)))
         }
         Ok(..) => Ok(api_name(ns, id, attrs)),
@@ -66,7 +61,7 @@
     pub(crate) fn new(config: &'a IncludeCppConfig) -> Self {
         ParseBindgen {
             config,
-            apis: Vec::new(),
+            apis: ApiVec::new(),
         }
     }
 
@@ -75,7 +70,7 @@
     pub(crate) fn parse_items(
         mut self,
         items: Vec<Item>,
-    ) -> Result<Vec<UnanalyzedApi>, ConvertError> {
+    ) -> Result<ApiVec<NullPhase>, ConvertError> {
         let items = Self::find_items_in_root(items)?;
         if !self.config.exclude_utilities() {
             generate_utilities(&mut self.apis, self.config);
@@ -137,7 +132,7 @@
         // This object maintains some state specific to this namespace, i.e.
         // this particular mod.
         let mut mod_converter = ParseForeignMod::new(ns.clone());
-        let mut more_apis = Vec::new();
+        let mut more_apis = ApiVec::new();
         for item in items {
             report_any_error(&ns, &mut more_apis, || {
                 self.parse_item(item, &mut mod_converter, &ns)
@@ -253,7 +248,7 @@
                             if new_tyname == old_tyname {
                                 return Err(ConvertErrorWithContext(
                                     ConvertError::InfinitelyRecursiveTypedef(new_tyname),
-                                    Some(ErrorContext::Item(new_id.clone())),
+                                    Some(ErrorContext::new_for_item(new_id.clone())),
                                 ));
                             }
                             let annotations = BindgenSemanticAttributes::new(&use_item.attrs);
@@ -287,6 +282,8 @@
             }
             Item::Type(ity) => {
                 let annotations = BindgenSemanticAttributes::new(&ity.attrs);
+                // It's known that sometimes bindgen will give us duplicate typedefs with the
+                // same name - see test_issue_264.
                 self.apis.push(UnanalyzedApi::Typedef {
                     name: api_name(ns, ity.ident.clone(), &annotations),
                     item: TypedefKind::Type(ity),
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_foreign_mod.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_foreign_mod.rs
similarity index 86%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_foreign_mod.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_foreign_mod.rs
index e2df889f..3f7855f 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/conversion/parse/parse_foreign_mod.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/parse/parse_foreign_mod.rs
@@ -1,18 +1,13 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
-use crate::conversion::api::ApiName;
+use crate::conversion::api::{ApiName, NullPhase, Provenance};
+use crate::conversion::apivec::ApiVec;
 use crate::conversion::doc_attr::get_doc_attr;
 use crate::conversion::error_reporter::report_any_error;
 use crate::conversion::{
@@ -44,7 +39,7 @@
     // may actually be methods (static or otherwise). Mapping from
     // function name to type name.
     method_receivers: HashMap<Ident, QualifiedName>,
-    ignored_apis: Vec<UnanalyzedApi>,
+    ignored_apis: ApiVec<NullPhase>,
 }
 
 impl ParseForeignMod {
@@ -53,14 +48,14 @@
             ns,
             funcs_to_convert: Vec::new(),
             method_receivers: HashMap::new(),
-            ignored_apis: Vec::new(),
+            ignored_apis: ApiVec::new(),
         }
     }
 
     /// Record information from foreign mod items encountered
     /// in bindgen output.
     pub(crate) fn convert_foreign_mod_items(&mut self, foreign_mod_items: Vec<ForeignItem>) {
-        let mut extra_apis = Vec::new();
+        let mut extra_apis = ApiVec::new();
         for i in foreign_mod_items {
             report_any_error(&self.ns.clone(), &mut extra_apis, || {
                 self.parse_foreign_item(i)
@@ -75,6 +70,7 @@
                 let annotations = BindgenSemanticAttributes::new(&item.attrs);
                 let doc_attr = get_doc_attr(&item.attrs);
                 self.funcs_to_convert.push(FuncToConvert {
+                    provenance: Provenance::Bindgen,
                     self_ty: None,
                     ident: item.sig.ident,
                     doc_attr,
@@ -89,14 +85,15 @@
                     references: annotations.get_reference_parameters_and_return(),
                     original_name: annotations.get_original_name(),
                     synthesized_this_type: None,
-                    synthesis: None,
+                    add_to_trait: None,
                     is_deleted: annotations.has_attr("deleted"),
+                    synthetic_cpp: None,
                 });
                 Ok(())
             }
             ForeignItem::Static(item) => Err(ConvertErrorWithContext(
                 ConvertError::StaticData(item.ident.to_string()),
-                Some(ErrorContext::Item(item.ident)),
+                Some(ErrorContext::new_for_item(item.ident)),
             )),
             _ => Err(ConvertErrorWithContext(
                 ConvertError::UnexpectedForeignItem,
@@ -129,7 +126,7 @@
     /// Indicate that all foreign mods and all impl blocks have been
     /// fed into us, and we should process that information to generate
     /// the resulting APIs.
-    pub(crate) fn finished(mut self, apis: &mut Vec<UnanalyzedApi>) {
+    pub(crate) fn finished(mut self, apis: &mut ApiVec<NullPhase>) {
         apis.append(&mut self.ignored_apis);
         while !self.funcs_to_convert.is_empty() {
             let mut fun = self.funcs_to_convert.remove(0);
@@ -142,7 +139,6 @@
                 ),
                 fun: Box::new(fun),
                 analysis: (),
-                name_for_gc: None,
             })
         }
     }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/utilities.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/utilities.rs
new file mode 100644
index 0000000..b90b3659
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/conversion/utilities.rs
@@ -0,0 +1,30 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use autocxx_parser::IncludeCppConfig;
+
+use super::{
+    api::{ApiName, NullPhase, UnanalyzedApi},
+    apivec::ApiVec,
+};
+use crate::types::{make_ident, Namespace};
+
+/// Adds items which we always add, cos they're useful.
+/// Any APIs or techniques which do not involve actual C++ interop
+/// shouldn't go here, but instead should go into the main autocxx
+/// src/lib.rs.
+pub(crate) fn generate_utilities(apis: &mut ApiVec<NullPhase>, config: &IncludeCppConfig) {
+    // Unless we've been specifically asked not to do so, we always
+    // generate a 'make_string' function. That pretty much *always* means
+    // we run two passes through bindgen. i.e. the next 'if' is always true,
+    // and we always generate an additional C++ file for our bindings additions,
+    // unless the include_cpp macro has specified ExcludeUtilities.
+    apis.push(UnanalyzedApi::StringConstructor {
+        name: ApiName::new(&Namespace::new(), make_ident(config.get_makestring_name())),
+    });
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/cxxbridge.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/cxxbridge.rs
similarity index 65%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/cxxbridge.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/cxxbridge.rs
index f979f6d..55aba29 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/cxxbridge.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/cxxbridge.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use proc_macro2::TokenStream;
 use quote::{ToTokens, TokenStreamExt};
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/known_types.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/known_types.rs
similarity index 83%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/known_types.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/known_types.rs
index b0e866d..f6a3a16 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/known_types.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/known_types.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::{
     conversion::ConvertError,
@@ -43,10 +37,12 @@
     rs_name: String,
     /// C++ equivalent name for a Rust type.
     cpp_name: String,
-    //// The behavior of the type.
+    /// The behavior of the type.
     behavior: Behavior,
     /// Any extra non-canonical names
     extra_non_canonical_name: Option<String>,
+    has_const_copy_constructor: bool,
+    has_move_constructor: bool,
 }
 
 impl TypeDetails {
@@ -55,12 +51,16 @@
         cpp_name: impl Into<String>,
         behavior: Behavior,
         extra_non_canonical_name: Option<String>,
+        has_const_copy_constructor: bool,
+        has_move_constructor: bool,
     ) -> Self {
         TypeDetails {
             rs_name: rs_name.into(),
             cpp_name: cpp_name.into(),
             behavior,
             extra_non_canonical_name,
+            has_const_copy_constructor,
+            has_move_constructor,
         }
     }
 
@@ -153,6 +153,11 @@
     Rust,
 }
 
+pub struct KnownTypeConstructorDetails {
+    pub has_move_constructor: bool,
+    pub has_const_copy_constructor: bool,
+}
+
 impl TypeDatabase {
     fn get(&self, ty: &QualifiedName) -> Option<&TypeDetails> {
         // The following line is important. It says that
@@ -180,13 +185,16 @@
         )
     }
 
+    /// Returns all known types.
+    pub(crate) fn all_names(&self) -> impl Iterator<Item = &QualifiedName> {
+        self.canonical_names.keys().chain(self.by_rs_name.keys())
+    }
+
     /// Types which are known to be safe (or unsafe) to hold and pass by
     /// value in Rust.
     pub(crate) fn get_pod_safe_types(&self) -> impl Iterator<Item = (QualifiedName, bool)> {
         let pod_safety = self
-            .canonical_names
-            .keys()
-            .chain(self.by_rs_name.keys())
+            .all_names()
             .map(|tn| {
                 (
                     tn.clone(),
@@ -208,6 +216,16 @@
         pod_safety.into_iter()
     }
 
+    pub(crate) fn get_constructor_details(
+        &self,
+        qn: &QualifiedName,
+    ) -> Option<KnownTypeConstructorDetails> {
+        self.get(qn).map(|x| KnownTypeConstructorDetails {
+            has_move_constructor: x.has_move_constructor,
+            has_const_copy_constructor: x.has_const_copy_constructor,
+        })
+    }
+
     /// Whether this TypePath should be treated as a value in C++
     /// but a reference in Rust. This only applies to rust::Str
     /// (C++ name) which is &str in Rust.
@@ -217,6 +235,18 @@
             .unwrap_or(false)
     }
 
+    /// Whether this can only be passed around using `std::move`
+    pub(crate) fn lacks_copy_constructor(&self, tn: &QualifiedName) -> bool {
+        self.get(tn)
+            .map(|td| {
+                matches!(
+                    td.behavior,
+                    Behavior::CxxContainerByValueSafe | Behavior::CxxContainerNotByValueSafe
+                )
+            })
+            .unwrap_or(false)
+    }
+
     /// Here we substitute any names which we know are Special from
     /// our type database, e.g. std::unique_ptr -> UniquePtr.
     /// We strip off and ignore
@@ -306,60 +336,80 @@
         "std::unique_ptr",
         Behavior::CxxContainerByValueSafe,
         None,
+        false,
+        true,
     ));
     db.insert(TypeDetails::new(
         "cxx::CxxVector",
         "std::vector",
         Behavior::CxxContainerNotByValueSafe,
         None,
+        false,
+        true,
     ));
     db.insert(TypeDetails::new(
         "cxx::SharedPtr",
         "std::shared_ptr",
         Behavior::CxxContainerByValueSafe,
         None,
+        true,
+        true,
     ));
     db.insert(TypeDetails::new(
         "cxx::WeakPtr",
         "std::weak_ptr",
         Behavior::CxxContainerByValueSafe,
         None,
+        true,
+        true,
     ));
     db.insert(TypeDetails::new(
         "cxx::CxxString",
         "std::string",
         Behavior::CxxString,
         None,
+        true,
+        true,
     ));
     db.insert(TypeDetails::new(
         "str",
         "rust::Str",
         Behavior::RustStr,
         None,
+        true,
+        false,
     ));
     db.insert(TypeDetails::new(
         "String",
         "rust::String",
         Behavior::RustString,
         None,
+        true,
+        true,
     ));
     db.insert(TypeDetails::new(
         "std::boxed::Box",
         "rust::Box",
         Behavior::RustContainerByValueSafe,
         None,
+        false,
+        true,
     ));
     db.insert(TypeDetails::new(
         "i8",
         "int8_t",
         Behavior::CByValue,
         Some("std::os::raw::c_schar".into()),
+        true,
+        true,
     ));
     db.insert(TypeDetails::new(
         "u8",
         "uint8_t",
         Behavior::CByValue,
         Some("std::os::raw::c_uchar".into()),
+        true,
+        true,
     ));
     for (cpp_type, rust_type) in (4..7).map(|x| 2i32.pow(x)).flat_map(|x| {
         vec![
@@ -372,15 +422,26 @@
             cpp_type,
             Behavior::CByValue,
             None,
+            true,
+            true,
         ));
     }
-    db.insert(TypeDetails::new("bool", "bool", Behavior::CByValue, None));
+    db.insert(TypeDetails::new(
+        "bool",
+        "bool",
+        Behavior::CByValue,
+        None,
+        true,
+        true,
+    ));
 
     db.insert(TypeDetails::new(
         "std::pin::Pin",
         "Pin",
         Behavior::RustByValue, // because this is actually Pin<&something>
         None,
+        true,
+        false,
     ));
 
     let mut insert_ctype = |cname: &str| {
@@ -390,12 +451,16 @@
             cname,
             Behavior::CVariableLengthByValue,
             Some(format!("std::os::raw::c_{}", concatenated_name)),
+            true,
+            true,
         ));
         db.insert(TypeDetails::new(
             format!("autocxx::c_u{}", concatenated_name),
             format!("unsigned {}", cname),
             Behavior::CVariableLengthByValue,
             Some(format!("std::os::raw::c_u{}", concatenated_name)),
+            true,
+            true,
         ));
     };
 
@@ -404,37 +469,41 @@
     insert_ctype("short");
     insert_ctype("long long");
 
-    db.insert(TypeDetails::new("f32", "float", Behavior::CByValue, None));
-    db.insert(TypeDetails::new("f64", "double", Behavior::CByValue, None));
+    db.insert(TypeDetails::new(
+        "f32",
+        "float",
+        Behavior::CByValue,
+        None,
+        true,
+        true,
+    ));
+    db.insert(TypeDetails::new(
+        "f64",
+        "double",
+        Behavior::CByValue,
+        None,
+        true,
+        true,
+    ));
     db.insert(TypeDetails::new(
         "::std::os::raw::c_char",
         "char",
         Behavior::CByValue,
         None,
+        true,
+        true,
     ));
     db.insert(TypeDetails::new(
         "autocxx::c_void",
         "void",
         Behavior::CVoid,
         Some("std::os::raw::c_void".into()),
+        false,
+        false,
     ));
     db
 }
 
-/// If a given type lacks a copy constructor, we should always use
-/// std::move in wrapper functions.
-pub(crate) fn type_lacks_copy_constructor(ty: &Type) -> bool {
-    // In future we may wish to look this up in KNOWN_TYPES.
-    match ty {
-        Type::Path(typ) => {
-            let tn = QualifiedName::from_type_path(typ);
-            tn.to_cpp_name().starts_with("std::unique_ptr")
-                || tn.to_cpp_name().starts_with("rust::Box")
-        }
-        _ => false,
-    }
-}
-
 pub(crate) fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertError> {
     match *ptr.elem {
         Type::Path(..) => Ok(()),
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/lib.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/lib.rs
similarity index 93%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/lib.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/lib.rs
index 669789e1..395ebf76 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/lib.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/lib.rs
@@ -4,21 +4,16 @@
 
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 // This feature=nightly could be set by build.rs, but since we only care
 // about it for docs, we ask docs.rs to set it in the Cargo.toml.
 #![cfg_attr(feature = "nightly", feature(doc_cfg))]
+#![forbid(unsafe_code)]
 
 mod ast_discoverer;
 mod conversion;
@@ -69,13 +64,6 @@
 
 pub use cxx_gen::HEADER;
 
-/// Re-export cxx such that clients can use the same version as
-/// us. This doesn't enable clients to avoid depending on the cxx
-/// crate too, unfortunately, since generated cxx::bridge code
-/// refers explicitly to ::cxx. See
-/// <https://github.com/google/autocxx/issues/36>
-pub use cxx;
-
 #[derive(Clone)]
 /// Some C++ content which should be written to disk and built.
 pub struct CppFilePair {
@@ -570,7 +558,9 @@
             State::NotGenerated => panic!("Call generate() first"),
             State::Generated(gen_results) => {
                 let rs = gen_results.item_mod.to_token_stream();
-                files.push(do_cxx_cpp_generation(rs, cpp_codegen_options)?);
+                if !cpp_codegen_options.skip_cxx_gen {
+                    files.push(do_cxx_cpp_generation(rs, cpp_codegen_options)?);
+                }
                 if let Some(cpp_file_pair) = &gen_results.cpp {
                     files.push(cpp_file_pair.clone());
                 }
@@ -628,18 +618,43 @@
         .unwrap_or_else(|_| "clang++".to_string())
 }
 
+/// Newtype wrapper so we can give it a [`Default`].
+pub struct HeaderNamer<'a>(pub Box<dyn 'a + Fn(String) -> String>);
+
+impl Default for HeaderNamer<'static> {
+    fn default() -> Self {
+        Self(Box::new(|mod_name| format!("autocxxgen_{}.h", mod_name)))
+    }
+}
+
+impl HeaderNamer<'_> {
+    fn name_header(&self, mod_name: String) -> String {
+        self.0(mod_name)
+    }
+}
+
 /// Options for C++ codegen
 #[derive(Default)]
-pub struct CppCodegenOptions {
+pub struct CppCodegenOptions<'a> {
     /// Whether to avoid generating `#include <some-system-header>`.
     /// You may wish to do this to make a hermetic test case with no
     /// external dependencies.
     pub suppress_system_headers: bool,
-    /// Optionally, a prefix to go at `#include "<here>cxx.h"
+    /// Optionally, a prefix to go at `#include "<here>cxx.h". This is a header file from the `cxx`
+    /// crate.
     pub path_to_cxx_h: Option<String>,
-    /// Optionally, a prefix to go at `#include "<here>cxxgen.h"
+    /// Optionally, a prefix to go at `#include "<here>cxxgen.h". This is a header file which we
+    /// generate.
     pub path_to_cxxgen_h: Option<String>,
+    /// Optionally, a function called to generate each of the per-section header files. The default
+    /// names are subject to change.
+    /// The function is passed the name of the module generated by each `include_cpp`,
+    /// configured via `name`. These will be unique.
+    pub header_namer: HeaderNamer<'a>,
     /// An annotation optionally to include on each C++ function.
     /// For example to export the symbol from a library.
     pub cxx_impl_annotations: Option<String>,
+    /// Whether to skip using [`cxx_gen`] to generate the C++ code,
+    /// so that some other process can handle that.
+    pub skip_cxx_gen: bool,
 }
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/parse_callbacks.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_callbacks.rs
new file mode 100644
index 0000000..61098c0
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_callbacks.rs
@@ -0,0 +1,23 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::panic::UnwindSafe;
+
+use crate::RebuildDependencyRecorder;
+use autocxx_bindgen::callbacks::ParseCallbacks;
+
+#[derive(Debug)]
+pub(crate) struct AutocxxParseCallbacks(pub(crate) Box<dyn RebuildDependencyRecorder>);
+
+impl UnwindSafe for AutocxxParseCallbacks {}
+
+impl ParseCallbacks for AutocxxParseCallbacks {
+    fn include_file(&self, filename: &str) {
+        self.0.record_header_file_dependency(filename);
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_17/crate/src/parse_file.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_file.rs
new file mode 100644
index 0000000..3b234ca
--- /dev/null
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/parse_file.rs
@@ -0,0 +1,425 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::ast_discoverer::Discoveries;
+use crate::CppCodegenOptions;
+use crate::{
+    cxxbridge::CxxBridge, Error as EngineError, GeneratedCpp, IncludeCppEngine,
+    RebuildDependencyRecorder,
+};
+use autocxx_parser::directives::SUBCLASS;
+use autocxx_parser::{AllowlistEntry, RustPath, Subclass, SubclassAttrs};
+use proc_macro2::{Span, TokenStream};
+use quote::ToTokens;
+use std::{collections::HashSet, fmt::Display, io::Read, path::PathBuf};
+use std::{panic::UnwindSafe, path::Path, rc::Rc};
+use syn::{token::Brace, Item, ItemMod};
+
+/// Errors which may occur when parsing a Rust source file to discover
+/// and interpret include_cxx macros.
+#[derive(Debug)]
+pub enum ParseError {
+    /// Unable to open the source file
+    FileOpen(std::io::Error),
+    /// The .rs file couldn't be read.
+    FileRead(std::io::Error),
+    /// The .rs file couldn't be parsed.
+    Syntax(syn::Error),
+    /// The include CPP macro could not be expanded into
+    /// Rust bindings to C++, because of some problem during the conversion
+    /// process. This could be anything from a C++ parsing error to some
+    /// C++ feature that autocxx can't yet handle and isn't able to skip
+    /// over. It could also cover errors in your syntax of the `include_cpp`
+    /// macro or the directives inside.
+    AutocxxCodegenError(EngineError),
+    /// There are two or more `include_cpp` macros with the same
+    /// mod name.
+    ConflictingModNames,
+    ZeroModsForDynamicDiscovery,
+    MultipleModsForDynamicDiscovery,
+    DiscoveredRustItemsWhenNotInAutoDiscover,
+}
+
+impl Display for ParseError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?,
+            ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?,
+            ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?,
+            ParseError::AutocxxCodegenError(err) =>
+                write!(f, "Unable to parse include_cpp! macro: {}", err)?,
+            ParseError::ConflictingModNames =>
+                write!(f, "There are two or more include_cpp! macros with the same output mod name. Use name!")?,
+            ParseError::ZeroModsForDynamicDiscovery =>
+                write!(f, "This file contains extra information to append to an include_cpp! but no such include_cpp! was found in this file.")?,
+            ParseError::MultipleModsForDynamicDiscovery =>
+                write!(f, "This file contains extra information to append to an include_cpp! but multiple such include_cpp! declarations were found in this file.")?,
+            ParseError::DiscoveredRustItemsWhenNotInAutoDiscover =>
+                write!(f, "This file contains extra information to append to an \"extern Rust\" but auto-discover was switched off.")?,
+        }
+        Ok(())
+    }
+}
+
+/// Parse a Rust file, and spot any include_cpp macros within it.
+pub fn parse_file<P1: AsRef<Path>>(
+    rs_file: P1,
+    auto_allowlist: bool,
+) -> Result<ParsedFile, ParseError> {
+    let mut source = String::new();
+    let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?;
+    file.read_to_string(&mut source)
+        .map_err(ParseError::FileRead)?;
+    proc_macro2::fallback::force();
+    let source = syn::parse_file(&source).map_err(ParseError::Syntax)?;
+    parse_file_contents(source, auto_allowlist)
+}
+
+fn parse_file_contents(source: syn::File, auto_allowlist: bool) -> Result<ParsedFile, ParseError> {
+    #[derive(Default)]
+    struct State {
+        auto_allowlist: bool,
+        results: Vec<Segment>,
+        extra_superclasses: Vec<Subclass>,
+        discoveries: Discoveries,
+    }
+    impl State {
+        fn parse_item(&mut self, item: Item, mod_path: Option<RustPath>) -> Result<(), ParseError> {
+            let result = match item {
+                Item::Macro(mac)
+                    if mac
+                        .mac
+                        .path
+                        .segments
+                        .last()
+                        .map(|s| s.ident == "include_cpp")
+                        .unwrap_or(false) =>
+                {
+                    Segment::Autocxx(
+                        crate::IncludeCppEngine::new_from_syn(mac.mac)
+                            .map_err(ParseError::AutocxxCodegenError)?,
+                    )
+                }
+                Item::Mod(itm)
+                    if itm
+                        .attrs
+                        .iter()
+                        .any(|attr| attr.path.to_token_stream().to_string() == "cxx :: bridge") =>
+                {
+                    Segment::Cxx(CxxBridge::from(itm))
+                }
+                Item::Mod(itm) => {
+                    if let Some((brace, items)) = itm.content {
+                        let mut mod_state = State {
+                            auto_allowlist: self.auto_allowlist,
+                            ..Default::default()
+                        };
+                        let mod_path = match &mod_path {
+                            None => RustPath::new_from_ident(itm.ident.clone()),
+                            Some(mod_path) => mod_path.append(itm.ident.clone()),
+                        };
+                        for item in items {
+                            mod_state.parse_item(item, Some(mod_path.clone()))?
+                        }
+                        self.extra_superclasses.extend(mod_state.extra_superclasses);
+                        self.discoveries.extend(mod_state.discoveries);
+                        Segment::Mod(
+                            mod_state.results,
+                            (
+                                brace,
+                                ItemMod {
+                                    content: None,
+                                    ..itm
+                                },
+                            ),
+                        )
+                    } else {
+                        Segment::Other(Item::Mod(itm))
+                    }
+                }
+                Item::Struct(ref its) if self.auto_allowlist => {
+                    let attrs = &its.attrs;
+                    let is_superclass_attr = attrs.iter().find(|attr| {
+                        attr.path
+                            .segments
+                            .last()
+                            .map(|seg| seg.ident == "is_subclass" || seg.ident == SUBCLASS)
+                            .unwrap_or(false)
+                    });
+                    if let Some(is_superclass_attr) = is_superclass_attr {
+                        if !is_superclass_attr.tokens.is_empty() {
+                            let subclass = its.ident.clone();
+                            let args: SubclassAttrs = is_superclass_attr
+                                .parse_args()
+                                .map_err(ParseError::Syntax)?;
+                            if let Some(superclass) = args.superclass {
+                                self.extra_superclasses.push(Subclass {
+                                    superclass,
+                                    subclass,
+                                })
+                            }
+                        }
+                    }
+                    self.discoveries.search_item(&item, mod_path);
+                    Segment::Other(item)
+                }
+                _ => {
+                    self.discoveries.search_item(&item, mod_path);
+                    Segment::Other(item)
+                }
+            };
+            self.results.push(result);
+            Ok(())
+        }
+    }
+    let mut state = State {
+        auto_allowlist,
+        ..Default::default()
+    };
+    for item in source.items {
+        state.parse_item(item, None)?
+    }
+    let State {
+        auto_allowlist,
+        mut results,
+        mut extra_superclasses,
+        mut discoveries,
+    } = state;
+    if !auto_allowlist
+        && (!discoveries.extern_rust_types.is_empty() || !discoveries.extern_rust_funs.is_empty())
+    {
+        return Err(ParseError::DiscoveredRustItemsWhenNotInAutoDiscover);
+    }
+    if !extra_superclasses.is_empty() || (auto_allowlist && !discoveries.is_empty()) {
+        let mut autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
+            Segment::Autocxx(engine) => Some(engine),
+            _ => None,
+        });
+        let our_seg = autocxx_seg_iterator.next();
+        match our_seg {
+            None => return Err(ParseError::ZeroModsForDynamicDiscovery),
+            Some(engine) => {
+                engine
+                    .config_mut()
+                    .subclasses
+                    .append(&mut extra_superclasses);
+                if auto_allowlist {
+                    for cpp in discoveries.cpp_list {
+                        engine
+                            .config_mut()
+                            .allowlist
+                            .push(AllowlistEntry::Item(cpp), Span::call_site())
+                            .map_err(ParseError::Syntax)?;
+                    }
+                }
+                engine
+                    .config_mut()
+                    .extern_rust_funs
+                    .append(&mut discoveries.extern_rust_funs);
+                engine
+                    .config_mut()
+                    .rust_types
+                    .append(&mut discoveries.extern_rust_types);
+            }
+        }
+        if autocxx_seg_iterator.next().is_some() {
+            return Err(ParseError::MultipleModsForDynamicDiscovery);
+        }
+    }
+    let autocxx_seg_iterator = results.iter_mut().filter_map(|seg| match seg {
+        Segment::Autocxx(engine) => Some(engine),
+        _ => None,
+    });
+    for seg in autocxx_seg_iterator {
+        seg.config
+            .confirm_complete(auto_allowlist)
+            .map_err(ParseError::Syntax)?;
+    }
+    Ok(ParsedFile(results))
+}
+
+/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines',
+/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp
+/// macros within this file. Also contains `syn::Item` structures for all
+/// the rest of the Rust code, such that it can be reconstituted if necessary.
+pub struct ParsedFile(Vec<Segment>);
+
+#[allow(clippy::large_enum_variant)]
+enum Segment {
+    Autocxx(IncludeCppEngine),
+    Cxx(CxxBridge),
+    Mod(Vec<Segment>, (Brace, ItemMod)),
+    Other(Item),
+}
+
+pub trait CppBuildable {
+    fn generate_h_and_cxx(
+        &self,
+        cpp_codegen_options: &CppCodegenOptions,
+    ) -> Result<GeneratedCpp, cxx_gen::Error>;
+}
+
+impl ParsedFile {
+    /// Get all the autocxxes in this parsed file.
+    pub fn get_rs_buildables(&self) -> impl Iterator<Item = &IncludeCppEngine> {
+        fn do_get_rs_buildables(segments: &[Segment]) -> impl Iterator<Item = &IncludeCppEngine> {
+            segments
+                .iter()
+                .flat_map(|s| -> Box<dyn Iterator<Item = &IncludeCppEngine>> {
+                    match s {
+                        Segment::Autocxx(includecpp) => Box::new(std::iter::once(includecpp)),
+                        Segment::Mod(segments, _) => Box::new(do_get_rs_buildables(segments)),
+                        _ => Box::new(std::iter::empty()),
+                    }
+                })
+        }
+
+        do_get_rs_buildables(&self.0)
+    }
+
+    /// Get all items which can result in C++ code
+    pub fn get_cpp_buildables(&self) -> impl Iterator<Item = &dyn CppBuildable> {
+        fn do_get_cpp_buildables(segments: &[Segment]) -> impl Iterator<Item = &dyn CppBuildable> {
+            segments
+                .iter()
+                .flat_map(|s| -> Box<dyn Iterator<Item = &dyn CppBuildable>> {
+                    match s {
+                        Segment::Autocxx(includecpp) => {
+                            Box::new(std::iter::once(includecpp as &dyn CppBuildable))
+                        }
+                        Segment::Cxx(cxxbridge) => {
+                            Box::new(std::iter::once(cxxbridge as &dyn CppBuildable))
+                        }
+                        Segment::Mod(segments, _) => Box::new(do_get_cpp_buildables(segments)),
+                        _ => Box::new(std::iter::empty()),
+                    }
+                })
+        }
+
+        do_get_cpp_buildables(&self.0)
+    }
+
+    fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> {
+        fn do_get_autocxxes_mut(
+            segments: &mut [Segment],
+        ) -> impl Iterator<Item = &mut IncludeCppEngine> {
+            segments
+                .iter_mut()
+                .flat_map(|s| -> Box<dyn Iterator<Item = &mut IncludeCppEngine>> {
+                    match s {
+                        Segment::Autocxx(includecpp) => Box::new(std::iter::once(includecpp)),
+                        Segment::Mod(segments, _) => Box::new(do_get_autocxxes_mut(segments)),
+                        _ => Box::new(std::iter::empty()),
+                    }
+                })
+        }
+
+        do_get_autocxxes_mut(&mut self.0)
+    }
+
+    pub fn include_dirs(&self) -> impl Iterator<Item = &PathBuf> {
+        fn do_get_include_dirs(segments: &[Segment]) -> impl Iterator<Item = &PathBuf> {
+            segments
+                .iter()
+                .flat_map(|s| -> Box<dyn Iterator<Item = &PathBuf>> {
+                    match s {
+                        Segment::Autocxx(includecpp) => Box::new(includecpp.include_dirs()),
+                        Segment::Mod(segments, _) => Box::new(do_get_include_dirs(segments)),
+                        _ => Box::new(std::iter::empty()),
+                    }
+                })
+        }
+
+        do_get_include_dirs(&self.0)
+    }
+
+    pub fn resolve_all(
+        &mut self,
+        autocxx_inc: Vec<PathBuf>,
+        extra_clang_args: &[&str],
+        dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
+        cpp_codegen_options: &CppCodegenOptions,
+    ) -> Result<(), ParseError> {
+        let mut mods_found = HashSet::new();
+        let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
+            dep_recorder.map(Rc::from);
+        for include_cpp in self.get_autocxxes_mut() {
+            #[allow(clippy::manual_map)] // because of dyn shenanigans
+            let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder
+            {
+                None => None,
+                Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new(
+                    inner_dep_recorder.clone(),
+                ))),
+            };
+            if !mods_found.insert(include_cpp.get_mod_name()) {
+                return Err(ParseError::ConflictingModNames);
+            }
+            include_cpp
+                .generate(
+                    autocxx_inc.clone(),
+                    extra_clang_args,
+                    dep_recorder,
+                    cpp_codegen_options,
+                )
+                .map_err(ParseError::AutocxxCodegenError)?
+        }
+        Ok(())
+    }
+}
+
+impl ToTokens for ParsedFile {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        for seg in &self.0 {
+            seg.to_tokens(tokens)
+        }
+    }
+}
+
+impl ToTokens for Segment {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Segment::Other(item) => item.to_tokens(tokens),
+            Segment::Autocxx(autocxx) => {
+                let these_tokens = autocxx.generate_rs();
+                tokens.extend(these_tokens);
+            }
+            Segment::Cxx(cxxbridge) => cxxbridge.to_tokens(tokens),
+            Segment::Mod(segments, (brace, itemmod)) => {
+                let mod_items = segments
+                    .iter()
+                    .map(|segment| syn::parse2::<Item>(segment.to_token_stream()).unwrap())
+                    .collect();
+                let itemmod = ItemMod {
+                    content: Some((*brace, mod_items)),
+                    ..itemmod.clone()
+                };
+                Item::Mod(itemmod).to_tokens(tokens)
+            }
+        }
+    }
+}
+
+/// Shenanigans required to share the same RebuildDependencyRecorder
+/// with all of the include_cpp instances in this one file.
+#[derive(Debug, Clone)]
+struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>);
+
+impl CompositeDepRecorder {
+    fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self {
+        CompositeDepRecorder(inner)
+    }
+}
+
+impl UnwindSafe for CompositeDepRecorder {}
+
+impl RebuildDependencyRecorder for CompositeDepRecorder {
+    fn record_header_file_dependency(&self, filename: &str) {
+        self.0.record_header_file_dependency(filename);
+    }
+}
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/rust_pretty_printer.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/rust_pretty_printer.rs
similarity index 62%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/rust_pretty_printer.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/rust_pretty_printer.rs
index 0c7ccee7..9c23cbc 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/rust_pretty_printer.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/rust_pretty_printer.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use proc_macro2::TokenStream;
 use std::io::Write;
diff --git a/third_party/rust/autocxx_engine/v0_16/crate/src/types.rs b/third_party/rust/autocxx_engine/v0_17/crate/src/types.rs
similarity index 93%
rename from third_party/rust/autocxx_engine/v0_16/crate/src/types.rs
rename to third_party/rust/autocxx_engine/v0_17/crate/src/types.rs
index a0bbf13..241c872 100644
--- a/third_party/rust/autocxx_engine/v0_16/crate/src/types.rs
+++ b/third_party/rust/autocxx_engine/v0_17/crate/src/types.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use itertools::Itertools;
 use proc_macro2::Span;
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_gen/v0_16/crate/.cargo_vcs_info.json
deleted file mode 100644
index bf555d2..0000000
--- a/third_party/rust/autocxx_gen/v0_16/crate/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74"
-  },
-  "path_in_vcs": "gen/cmd"
-}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml.orig
deleted file mode 100644
index ee50bbfd..0000000
--- a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml.orig
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-[package]
-name = "autocxx-gen"
-version = "0.16.0"
-authors = ["Adrian Taylor <adetaylor@chromium.org>"]
-edition = "2021"
-license = "MIT OR Apache-2.0"
-description = "Safe autogenerated interop between Rust and C++"
-repository = "https://github.com/google/autocxx"
-keywords = ["ffi"]
-categories = ["development-tools::ffi", "api-bindings"]
-
-[features]
-runtime = [ "autocxx-engine/runtime" ]
-static = [ "autocxx-engine/static" ]
-
-[dependencies]
-autocxx-engine = { version="=0.16.0", path="../../engine" }
-clap = "~2.33"
-quote = "1.0.7"
-proc-macro2 = "1.0"
-env_logger = "0.9.0"
-
-[dev-dependencies]
-assert_cmd = "1.0.3"
-tempdir = "0.3.7"
diff --git a/third_party/rust/autocxx_gen/v0_16/BUILD.gn b/third_party/rust/autocxx_gen/v0_17/BUILD.gn
similarity index 80%
rename from third_party/rust/autocxx_gen/v0_16/BUILD.gn
rename to third_party/rust/autocxx_gen/v0_17/BUILD.gn
index c43f38cc..d5c5b3f5 100644
--- a/third_party/rust/autocxx_gen/v0_16/BUILD.gn
+++ b/third_party/rust/autocxx_gen/v0_17/BUILD.gn
@@ -10,14 +10,14 @@
   sources = [ "crate/src/main.rs" ]
   edition = "2018"
   deps = [
-    "//third_party/rust/autocxx_engine/v0_16:lib",
+    "//third_party/rust/autocxx_engine/v0_17:lib",
     "//third_party/rust/clap/v2:lib",
     "//third_party/rust/env_logger/v0_9:lib",
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
   ]
   rustenv = [
-    "CARGO_PKG_VERSION=foo",
-    "CARGO_PKG_AUTHORS=foo",
+    "CARGO_PKG_VERSION=0.17",
+    "CARGO_PKG_AUTHORS=adetaylor@chromium.org",
   ]
 }
diff --git a/third_party/rust/autocxx_gen/v0_16/README.chromium b/third_party/rust/autocxx_gen/v0_17/README.chromium
similarity index 78%
rename from third_party/rust/autocxx_gen/v0_16/README.chromium
rename to third_party/rust/autocxx_gen/v0_17/README.chromium
index 30cc38f..063d33e9 100644
--- a/third_party/rust/autocxx_gen/v0_16/README.chromium
+++ b/third_party/rust/autocxx_gen/v0_17/README.chromium
@@ -1,6 +1,6 @@
 Name: autocxx-gen
 URL: https://crates.io/crates/autocxx-gen
 Description: Safe autogenerated interop between Rust and C++
-Version: 0.16.0
-Security Critical: no
+Version: 0.17.2
+Security Critical: yes
 License: Apache 2.0
diff --git a/third_party/rust/autocxx_gen/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_gen/v0_17/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..3c793de0
--- /dev/null
+++ b/third_party/rust/autocxx_gen/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd"
+  },
+  "path_in_vcs": "gen/cmd"
+}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.lock b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.lock
similarity index 84%
rename from third_party/rust/autocxx_gen/v0_16/crate/Cargo.lock
rename to third_party/rust/autocxx_gen/v0_17/crate/Cargo.lock
index e7863d2..e5785f6 100644
--- a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.lock
+++ b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.lock
@@ -84,14 +84,13 @@
 
 [[package]]
 name = "autocxx-engine"
-version = "0.16.0"
+version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02323905bec49fde96ff028fcff1c478d0eba14fa34ea5eb5e4d17439748e42a"
+checksum = "66906a5d5167cb933a8faf70a9dd27ebb7f8f68d77054766bc9cf05582d3ac51"
 dependencies = [
  "aquamarine",
  "autocxx-bindgen",
  "autocxx-parser",
- "cxx",
  "cxx-gen",
  "indoc",
  "itertools 0.10.3",
@@ -103,13 +102,12 @@
  "strum_macros",
  "syn",
  "tempfile",
- "unzip-n",
  "version_check",
 ]
 
 [[package]]
 name = "autocxx-gen"
-version = "0.16.0"
+version = "0.17.2"
 dependencies = [
  "assert_cmd",
  "autocxx-engine",
@@ -122,9 +120,9 @@
 
 [[package]]
 name = "autocxx-parser"
-version = "0.16.0"
+version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0ad08260adcecc119b08f460b0633b6e306ea2f6fda4f27e4dd28e20b9a2f4"
+checksum = "688d5eadba022940ccca017a3aa83edd5d1d7acc8ba23c322d0cec1e4ed4afc0"
 dependencies = [
  "log",
  "proc-macro2",
@@ -152,12 +150,6 @@
 ]
 
 [[package]]
-name = "cc"
-version = "1.0.72"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
-
-[[package]]
 name = "cexpr"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -209,22 +201,10 @@
 ]
 
 [[package]]
-name = "cxx"
-version = "1.0.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcd554072878dec8c16f59839336bf712d6d1f0d8d27cf9b471c10a484a3290f"
-dependencies = [
- "cc",
- "cxxbridge-flags",
- "cxxbridge-macro",
- "link-cplusplus",
-]
-
-[[package]]
 name = "cxx-gen"
-version = "0.7.64"
+version = "0.7.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2614ca083ee2bb4d362fd63d4d892900b2814566f72e9db38fa9f30182e6fa66"
+checksum = "2bba249c8ea90cff9c647cd76928efc82f1e8684da0a6cedb4416cc79478050d"
 dependencies = [
  "codespan-reporting",
  "proc-macro2",
@@ -233,23 +213,6 @@
 ]
 
 [[package]]
-name = "cxxbridge-flags"
-version = "1.0.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cc629121d3f01cd7c85ba046b3bcef66d182429b495eeb1080396ba4bfb8ae4"
-
-[[package]]
-name = "cxxbridge-macro"
-version = "1.0.64"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93a8fa39ee5a91d04a99fc09fc3f1dd780ebfe31817721abcaecd823e00c9f3b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "difflib"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -303,12 +266,9 @@
 
 [[package]]
 name = "heck"
-version = "0.3.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
 
 [[package]]
 name = "hermit-abi"
@@ -327,9 +287,9 @@
 
 [[package]]
 name = "indoc"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136"
+checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
 dependencies = [
  "unindent",
 ]
@@ -381,9 +341,9 @@
 
 [[package]]
 name = "libc"
-version = "0.2.117"
+version = "0.2.120"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
+checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09"
 
 [[package]]
 name = "libloading"
@@ -396,15 +356,6 @@
 ]
 
 [[package]]
-name = "link-cplusplus"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8"
-dependencies = [
- "cc",
-]
-
-[[package]]
 name = "log"
 version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -427,20 +378,19 @@
 
 [[package]]
 name = "nom"
-version = "7.1.0"
+version = "7.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
 dependencies = [
  "memchr",
  "minimal-lexical",
- "version_check",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.9.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
 
 [[package]]
 name = "peeking_take_while"
@@ -556,18 +506,18 @@
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.10"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -632,9 +582,9 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.78"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085"
+checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
 dependencies = [
  "itoa",
  "ryu",
@@ -655,9 +605,9 @@
 
 [[package]]
 name = "strum_macros"
-version = "0.23.1"
+version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38"
+checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -668,9 +618,9 @@
 
 [[package]]
 name = "syn"
-version = "1.0.86"
+version = "1.0.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
+checksum = "ebd69e719f31e88618baa1eaa6ee2de5c9a1c004f1e9ecdb58e8352a13f20a01"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -703,9 +653,9 @@
 
 [[package]]
 name = "termcolor"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
 dependencies = [
  "winapi-util",
 ]
@@ -726,12 +676,6 @@
 ]
 
 [[package]]
-name = "unicode-segmentation"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
-
-[[package]]
 name = "unicode-width"
 version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -745,20 +689,9 @@
 
 [[package]]
 name = "unindent"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
-
-[[package]]
-name = "unzip-n"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
 
 [[package]]
 name = "vec_map"
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml
similarity index 96%
rename from third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml
rename to third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml
index e4b1d69..bee6b0a 100644
--- a/third_party/rust/autocxx_gen/v0_16/crate/Cargo.toml
+++ b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "autocxx-gen"
-version = "0.16.0"
+version = "0.17.2"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 description = "Safe autogenerated interop between Rust and C++"
 keywords = ["ffi"]
@@ -25,7 +25,7 @@
 resolver = "2"
 
 [dependencies.autocxx-engine]
-version = "=0.16.0"
+version = "=0.17.2"
 
 [dependencies.clap]
 version = "~2.33"
diff --git a/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml.orig
new file mode 100644
index 0000000..b299d79
--- /dev/null
+++ b/third_party/rust/autocxx_gen/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,33 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-gen"
+version = "0.17.2"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+edition = "2021"
+license = "MIT OR Apache-2.0"
+description = "Safe autogenerated interop between Rust and C++"
+repository = "https://github.com/google/autocxx"
+keywords = ["ffi"]
+categories = ["development-tools::ffi", "api-bindings"]
+
+[features]
+runtime = [ "autocxx-engine/runtime" ]
+static = [ "autocxx-engine/static" ]
+
+[dependencies]
+autocxx-engine = { version="=0.17.2", path="../../engine" }
+clap = "~2.33"
+quote = "1.0.7"
+proc-macro2 = "1.0"
+env_logger = "0.9.0"
+
+[dev-dependencies]
+assert_cmd = "1.0.3"
+tempdir = "0.3.7"
diff --git a/third_party/rust/autocxx_gen/v0_17/crate/README.md b/third_party/rust/autocxx_gen/v0_17/crate/README.md
new file mode 100644
index 0000000..9b91d4a
--- /dev/null
+++ b/third_party/rust/autocxx_gen/v0_17/crate/README.md
@@ -0,0 +1 @@
+This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/src/main.rs b/third_party/rust/autocxx_gen/v0_17/crate/src/main.rs
similarity index 76%
rename from third_party/rust/autocxx_gen/v0_16/crate/src/main.rs
rename to third_party/rust/autocxx_gen/v0_17/crate/src/main.rs
index 0af32b4..207ba96 100644
--- a/third_party/rust/autocxx_gen/v0_16/crate/src/main.rs
+++ b/third_party/rust/autocxx_gen/v0_17/crate/src/main.rs
@@ -1,27 +1,20 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
-#[cfg(test)]
-mod cmd_test;
+#![forbid(unsafe_code)]
 
-use autocxx_engine::{parse_file, CppCodegenOptions};
+use autocxx_engine::{parse_file, HeaderNamer};
 use clap::{crate_authors, crate_version, App, Arg, ArgGroup};
 use proc_macro2::TokenStream;
 use quote::ToTokens;
 use std::io::{Read, Write};
 use std::path::PathBuf;
-use std::{fs::File, path::Path};
+use std::{cell::Cell, fs::File, path::Path};
 
 pub(crate) static BLANK: &str = "// Blank autocxx placeholder";
 
@@ -62,13 +55,21 @@
 b) build all *.cc files produced by this tool.
 
 If your build system requires each build rule to make precise filenames
-known in advance, then use --generate-exact 2
-and --fix-rs-include-name. If you do this, you'll need to:
-a) Have exactly one include_cpp directive in each source .rs file
-   (or use different numbers with --generate-exact)
-b) Set AUTOCXX_RS_FILE when using autocxx_macro.
-c) Teach your build system always that the outputs of this tool
-   are always guaranteed to be gen0.include.rs, gen0.cc and gen1.cc.
+known in advance, then you will need to:
+a) Use `--generate-exact <N> --gen-rs-complete`
+b) Teach your build system that the C++ files to compile are named `gen0.h`,
+   `gen0.cc`, `gen1.h`, `gen1.cc`, etc (through `N`), corresponding
+   to each `include_cpp!` section, plus 'cxxgen.h`. `gen.complete.rs` will also
+   be generated and should be compiled _instead of_ the original Rust file.
+c) If `N` is bigger than the number of files needed, extra no-op files will
+   be emitted. These may still be compiled normally, but won't do anything. If
+   `N` is smaller than the number of files needed, generation will fail.
+
+Note that there is currently no way to teach each `include_cpp!` section
+which `.include.rs` file to use, so the only way to get fixed output paths is
+with `--gen-rs-complete`. There are always multiple `.cc` files (even with just
+a single `include_cpp!` section), and we always generate the same number of each
+type of file.
 ";
 
 fn main() {
@@ -133,9 +134,9 @@
             .arg("gen-rs-include")
         )
         .arg(
-            Arg::with_name("cxx-gen")
-                .long("cxx-gen")
-                .help("Perform C++ codegen also for #[cxx::bridge] blocks. Only applies for --gen-cpp")
+            Arg::with_name("skip-cxx-gen")
+                .long("skip-cxx-gen")
+                .help("Skip performing C++ codegen for #[cxx::bridge] blocks. Only applies for --gen-cpp")
                 .requires("gen-cpp")
         )
         .arg(
@@ -172,14 +173,14 @@
             Arg::with_name("cxx-h-path")
                 .long("cxx-h-path")
                 .value_name("PREFIX")
-                .help("prefix for path to cxx.h within #include statements. Must end in /")
+                .help("prefix for path to cxx.h (from the cxx crate) within #include statements. Must end in /")
                 .takes_value(true),
         )
         .arg(
             Arg::with_name("cxxgen-h-path")
                 .long("cxxgen-h-path")
                 .value_name("PREFIX")
-                .help("prefix for path to cxxgen.h within #include statements. Must end in /")
+                .help("prefix for path to cxxgen.h (which we generate into the output directory) within #include statements. Must end in /")
                 .takes_value(true),
         )
         .arg(
@@ -206,11 +207,27 @@
         .unwrap_or_default()
         .collect();
     let suppress_system_headers = matches.is_present("suppress-system-headers");
-    let mut cpp_codegen_options = CppCodegenOptions::default();
-    cpp_codegen_options.suppress_system_headers = suppress_system_headers;
-    cpp_codegen_options.cxx_impl_annotations = get_option_string("cxx-impl-annotations", &matches);
-    cpp_codegen_options.path_to_cxx_h = get_option_string("cxx-h-path", &matches);
-    cpp_codegen_options.path_to_cxxgen_h = get_option_string("cxxgen-h-path", &matches);
+    let desired_number = matches
+        .value_of("generate-exact")
+        .map(|s| s.parse::<usize>().unwrap());
+    let header_counter = Cell::new(0);
+    let header_namer = if desired_number.is_some() {
+        HeaderNamer(Box::new(|_| {
+            let r = format!("gen{}.h", header_counter.get());
+            header_counter.set(header_counter.get() + 1);
+            r
+        }))
+    } else {
+        Default::default()
+    };
+    let cpp_codegen_options = autocxx_engine::CppCodegenOptions {
+        suppress_system_headers,
+        cxx_impl_annotations: get_option_string("cxx-impl-annotations", &matches),
+        path_to_cxx_h: get_option_string("cxx-h-path", &matches),
+        path_to_cxxgen_h: get_option_string("cxxgen-h-path", &matches),
+        skip_cxx_gen: matches.is_present("skip-cxx-gen"),
+        header_namer,
+    };
     // In future, we should provide an option to write a .d file here
     // by passing a callback into the dep_recorder parameter here.
     // https://github.com/google/autocxx/issues/56
@@ -218,9 +235,6 @@
         .resolve_all(incs, &extra_clang_args, None, &cpp_codegen_options)
         .expect("Unable to resolve macro");
     let outdir: PathBuf = matches.value_of_os("outdir").unwrap().into();
-    let desired_number = matches
-        .value_of("generate-exact")
-        .map(|s| s.parse::<usize>().unwrap());
     if matches.is_present("gen-cpp") {
         let cpp = matches.value_of("cpp-extension").unwrap();
         let mut counter = 0usize;
@@ -237,6 +251,8 @@
         }
         write_placeholders(&outdir, counter, desired_number, cpp);
     }
+    drop(cpp_codegen_options);
+    write_placeholders(&outdir, header_counter.into_inner(), desired_number, "h");
     if matches.is_present("gen-rs-complete") {
         let mut ts = TokenStream::new();
         parsed_file.to_tokens(&mut ts);
@@ -259,7 +275,9 @@
             write_to_file(&outdir, fname, ts.to_string().as_bytes());
             counter += 1;
         }
-        write_placeholders(&outdir, counter, desired_number, "include.rs");
+        if matches.is_present("fix-rs-include-name") {
+            write_placeholders(&outdir, counter, desired_number, "include.rs");
+        }
     }
 }
 
@@ -276,7 +294,7 @@
 ) {
     if let Some(desired_number) = desired_number {
         if counter > desired_number {
-            panic!("More include_cpp! sections were found than expected");
+            panic!("More .{} files were generated than expected. Increase the value passed to --generate-exact or reduce the number of include_cpp! sections.", extension);
         }
         while counter < desired_number {
             let fname = format!("gen{}.{}", counter, extension);
diff --git a/third_party/rust/autocxx_gen/v0_16/crate/src/cmd_test.rs b/third_party/rust/autocxx_gen/v0_17/crate/tests/cmd_test.rs
similarity index 61%
rename from third_party/rust/autocxx_gen/v0_16/crate/src/cmd_test.rs
rename to third_party/rust/autocxx_gen/v0_17/crate/tests/cmd_test.rs
index 2adbc3a..8ebd3fb 100644
--- a/third_party/rust/autocxx_gen/v0_16/crate/src/cmd_test.rs
+++ b/third_party/rust/autocxx_gen/v0_17/crate/tests/cmd_test.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use std::{convert::TryInto, fs::File, io::Write, path::Path};
 
@@ -19,6 +13,7 @@
 
 static MAIN_RS: &str = include_str!("../../../demo/src/main.rs");
 static INPUT_H: &str = include_str!("../../../demo/src/input.h");
+static BLANK: &str = "// Blank autocxx placeholder";
 
 #[test]
 fn test_help() -> Result<(), Box<dyn std::error::Error>> {
@@ -31,6 +26,19 @@
 where
     F: FnOnce(&mut Command),
 {
+    let result = base_test_ex(tmp_dir, arg_modifier, false);
+    assert_contentful(tmp_dir, "gen0.cc");
+    result
+}
+
+fn base_test_ex<F>(
+    tmp_dir: &TempDir,
+    arg_modifier: F,
+    use_complete_rs: bool,
+) -> Result<(), Box<dyn std::error::Error>>
+where
+    F: FnOnce(&mut Command),
+{
     let demo_code_dir = tmp_dir.path().join("demo");
     std::fs::create_dir(&demo_code_dir).unwrap();
     write_to_file(&demo_code_dir, "input.h", INPUT_H.as_bytes());
@@ -43,11 +51,13 @@
         .arg(demo_rs)
         .arg("--outdir")
         .arg(tmp_dir.path().to_str().unwrap())
-        .arg("--gen-cpp")
-        .arg("--gen-rs-include")
-        .assert()
-        .success();
-    assert_contentful(&tmp_dir, "gen0.cc");
+        .arg("--gen-cpp");
+    if use_complete_rs {
+        cmd.arg("--gen-rs-complete");
+    } else {
+        cmd.arg("--gen-rs-include");
+    }
+    cmd.assert().success();
     Ok(())
 }
 
@@ -68,7 +78,7 @@
             .arg("--generate-exact")
             .arg("3");
     })?;
-    assert_contains(&tmp_dir, "autocxxgen_ffi.h", "foo/cxx.h");
+    assert_contains(&tmp_dir, "gen0.h", "foo/cxx.h");
     // Currently we don't test cxxgen-h-path because we build the demo code
     // which doesn't refer to generated cxx header code.
     Ok(())
@@ -77,17 +87,23 @@
 #[test]
 fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> {
     let tmp_dir = TempDir::new("example")?;
-    base_test(&tmp_dir, |cmd| {
-        cmd.arg("--generate-exact")
-            .arg("3")
-            .arg("--fix-rs-include-name");
-    })?;
+    base_test_ex(
+        &tmp_dir,
+        |cmd| {
+            cmd.arg("--generate-exact").arg("3");
+        },
+        true,
+    )?;
     assert_contentful(&tmp_dir, "gen0.cc");
-    assert_exists(&tmp_dir, "gen1.cc");
-    assert_exists(&tmp_dir, "gen2.cc");
-    assert_contentful(&tmp_dir, "gen0.include.rs");
-    assert_exists(&tmp_dir, "gen1.include.rs");
-    assert_exists(&tmp_dir, "gen2.include.rs");
+    assert_contentful(&tmp_dir, "gen0.h");
+    // TODO: This file will actually try #including one of our .h files if there's anything to put
+    // in it. Figure out how to make that happen to test that it works.
+    assert_not_contentful(&tmp_dir, "gen1.cc");
+    assert_not_contentful(&tmp_dir, "gen1.h");
+    assert_not_contentful(&tmp_dir, "gen2.cc");
+    assert_not_contentful(&tmp_dir, "gen2.h");
+    assert_contentful(&tmp_dir, "cxxgen.h");
+    assert_contentful(&tmp_dir, "gen.complete.rs");
     Ok(())
 }
 
@@ -119,6 +135,31 @@
     Ok(())
 }
 
+#[test]
+fn test_skip_cxx_gen() -> Result<(), Box<dyn std::error::Error>> {
+    let tmp_dir = TempDir::new("example")?;
+    base_test_ex(
+        &tmp_dir,
+        |cmd| {
+            cmd.arg("--generate-exact")
+                .arg("3")
+                .arg("--fix-rs-include-name")
+                .arg("--skip-cxx-gen");
+        },
+        false,
+    )?;
+    assert_contentful(&tmp_dir, "gen0.h");
+    assert_not_contentful(&tmp_dir, "gen0.cc");
+    assert_exists(&tmp_dir, "gen1.cc");
+    assert_exists(&tmp_dir, "gen1.h");
+    assert_exists(&tmp_dir, "gen2.cc");
+    assert_exists(&tmp_dir, "gen2.h");
+    assert_contentful(&tmp_dir, "gen0.include.rs");
+    assert_exists(&tmp_dir, "gen1.include.rs");
+    assert_exists(&tmp_dir, "gen2.include.rs");
+    Ok(())
+}
+
 fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
     let path = dir.join(filename);
     let mut f = File::create(&path).expect("Unable to create file");
@@ -130,7 +171,23 @@
     if !p.exists() {
         panic!("File {} didn't exist", p.to_string_lossy());
     }
-    assert!(p.metadata().unwrap().len() > super::BLANK.len().try_into().unwrap());
+    assert!(
+        p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(),
+        "File {} is empty",
+        fname
+    );
+}
+
+fn assert_not_contentful(outdir: &TempDir, fname: &str) {
+    let p = outdir.path().join(fname);
+    if !p.exists() {
+        panic!("File {} didn't exist", p.to_string_lossy());
+    }
+    assert!(
+        p.metadata().unwrap().len() <= BLANK.len().try_into().unwrap(),
+        "File {} is not empty",
+        fname
+    );
 }
 
 fn assert_exists(outdir: &TempDir, fname: &str) {
@@ -142,7 +199,7 @@
 
 fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) {
     let p = outdir.path().join(fname);
-    let content = std::fs::read_to_string(&p).unwrap();
+    let content = std::fs::read_to_string(&p).expect(fname);
     eprintln!("content = {}", content);
     assert!(content.contains(pattern));
 }
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_macro/v0_16/crate/.cargo_vcs_info.json
deleted file mode 100644
index 6852a74..0000000
--- a/third_party/rust/autocxx_macro/v0_16/crate/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74"
-  },
-  "path_in_vcs": "macro"
-}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml.orig
deleted file mode 100644
index a680f7f..0000000
--- a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml.orig
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-[package]
-name = "autocxx-macro"
-version = "0.16.0"
-authors = ["Adrian Taylor <adetaylor@chromium.org>"]
-license = "MIT OR Apache-2.0"
-description = "Safe autogenerated interop between Rust and C++"
-repository = "https://github.com/google/autocxx"
-edition = "2021"
-keywords = ["ffi"]
-categories = ["development-tools::ffi", "api-bindings"]
-
-[lib]
-proc-macro = true
-
-[dependencies]
-autocxx-parser = { path="../parser", version="=0.16.0" }
-proc-macro-error = "1.0"
-proc-macro2 = "1.0.11"
-quote = "1.0"
-
-[dependencies.syn]
-version = "1.0"
-features = [ "full" ]
diff --git a/third_party/rust/autocxx_macro/v0_16/BUILD.gn b/third_party/rust/autocxx_macro/v0_17/BUILD.gn
similarity index 92%
rename from third_party/rust/autocxx_macro/v0_16/BUILD.gn
rename to third_party/rust/autocxx_macro/v0_17/BUILD.gn
index b94b7fb..8608bcd4 100644
--- a/third_party/rust/autocxx_macro/v0_16/BUILD.gn
+++ b/third_party/rust/autocxx_macro/v0_17/BUILD.gn
@@ -6,7 +6,7 @@
 
 cargo_crate("lib") {
   crate_name = "autocxx_macro"
-  epoch = "0.16"
+  epoch = "0.17"
   crate_type = "proc-macro"
 
   # Only for usage from third-party crates. Add the crate to
@@ -19,7 +19,7 @@
   sources = [ "crate/src/lib.rs" ]
   edition = "2021"
   deps = [
-    "//third_party/rust/autocxx_parser/v0_16:lib",
+    "//third_party/rust/autocxx_parser/v0_17:lib",
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/proc_macro_error/v1:lib",
     "//third_party/rust/quote/v1:lib",
diff --git a/third_party/rust/autocxx_macro/v0_16/README.chromium b/third_party/rust/autocxx_macro/v0_17/README.chromium
similarity index 78%
rename from third_party/rust/autocxx_macro/v0_16/README.chromium
rename to third_party/rust/autocxx_macro/v0_17/README.chromium
index 1723585..e4b2e74 100644
--- a/third_party/rust/autocxx_macro/v0_16/README.chromium
+++ b/third_party/rust/autocxx_macro/v0_17/README.chromium
@@ -1,6 +1,6 @@
 Name: autocxx-macro
 URL: https://crates.io/crates/autocxx-macro
 Description: Safe autogenerated interop between Rust and C++
-Version: 0.16.0
-Security Critical: no
+Version: 0.17.2
+Security Critical: yes
 License: Apache 2.0
diff --git a/third_party/rust/autocxx_macro/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_macro/v0_17/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..ca758e7
--- /dev/null
+++ b/third_party/rust/autocxx_macro/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd"
+  },
+  "path_in_vcs": "macro"
+}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml
similarity index 96%
rename from third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml
rename to third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml
index 61df0bf..ea4aed9 100644
--- a/third_party/rust/autocxx_macro/v0_16/crate/Cargo.toml
+++ b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "autocxx-macro"
-version = "0.16.0"
+version = "0.17.2"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 description = "Safe autogenerated interop between Rust and C++"
 keywords = ["ffi"]
@@ -28,7 +28,7 @@
 proc-macro = true
 
 [dependencies.autocxx-parser]
-version = "=0.16.0"
+version = "=0.17.2"
 
 [dependencies.proc-macro-error]
 version = "1.0"
diff --git a/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml.orig
new file mode 100644
index 0000000..b4c21ee
--- /dev/null
+++ b/third_party/rust/autocxx_macro/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,31 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-macro"
+version = "0.17.2"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+license = "MIT OR Apache-2.0"
+description = "Safe autogenerated interop between Rust and C++"
+repository = "https://github.com/google/autocxx"
+edition = "2021"
+keywords = ["ffi"]
+categories = ["development-tools::ffi", "api-bindings"]
+
+[lib]
+proc-macro = true
+
+[dependencies]
+autocxx-parser = { path="../parser", version="=0.17.2" }
+proc-macro-error = "1.0"
+proc-macro2 = "1.0.11"
+quote = "1.0"
+
+[dependencies.syn]
+version = "1.0"
+features = [ "full" ]
diff --git a/third_party/rust/autocxx_macro/v0_17/crate/README.md b/third_party/rust/autocxx_macro/v0_17/crate/README.md
new file mode 100644
index 0000000..9b91d4a
--- /dev/null
+++ b/third_party/rust/autocxx_macro/v0_17/crate/README.md
@@ -0,0 +1 @@
+This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_macro/v0_16/crate/src/lib.rs b/third_party/rust/autocxx_macro/v0_17/crate/src/lib.rs
similarity index 89%
rename from third_party/rust/autocxx_macro/v0_16/crate/src/lib.rs
rename to third_party/rust/autocxx_macro/v0_17/crate/src/lib.rs
index 529476c7..01ccd69e 100644
--- a/third_party/rust/autocxx_macro/v0_16/crate/src/lib.rs
+++ b/third_party/rust/autocxx_macro/v0_17/crate/src/lib.rs
@@ -1,16 +1,12 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![forbid(unsafe_code)]
 
 use autocxx_parser::{IncludeCpp, SubclassAttrs};
 use proc_macro::TokenStream;
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_parser/v0_16/crate/.cargo_vcs_info.json
deleted file mode 100644
index e8fe9f9d1..0000000
--- a/third_party/rust/autocxx_parser/v0_16/crate/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "1c57dc961febd206a8046db5a9badac9f5f23b74"
-  },
-  "path_in_vcs": "parser"
-}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml.orig b/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml.orig
deleted file mode 100644
index 9d23eae..0000000
--- a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml.orig
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2020 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-[package]
-name = "autocxx-parser"
-version = "0.16.0"
-authors = ["Adrian Taylor <adetaylor@chromium.org>"]
-license = "MIT OR Apache-2.0"
-description = "Safe autogenerated interop between Rust and C++"
-repository = "https://github.com/google/autocxx"
-edition = "2021"
-keywords = ["ffi"]
-categories = ["development-tools::ffi", "api-bindings"]
-
-[dependencies]
-log = "0.4"
-proc-macro2 = "1.0"
-quote = "1.0"
-serde = { version = "1.0", optional = true }
-serde_derive = { version = "1.0", optional = true }
-
-[dependencies.syn]
-version = "1.0.39"
-features = [ "full" ]
-
-[features]
-reproduction_case = [ "serde_derive", "serde" ]
diff --git a/third_party/rust/autocxx_parser/v0_16/BUILD.gn b/third_party/rust/autocxx_parser/v0_17/BUILD.gn
similarity index 98%
rename from third_party/rust/autocxx_parser/v0_16/BUILD.gn
rename to third_party/rust/autocxx_parser/v0_17/BUILD.gn
index 4286401..2b80f884 100644
--- a/third_party/rust/autocxx_parser/v0_16/BUILD.gn
+++ b/third_party/rust/autocxx_parser/v0_17/BUILD.gn
@@ -6,7 +6,7 @@
 
 cargo_crate("lib") {
   crate_name = "autocxx_parser"
-  epoch = "0.16"
+  epoch = "0.17"
   crate_type = "rlib"
 
   # Only for usage from third-party crates. Add the crate to
diff --git a/third_party/rust/autocxx_parser/v0_16/README.chromium b/third_party/rust/autocxx_parser/v0_17/README.chromium
similarity index 78%
rename from third_party/rust/autocxx_parser/v0_16/README.chromium
rename to third_party/rust/autocxx_parser/v0_17/README.chromium
index e7b607d..7bf312a 100644
--- a/third_party/rust/autocxx_parser/v0_16/README.chromium
+++ b/third_party/rust/autocxx_parser/v0_17/README.chromium
@@ -1,6 +1,6 @@
 Name: autocxx-parser
 URL: https://crates.io/crates/autocxx-parser
 Description: Safe autogenerated interop between Rust and C++
-Version: 0.16.0
-Security Critical: no
+Version: 0.17.2
+Security Critical: yes
 License: Apache 2.0
diff --git a/third_party/rust/autocxx_parser/v0_17/crate/.cargo_vcs_info.json b/third_party/rust/autocxx_parser/v0_17/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..f7c92a32
--- /dev/null
+++ b/third_party/rust/autocxx_parser/v0_17/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "5bb748878f97f854bffa61e5a7cd8ebf970f3dcd"
+  },
+  "path_in_vcs": "parser"
+}
\ No newline at end of file
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml
similarity index 98%
rename from third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml
rename to third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml
index b2a50884..0932198 100644
--- a/third_party/rust/autocxx_parser/v0_16/crate/Cargo.toml
+++ b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "autocxx-parser"
-version = "0.16.0"
+version = "0.17.2"
 authors = ["Adrian Taylor <adetaylor@chromium.org>"]
 description = "Safe autogenerated interop between Rust and C++"
 keywords = ["ffi"]
diff --git a/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml.orig b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml.orig
new file mode 100644
index 0000000..15ea808
--- /dev/null
+++ b/third_party/rust/autocxx_parser/v0_17/crate/Cargo.toml.orig
@@ -0,0 +1,32 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+[package]
+name = "autocxx-parser"
+version = "0.17.2"
+authors = ["Adrian Taylor <adetaylor@chromium.org>"]
+license = "MIT OR Apache-2.0"
+description = "Safe autogenerated interop between Rust and C++"
+repository = "https://github.com/google/autocxx"
+edition = "2021"
+keywords = ["ffi"]
+categories = ["development-tools::ffi", "api-bindings"]
+
+[dependencies]
+log = "0.4"
+proc-macro2 = "1.0"
+quote = "1.0"
+serde = { version = "1.0", optional = true }
+serde_derive = { version = "1.0", optional = true }
+
+[dependencies.syn]
+version = "1.0.39"
+features = [ "full" ]
+
+[features]
+reproduction_case = [ "serde_derive", "serde" ]
diff --git a/third_party/rust/autocxx_parser/v0_17/crate/README.md b/third_party/rust/autocxx_parser/v0_17/crate/README.md
new file mode 100644
index 0000000..9b91d4a
--- /dev/null
+++ b/third_party/rust/autocxx_parser/v0_17/crate/README.md
@@ -0,0 +1 @@
+This crate is a [component of autocxx](https://google.github.io/autocxx/).
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/config.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/config.rs
similarity index 79%
rename from third_party/rust/autocxx_parser/v0_16/crate/src/config.rs
rename to third_party/rust/autocxx_parser/v0_17/crate/src/config.rs
index 3ada407..4a9a181 100644
--- a/third_party/rust/autocxx_parser/v0_16/crate/src/config.rs
+++ b/third_party/rust/autocxx_parser/v0_17/crate/src/config.rs
@@ -1,24 +1,18 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
-use std::collections::HashSet;
+use std::{borrow::Cow, collections::HashSet};
 
 use proc_macro2::Span;
 use quote::ToTokens;
 use syn::{
     parse::{Parse, ParseStream},
-    LitStr, Signature, Token,
+    Signature, Token,
 };
 use syn::{Ident, Result as ParseResult};
 
@@ -70,31 +64,47 @@
     }
 }
 
+/// An entry in the allowlist.
+#[derive(Hash, Debug)]
+pub enum AllowlistEntry {
+    Item(String),
+    Namespace(String),
+}
+
+impl AllowlistEntry {
+    fn to_bindgen_item(&self) -> String {
+        match self {
+            AllowlistEntry::Item(i) => i.clone(),
+            AllowlistEntry::Namespace(ns) => format!("{}::.*", ns),
+        }
+    }
+}
+
 /// Allowlist configuration.
 #[derive(Hash, Debug)]
 pub enum Allowlist {
-    Unspecified(Vec<String>),
+    Unspecified(Vec<AllowlistEntry>),
     All,
-    Specific(Vec<String>),
+    Specific(Vec<AllowlistEntry>),
 }
 
 impl Allowlist {
-    pub fn push(&mut self, item: LitStr) -> ParseResult<()> {
+    pub fn push(&mut self, item: AllowlistEntry, span: Span) -> ParseResult<()> {
         match self {
             Allowlist::Unspecified(ref mut uncommitted_list) => {
                 let new_list = uncommitted_list
                     .drain(..)
-                    .chain(std::iter::once(item.value()))
+                    .chain(std::iter::once(item))
                     .collect();
                 *self = Allowlist::Specific(new_list);
             }
             Allowlist::All => {
                 return Err(syn::Error::new(
-                    item.span(),
-                    "use either generate!/generate_pod! or generate_all!, not both.",
+                    span,
+                    "use either generate!/generate_pod!/generate_ns! or generate_all!, not both.",
                 ))
             }
-            Allowlist::Specific(list) => list.push(item.value()),
+            Allowlist::Specific(list) => list.push(item),
         };
         Ok(())
     }
@@ -103,7 +113,7 @@
         if matches!(self, Allowlist::Specific(..)) {
             return Err(syn::Error::new(
                 ident.span(),
-                "use either generate!/generate_pod! or generate_all!, not both.",
+                "use either generate!/generate_pod!/generate_ns! or generate_all!, not both.",
             ));
         }
         *self = Allowlist::All;
@@ -191,13 +201,24 @@
                     let args;
                     syn::parenthesized!(args in input);
                     let generate: syn::LitStr = args.parse()?;
-                    allowlist.push(generate)?;
+                    allowlist.push(AllowlistEntry::Item(generate.value()), generate.span())?;
+                } else if ident == "generate_ns" {
+                    let args;
+                    syn::parenthesized!(args in input);
+                    let generate_ns: syn::LitStr = args.parse()?;
+                    allowlist.push(
+                        AllowlistEntry::Namespace(generate_ns.value()),
+                        generate_ns.span(),
+                    )?;
                 } else if ident == "generate_pod" {
                     let args;
                     syn::parenthesized!(args in input);
                     let generate_pod: syn::LitStr = args.parse()?;
                     pod_requests.push(generate_pod.value());
-                    allowlist.push(generate_pod)?;
+                    allowlist.push(
+                        AllowlistEntry::Item(generate_pod.value()),
+                        generate_pod.span(),
+                    )?;
                 } else if ident == "pod" {
                     let args;
                     syn::parenthesized!(args in input);
@@ -321,7 +342,16 @@
     /// we should raise an error if we weren't able to do so.
     pub fn must_generate_list(&self) -> Box<dyn Iterator<Item = String> + '_> {
         if let Allowlist::Specific(items) = &self.allowlist {
-            Box::new(items.iter().chain(self.pod_requests.iter()).cloned())
+            Box::new(
+                items
+                    .iter()
+                    .filter_map(|i| match i {
+                        AllowlistEntry::Item(i) => Some(i),
+                        AllowlistEntry::Namespace(_) => None,
+                    })
+                    .chain(self.pod_requests.iter())
+                    .cloned(),
+            )
         } else {
             Box::new(self.pod_requests.iter().cloned())
         }
@@ -334,8 +364,8 @@
             Allowlist::Specific(items) => Some(Box::new(
                 items
                     .iter()
-                    .chain(self.pod_requests.iter())
-                    .cloned()
+                    .map(AllowlistEntry::to_bindgen_item)
+                    .chain(self.pod_requests.iter().cloned())
                     .chain(self.active_utilities())
                     .chain(self.subclasses.iter().flat_map(|sc| {
                         [
@@ -353,10 +383,22 @@
         if self.exclude_utilities {
             Vec::new()
         } else {
-            vec![self.get_makestring_name()]
+            vec![self.get_makestring_name().to_string()]
         }
     }
 
+    fn is_subclass_or_superclass(&self, cpp_name: &str) -> bool {
+        self.subclasses
+            .iter()
+            .flat_map(|sc| {
+                [
+                    Cow::Owned(sc.subclass.to_string()),
+                    Cow::Borrowed(&sc.superclass),
+                ]
+            })
+            .any(|item| cpp_name == item.as_str())
+    }
+
     /// Whether this type is on the allowlist specified by the user.
     ///
     /// A note on the allowlist handling in general. It's used in two places:
@@ -366,16 +408,19 @@
     /// This second pass may seem redundant. But sometimes bindgen generates
     /// unnecessary stuff.
     pub fn is_on_allowlist(&self, cpp_name: &str) -> bool {
-        match self.bindgen_allowlist() {
-            None => true,
-            Some(mut items) => {
-                items.any(|item| item == cpp_name)
-                    || self.active_utilities().iter().any(|item| *item == cpp_name)
-                    || self.is_subclass_holder(cpp_name)
-                    || self.is_subclass_cpp(cpp_name)
-                    || self.is_rust_fun(cpp_name)
+        self.active_utilities().iter().any(|item| *item == cpp_name)
+            || self.is_subclass_or_superclass(cpp_name)
+            || self.is_subclass_holder(cpp_name)
+            || self.is_subclass_cpp(cpp_name)
+            || self.is_rust_fun(cpp_name)
+            || match &self.allowlist {
+                Allowlist::Unspecified(_) => panic!("Eek no allowlist yet"),
+                Allowlist::All => true,
+                Allowlist::Specific(items) => items.iter().any(|entry| match entry {
+                    AllowlistEntry::Item(i) => i == cpp_name,
+                    AllowlistEntry::Namespace(ns) => cpp_name.starts_with(ns),
+                }),
             }
-        }
     }
 
     pub fn is_on_blocklist(&self, cpp_name: &str) -> bool {
@@ -390,14 +435,18 @@
         self.blocklist.iter()
     }
 
-    pub fn get_makestring_name(&self) -> String {
-        format!(
-            "autocxx_make_string_{}",
-            self.mod_name
-                .as_ref()
-                .map(|i| i.to_string())
-                .unwrap_or_else(|| "default".into())
-        )
+    /// In case there are multiple sets of ffi mods in a single binary,
+    /// endeavor to return a name which can be used to make symbols
+    /// unique.
+    pub fn uniquify_name_per_mod<'a>(&self, name: &'a str) -> Cow<'a, str> {
+        match self.mod_name.as_ref() {
+            None => Cow::Borrowed(name),
+            Some(md) => Cow::Owned(format!("{}_{}", name, md)),
+        }
+    }
+
+    pub fn get_makestring_name(&self) -> Cow<str> {
+        self.uniquify_name_per_mod("autocxx_make_string")
     }
 
     pub fn is_rust_type(&self, id: &Ident) -> bool {
@@ -451,7 +500,7 @@
             } else {
                 Err(syn::Error::new(
                     Span::call_site(),
-                    "expected either generate! or generate_all!",
+                    "expected either generate!/generate_ns! or generate_all!",
                 ))
             }
         } else {
@@ -505,7 +554,12 @@
             Allowlist::All => tokens.extend(quote! { generate_all!() }),
             Allowlist::Specific(items) => {
                 for i in items {
-                    tokens.extend(quote! { generate!(#i) });
+                    match i {
+                        AllowlistEntry::Item(i) => tokens.extend(quote! { generate!(#i) }),
+                        AllowlistEntry::Namespace(ns) => {
+                            tokens.extend(quote! { generate_ns!(#ns) })
+                        }
+                    }
                 }
             }
             Allowlist::Unspecified(_) => panic!("Allowlist mode not yet determined"),
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/file_locations.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/file_locations.rs
similarity index 89%
rename from third_party/rust/autocxx_parser/v0_16/crate/src/file_locations.rs
rename to third_party/rust/autocxx_parser/v0_17/crate/src/file_locations.rs
index 2bcb65d..b353e06 100644
--- a/third_party/rust/autocxx_parser/v0_16/crate/src/file_locations.rs
+++ b/third_party/rust/autocxx_parser/v0_17/crate/src/file_locations.rs
@@ -1,16 +1,10 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use proc_macro2::TokenStream;
 use quote::quote;
@@ -29,7 +23,7 @@
 /// (based on a hash of the contents of `include_cpp!`.) But
 /// some types of build system need to know the precise file _name_
 /// produced by the codegen phase and passed into the macro phase,
-/// so we have a `AUTOCXX_RS_FILE` option for that.
+/// so we have some options for that. See `gen --help` for details.
 pub enum FileLocationStrategy {
     Custom(PathBuf),
     FromAutocxxRsFile(PathBuf),
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/lib.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/lib.rs
similarity index 72%
rename from third_party/rust/autocxx_parser/v0_16/crate/src/lib.rs
rename to third_party/rust/autocxx_parser/v0_17/crate/src/lib.rs
index b63635c..f0d988df 100644
--- a/third_party/rust/autocxx_parser/v0_16/crate/src/lib.rs
+++ b/third_party/rust/autocxx_parser/v0_17/crate/src/lib.rs
@@ -1,23 +1,19 @@
 // Copyright 2020 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![forbid(unsafe_code)]
 
 mod config;
 pub mod file_locations;
 mod path;
 mod subclass_attrs;
 
-pub use config::{IncludeCppConfig, RustFun, Subclass, UnsafePolicy};
+pub use config::{AllowlistEntry, IncludeCppConfig, RustFun, Subclass, UnsafePolicy};
 use file_locations::FileLocationStrategy;
 pub use path::RustPath;
 use proc_macro2::TokenStream as TokenStream2;
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/path.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/path.rs
similarity index 72%
rename from third_party/rust/autocxx_parser/v0_16/crate/src/path.rs
rename to third_party/rust/autocxx_parser/v0_17/crate/src/path.rs
index 222a90f..8958807 100644
--- a/third_party/rust/autocxx_parser/v0_16/crate/src/path.rs
+++ b/third_party/rust/autocxx_parser/v0_17/crate/src/path.rs
@@ -1,16 +1,10 @@
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use crate::ParseResult;
 use proc_macro2::Ident;
diff --git a/third_party/rust/autocxx_parser/v0_16/crate/src/subclass_attrs.rs b/third_party/rust/autocxx_parser/v0_17/crate/src/subclass_attrs.rs
similarity index 74%
rename from third_party/rust/autocxx_parser/v0_16/crate/src/subclass_attrs.rs
rename to third_party/rust/autocxx_parser/v0_17/crate/src/subclass_attrs.rs
index 3118ef1..9174bec 100644
--- a/third_party/rust/autocxx_parser/v0_16/crate/src/subclass_attrs.rs
+++ b/third_party/rust/autocxx_parser/v0_17/crate/src/subclass_attrs.rs
@@ -1,16 +1,10 @@
 // Copyright 2021 Google LLC
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
 
 use proc_macro2::Ident;
 use quote::ToTokens;
diff --git a/third_party/rust/heck/v0_3/crate/.cargo_vcs_info.json b/third_party/rust/heck/v0_3/crate/.cargo_vcs_info.json
deleted file mode 100644
index 43f6c71..0000000
--- a/third_party/rust/heck/v0_3/crate/.cargo_vcs_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "git": {
-    "sha1": "d2f9cda3a00a62ae1e0eeeea8f0a081c7598b8c5"
-  }
-}
diff --git a/third_party/rust/heck/v0_3/crate/src/camel.rs b/third_party/rust/heck/v0_3/crate/src/camel.rs
deleted file mode 100644
index 6949435..0000000
--- a/third_party/rust/heck/v0_3/crate/src/camel.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use crate::{capitalize, transform};
-
-/// This trait defines a camel case conversion.
-///
-/// In CamelCase, word boundaries are indicated by capital letters, including
-/// the first word.
-///
-/// ## Example:
-///
-/// ```rust
-/// use heck::CamelCase;
-///
-/// let sentence = "We are not in the least afraid of ruins.";
-/// assert_eq!(sentence.to_camel_case(), "WeAreNotInTheLeastAfraidOfRuins");
-/// ```
-pub trait CamelCase: ToOwned {
-    /// Convert this type to camel case.
-    fn to_camel_case(&self) -> Self::Owned;
-}
-
-impl CamelCase for str {
-    fn to_camel_case(&self) -> String {
-        transform(self, capitalize, |_| {})
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::CamelCase;
-
-    macro_rules! t {
-        ($t:ident : $s1:expr => $s2:expr) => {
-            #[test]
-            fn $t() {
-                assert_eq!($s1.to_camel_case(), $s2)
-            }
-        };
-    }
-
-    t!(test1: "CamelCase" => "CamelCase");
-    t!(test2: "This is Human case." => "ThisIsHumanCase");
-    t!(test3: "MixedUP_CamelCase, with some Spaces" => "MixedUpCamelCaseWithSomeSpaces");
-    t!(test4: "mixed_up_ snake_case, with some _spaces" => "MixedUpSnakeCaseWithSomeSpaces");
-    t!(test5: "kebab-case" => "KebabCase");
-    t!(test6: "SHOUTY_SNAKE_CASE" => "ShoutySnakeCase");
-    t!(test7: "snake_case" => "SnakeCase");
-    t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "ThisContainsAllKindsOfWordBoundaries");
-    t!(test9: "XΣXΣ baffle" => "XσxςBaffle");
-    t!(test10: "XMLHttpRequest" => "XmlHttpRequest");
-}
diff --git a/third_party/rust/heck/v0_3/crate/src/mixed.rs b/third_party/rust/heck/v0_3/crate/src/mixed.rs
deleted file mode 100644
index 71089f28..0000000
--- a/third_party/rust/heck/v0_3/crate/src/mixed.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use crate::{capitalize, lowercase, transform};
-
-/// This trait defines a mixed case conversion.
-///
-/// In mixedCase, word boundaries are indicated by capital letters, excepting
-/// the first word.
-///
-/// ## Example:
-///
-/// ```rust
-/// use heck::MixedCase;
-///
-/// let sentence = "It is we who built these palaces and cities.";
-/// assert_eq!(sentence.to_mixed_case(), "itIsWeWhoBuiltThesePalacesAndCities");
-/// ```
-pub trait MixedCase: ToOwned {
-    /// Convert this type to mixed case.
-    fn to_mixed_case(&self) -> Self::Owned;
-}
-
-impl MixedCase for str {
-    fn to_mixed_case(&self) -> String {
-        transform(
-            self,
-            |s, out| {
-                if out.is_empty() {
-                    lowercase(s, out);
-                } else {
-                    capitalize(s, out)
-                }
-            },
-            |_| {},
-        )
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::MixedCase;
-
-    macro_rules! t {
-        ($t:ident : $s1:expr => $s2:expr) => {
-            #[test]
-            fn $t() {
-                assert_eq!($s1.to_mixed_case(), $s2)
-            }
-        };
-    }
-
-    t!(test1: "CamelCase" => "camelCase");
-    t!(test2: "This is Human case." => "thisIsHumanCase");
-    t!(test3: "MixedUP CamelCase, with some Spaces" => "mixedUpCamelCaseWithSomeSpaces");
-    t!(test4: "mixed_up_ snake_case, with some _spaces" => "mixedUpSnakeCaseWithSomeSpaces");
-    t!(test5: "kebab-case" => "kebabCase");
-    t!(test6: "SHOUTY_SNAKE_CASE" => "shoutySnakeCase");
-    t!(test7: "snake_case" => "snakeCase");
-    t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "thisContainsAllKindsOfWordBoundaries");
-    t!(test9: "XΣXΣ baffle" => "xσxςBaffle");
-    t!(test10: "XMLHttpRequest" => "xmlHttpRequest");
-    // TODO unicode tests
-}
diff --git a/third_party/rust/heck/v0_3/BUILD.gn b/third_party/rust/heck/v0_4/BUILD.gn
similarity index 88%
rename from third_party/rust/heck/v0_3/BUILD.gn
rename to third_party/rust/heck/v0_4/BUILD.gn
index faa3ea9..3514d1d 100644
--- a/third_party/rust/heck/v0_3/BUILD.gn
+++ b/third_party/rust/heck/v0_4/BUILD.gn
@@ -6,7 +6,7 @@
 
 cargo_crate("lib") {
   crate_name = "heck"
-  epoch = "0.3"
+  epoch = "0.4"
   crate_type = "rlib"
 
   # Only for usage from third-party crates. Add the crate to
@@ -18,5 +18,4 @@
   build_native_rust_unit_tests = false
   sources = [ "crate/src/lib.rs" ]
   edition = "2018"
-  deps = [ "//third_party/rust/unicode_segmentation/v1:lib" ]
 }
diff --git a/third_party/rust/heck/v0_3/README.chromium b/third_party/rust/heck/v0_4/README.chromium
similarity index 74%
rename from third_party/rust/heck/v0_3/README.chromium
rename to third_party/rust/heck/v0_4/README.chromium
index 2f956a5..5b66f929 100644
--- a/third_party/rust/heck/v0_3/README.chromium
+++ b/third_party/rust/heck/v0_4/README.chromium
@@ -1,6 +1,6 @@
 Name: heck
 URL: https://crates.io/crates/heck
 Description: heck is a case conversion library.
-Version: 0.3.3
-Security Critical: no
+Version: 0.4.0
+Security Critical: yes
 License: Apache 2.0
diff --git a/third_party/rust/heck/v0_4/crate/.cargo_vcs_info.json b/third_party/rust/heck/v0_4/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..192652c
--- /dev/null
+++ b/third_party/rust/heck/v0_4/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "dbcfc7b8db8e532d1fad44518cf73e88d5212161"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/third_party/rust/heck/v0_4/crate/CHANGELOG.md b/third_party/rust/heck/v0_4/crate/CHANGELOG.md
new file mode 100644
index 0000000..1e02914
--- /dev/null
+++ b/third_party/rust/heck/v0_4/crate/CHANGELOG.md
@@ -0,0 +1,11 @@
+# 0.4.0
+
+Breaking changes:
+
+* Make unicode support optional (off by default). Enable the `unicode` crate
+  feature if you need unicode support.
+* Rename all traits from `SomeCase` to `ToSomeCase`, matching `std`s convention
+  of beginning trait names with a verb (`ToOwned`, `AsRef`, …)
+* Rename `ToMixedCase` to `ToLowerCamelCase`
+* Rename `ToCamelCase` to `ToUpperCamelCase`
+* Add `ToPascalCase` as an alias to `ToUpperCamelCase`
diff --git a/third_party/rust/heck/v0_3/crate/Cargo.toml b/third_party/rust/heck/v0_4/crate/Cargo.toml
similarity index 70%
rename from third_party/rust/heck/v0_3/crate/Cargo.toml
rename to third_party/rust/heck/v0_4/crate/Cargo.toml
index fc9c28dc..b72cceb 100644
--- a/third_party/rust/heck/v0_3/crate/Cargo.toml
+++ b/third_party/rust/heck/v0_4/crate/Cargo.toml
@@ -3,17 +3,16 @@
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
 #
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
 
 [package]
 edition = "2018"
 name = "heck"
-version = "0.3.3"
+version = "0.4.0"
 authors = ["Without Boats <woboats@gmail.com>"]
 include = ["src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"]
 description = "heck is a case conversion library."
@@ -25,3 +24,8 @@
 repository = "https://github.com/withoutboats/heck"
 [dependencies.unicode-segmentation]
 version = "1.2.0"
+optional = true
+
+[features]
+default = []
+unicode = ["unicode-segmentation"]
diff --git a/third_party/rust/heck/v0_3/crate/Cargo.toml.orig b/third_party/rust/heck/v0_4/crate/Cargo.toml.orig
similarity index 77%
rename from third_party/rust/heck/v0_3/crate/Cargo.toml.orig
rename to third_party/rust/heck/v0_4/crate/Cargo.toml.orig
index 80acd39..1de9796 100644
--- a/third_party/rust/heck/v0_3/crate/Cargo.toml.orig
+++ b/third_party/rust/heck/v0_4/crate/Cargo.toml.orig
@@ -1,7 +1,7 @@
 [package]
 authors = ["Without Boats <woboats@gmail.com>"]
 name = "heck"
-version = "0.3.3"
+version = "0.4.0"
 edition = "2018"
 license = "MIT OR Apache-2.0"
 description = "heck is a case conversion library."
@@ -12,5 +12,9 @@
 readme = "README.md"
 include = ["src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"]
 
+[features]
+default = []
+unicode = ["unicode-segmentation"]
+
 [dependencies]
-unicode-segmentation = "1.2.0"
+unicode-segmentation = { version = "1.2.0", optional = true }
diff --git a/third_party/rust/heck/v0_3/crate/LICENSE-APACHE b/third_party/rust/heck/v0_4/crate/LICENSE-APACHE
similarity index 100%
rename from third_party/rust/heck/v0_3/crate/LICENSE-APACHE
rename to third_party/rust/heck/v0_4/crate/LICENSE-APACHE
diff --git a/third_party/rust/heck/v0_3/crate/LICENSE-MIT b/third_party/rust/heck/v0_4/crate/LICENSE-MIT
similarity index 100%
rename from third_party/rust/heck/v0_3/crate/LICENSE-MIT
rename to third_party/rust/heck/v0_4/crate/LICENSE-MIT
diff --git a/third_party/rust/heck/v0_3/crate/README.md b/third_party/rust/heck/v0_4/crate/README.md
similarity index 96%
rename from third_party/rust/heck/v0_3/crate/README.md
rename to third_party/rust/heck/v0_4/crate/README.md
index 33f2a5d..5099339 100644
--- a/third_party/rust/heck/v0_3/crate/README.md
+++ b/third_party/rust/heck/v0_4/crate/README.md
@@ -30,11 +30,11 @@
 
 ## Cases contained in this library:
 
-1. CamelCase
-2. snake_case
-3. kebab-case
-4. SHOUTY_SNAKE_CASE
-5. mixedCase
+1. UpperCamelCase
+2. lowerCamelCase
+3. snake_case
+4. kebab-case
+5. SHOUTY_SNAKE_CASE
 6. Title Case
 7. SHOUTY-KEBAB-CASE
 
diff --git a/third_party/rust/heck/v0_3/crate/src/kebab.rs b/third_party/rust/heck/v0_4/crate/src/kebab.rs
similarity index 65%
rename from third_party/rust/heck/v0_3/crate/src/kebab.rs
rename to third_party/rust/heck/v0_4/crate/src/kebab.rs
index 75e5978..6cce5a5 100644
--- a/third_party/rust/heck/v0_3/crate/src/kebab.rs
+++ b/third_party/rust/heck/v0_4/crate/src/kebab.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use crate::{lowercase, transform};
 
 /// This trait defines a kebab case conversion.
@@ -7,25 +9,43 @@
 /// ## Example:
 ///
 /// ```rust
-/// use heck::KebabCase;
+/// use heck::ToKebabCase;
 ///
 /// let sentence = "We are going to inherit the earth.";
 /// assert_eq!(sentence.to_kebab_case(), "we-are-going-to-inherit-the-earth");
 /// ```
-pub trait KebabCase: ToOwned {
+pub trait ToKebabCase: ToOwned {
     /// Convert this type to kebab case.
     fn to_kebab_case(&self) -> Self::Owned;
 }
 
-impl KebabCase for str {
+impl ToKebabCase for str {
     fn to_kebab_case(&self) -> Self::Owned {
-        transform(self, lowercase, |s| s.push('-'))
+        AsKebabCase(self).to_string()
+    }
+}
+
+/// This wrapper performs a kebab case conversion in [`fmt::Display`].
+///
+/// ## Example:
+///
+/// ```
+/// use heck::AsKebabCase;
+///
+/// let sentence = "We are going to inherit the earth.";
+/// assert_eq!(format!("{}", AsKebabCase(sentence)), "we-are-going-to-inherit-the-earth");
+/// ```
+pub struct AsKebabCase<T: AsRef<str>>(pub T);
+
+impl<T: AsRef<str>> fmt::Display for AsKebabCase<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        transform(self.0.as_ref(), lowercase, |f| write!(f, "-"), f)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::KebabCase;
+    use super::ToKebabCase;
 
     macro_rules! t {
         ($t:ident : $s1:expr => $s2:expr) => {
@@ -44,6 +64,7 @@
     t!(test6: "SHOUTY_SNAKE_CASE" => "shouty-snake-case");
     t!(test7: "snake_case" => "snake-case");
     t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "this-contains-all-kinds-of-word-boundaries");
+    #[cfg(feature = "unicode")]
     t!(test9: "XΣXΣ baffle" => "xσxς-baffle");
     t!(test10: "XMLHttpRequest" => "xml-http-request");
 }
diff --git a/third_party/rust/heck/v0_3/crate/src/lib.rs b/third_party/rust/heck/v0_4/crate/src/lib.rs
similarity index 71%
rename from third_party/rust/heck/v0_3/crate/src/lib.rs
rename to third_party/rust/heck/v0_4/crate/src/lib.rs
index 00a9fbea..21a45f0 100644
--- a/third_party/rust/heck/v0_3/crate/src/lib.rs
+++ b/third_party/rust/heck/v0_4/crate/src/lib.rs
@@ -30,37 +30,57 @@
 //!
 //! ### Cases contained in this library:
 //!
-//! 1. CamelCase
-//! 2. snake_case
-//! 3. kebab-case
-//! 4. SHOUTY_SNAKE_CASE
-//! 5. mixedCase
+//! 1. UpperCamelCase
+//! 2. lowerCamelCase
+//! 3. snake_case
+//! 4. kebab-case
+//! 5. SHOUTY_SNAKE_CASE
 //! 6. Title Case
 //! 7. SHOUTY-KEBAB-CASE
 #![deny(missing_docs)]
+#![forbid(unsafe_code)]
 
-mod camel;
 mod kebab;
-mod mixed;
+mod lower_camel;
 mod shouty_kebab;
 mod shouty_snake;
 mod snake;
 mod title;
+mod upper_camel;
 
-pub use camel::CamelCase;
-pub use kebab::KebabCase;
-pub use mixed::MixedCase;
-pub use shouty_kebab::ShoutyKebabCase;
-pub use shouty_snake::{ShoutySnakeCase, ShoutySnekCase};
-pub use snake::{SnakeCase, SnekCase};
-pub use title::TitleCase;
+pub use kebab::{AsKebabCase, ToKebabCase};
+pub use lower_camel::{AsLowerCamelCase, ToLowerCamelCase};
+pub use shouty_kebab::{AsShoutyKebabCase, ToShoutyKebabCase};
+pub use shouty_snake::{
+    AsShoutySnakeCase, AsShoutySnakeCase as AsShoutySnekCase, ToShoutySnakeCase, ToShoutySnekCase,
+};
+pub use snake::{AsSnakeCase, AsSnakeCase as AsSnekCase, ToSnakeCase, ToSnekCase};
+pub use title::{AsTitleCase, ToTitleCase};
+pub use upper_camel::{
+    AsUpperCamelCase, AsUpperCamelCase as AsPascalCase, ToPascalCase, ToUpperCamelCase,
+};
 
-use unicode_segmentation::UnicodeSegmentation;
+use std::fmt;
 
-fn transform<F, G>(s: &str, with_word: F, boundary: G) -> String
+#[cfg(feature = "unicode")]
+fn get_iterator(s: &str) -> unicode_segmentation::UnicodeWords {
+    use unicode_segmentation::UnicodeSegmentation;
+    s.unicode_words()
+}
+#[cfg(not(feature = "unicode"))]
+fn get_iterator(s: &str) -> impl Iterator<Item = &str> {
+    s.split(|letter: char| !letter.is_ascii_alphanumeric())
+}
+
+fn transform<F, G>(
+    s: &str,
+    mut with_word: F,
+    mut boundary: G,
+    f: &mut fmt::Formatter,
+) -> fmt::Result
 where
-    F: Fn(&str, &mut String),
-    G: Fn(&mut String),
+    F: FnMut(&str, &mut fmt::Formatter) -> fmt::Result,
+    G: FnMut(&mut fmt::Formatter) -> fmt::Result,
 {
     /// Tracks the current 'mode' of the transformation algorithm as it scans
     /// the input string.
@@ -82,10 +102,9 @@
         Uppercase,
     }
 
-    let mut out = String::new();
     let mut first_word = true;
 
-    for word in s.unicode_words() {
+    for word in get_iterator(s) {
         let mut char_indices = word.char_indices().peekable();
         let mut init = 0;
         let mut mode = WordMode::Boundary;
@@ -114,9 +133,9 @@
                 // not uppercase and next is uppercase
                 if next == '_' || (next_mode == WordMode::Lowercase && next.is_uppercase()) {
                     if !first_word {
-                        boundary(&mut out);
+                        boundary(f)?;
                     }
-                    with_word(&word[init..next_i], &mut out);
+                    with_word(&word[init..next_i], f)?;
                     first_word = false;
                     init = next_i;
                     mode = WordMode::Boundary;
@@ -125,11 +144,11 @@
                 // is lowercase, word boundary before
                 } else if mode == WordMode::Uppercase && c.is_uppercase() && next.is_lowercase() {
                     if !first_word {
-                        boundary(&mut out);
+                        boundary(f)?;
                     } else {
                         first_word = false;
                     }
-                    with_word(&word[init..i], &mut out);
+                    with_word(&word[init..i], f)?;
                     init = i;
                     mode = WordMode::Boundary;
 
@@ -140,42 +159,48 @@
             } else {
                 // Collect trailing characters as a word
                 if !first_word {
-                    boundary(&mut out);
+                    boundary(f)?;
                 } else {
                     first_word = false;
                 }
-                with_word(&word[init..], &mut out);
+                with_word(&word[init..], f)?;
                 break;
             }
         }
     }
 
-    out
+    Ok(())
 }
 
-fn lowercase(s: &str, out: &mut String) {
+fn lowercase(s: &str, f: &mut fmt::Formatter) -> fmt::Result {
     let mut chars = s.chars().peekable();
     while let Some(c) = chars.next() {
         if c == 'Σ' && chars.peek().is_none() {
-            out.push('ς');
+            write!(f, "ς")?;
         } else {
-            out.extend(c.to_lowercase());
+            write!(f, "{}", c.to_lowercase())?;
         }
     }
+
+    Ok(())
 }
 
-fn uppercase(s: &str, out: &mut String) {
+fn uppercase(s: &str, f: &mut fmt::Formatter) -> fmt::Result {
     for c in s.chars() {
-        out.extend(c.to_uppercase())
+        write!(f, "{}", c.to_uppercase())?;
     }
+
+    Ok(())
 }
 
-fn capitalize(s: &str, out: &mut String) {
+fn capitalize(s: &str, f: &mut fmt::Formatter) -> fmt::Result {
     let mut char_indices = s.char_indices();
     if let Some((_, c)) = char_indices.next() {
-        out.extend(c.to_uppercase());
+        write!(f, "{}", c.to_uppercase())?;
         if let Some((i, _)) = char_indices.next() {
-            lowercase(&s[i..], out);
+            lowercase(&s[i..], f)?;
         }
     }
+
+    Ok(())
 }
diff --git a/third_party/rust/heck/v0_4/crate/src/lower_camel.rs b/third_party/rust/heck/v0_4/crate/src/lower_camel.rs
new file mode 100644
index 0000000..f1d6c94
--- /dev/null
+++ b/third_party/rust/heck/v0_4/crate/src/lower_camel.rs
@@ -0,0 +1,85 @@
+use std::fmt;
+
+use crate::{capitalize, lowercase, transform};
+
+/// This trait defines a lower camel case conversion.
+///
+/// In lowerCamelCase, word boundaries are indicated by capital letters,
+/// excepting the first word.
+///
+/// ## Example:
+///
+/// ```rust
+/// use heck::ToLowerCamelCase;
+///
+/// let sentence = "It is we who built these palaces and cities.";
+/// assert_eq!(sentence.to_lower_camel_case(), "itIsWeWhoBuiltThesePalacesAndCities");
+/// ```
+pub trait ToLowerCamelCase: ToOwned {
+    /// Convert this type to lower camel case.
+    fn to_lower_camel_case(&self) -> Self::Owned;
+}
+
+impl ToLowerCamelCase for str {
+    fn to_lower_camel_case(&self) -> String {
+        AsLowerCamelCase(self).to_string()
+    }
+}
+
+/// This wrapper performs a lower camel case conversion in [`fmt::Display`].
+///
+/// ## Example:
+///
+/// ```
+/// use heck::AsLowerCamelCase;
+///
+/// let sentence = "It is we who built these palaces and cities.";
+/// assert_eq!(format!("{}", AsLowerCamelCase(sentence)), "itIsWeWhoBuiltThesePalacesAndCities");
+/// ```
+pub struct AsLowerCamelCase<T: AsRef<str>>(pub T);
+
+impl<T: AsRef<str>> fmt::Display for AsLowerCamelCase<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let mut first = true;
+        transform(
+            self.0.as_ref(),
+            |s, f| {
+                if first {
+                    first = false;
+                    lowercase(s, f)
+                } else {
+                    capitalize(s, f)
+                }
+            },
+            |_| Ok(()),
+            f,
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::ToLowerCamelCase;
+
+    macro_rules! t {
+        ($t:ident : $s1:expr => $s2:expr) => {
+            #[test]
+            fn $t() {
+                assert_eq!($s1.to_lower_camel_case(), $s2)
+            }
+        };
+    }
+
+    t!(test1: "CamelCase" => "camelCase");
+    t!(test2: "This is Human case." => "thisIsHumanCase");
+    t!(test3: "MixedUP CamelCase, with some Spaces" => "mixedUpCamelCaseWithSomeSpaces");
+    t!(test4: "mixed_up_ snake_case, with some _spaces" => "mixedUpSnakeCaseWithSomeSpaces");
+    t!(test5: "kebab-case" => "kebabCase");
+    t!(test6: "SHOUTY_SNAKE_CASE" => "shoutySnakeCase");
+    t!(test7: "snake_case" => "snakeCase");
+    t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "thisContainsAllKindsOfWordBoundaries");
+    #[cfg(feature = "unicode")]
+    t!(test9: "XΣXΣ baffle" => "xσxςBaffle");
+    t!(test10: "XMLHttpRequest" => "xmlHttpRequest");
+    // TODO unicode tests
+}
diff --git a/third_party/rust/heck/v0_3/crate/src/shouty_kebab.rs b/third_party/rust/heck/v0_4/crate/src/shouty_kebab.rs
similarity index 65%
rename from third_party/rust/heck/v0_3/crate/src/shouty_kebab.rs
rename to third_party/rust/heck/v0_4/crate/src/shouty_kebab.rs
index 0225471..e679978 100644
--- a/third_party/rust/heck/v0_3/crate/src/shouty_kebab.rs
+++ b/third_party/rust/heck/v0_4/crate/src/shouty_kebab.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use crate::{transform, uppercase};
 
 /// This trait defines a shouty kebab case conversion.
@@ -8,25 +10,43 @@
 /// ## Example:
 ///
 /// ```rust
-/// use heck::ShoutyKebabCase;
+/// use heck::ToShoutyKebabCase;
 ///
 /// let sentence = "We are going to inherit the earth.";
 /// assert_eq!(sentence.to_shouty_kebab_case(), "WE-ARE-GOING-TO-INHERIT-THE-EARTH");
 /// ```
-pub trait ShoutyKebabCase: ToOwned {
+pub trait ToShoutyKebabCase: ToOwned {
     /// Convert this type to shouty kebab case.
     fn to_shouty_kebab_case(&self) -> Self::Owned;
 }
 
-impl ShoutyKebabCase for str {
+impl ToShoutyKebabCase for str {
     fn to_shouty_kebab_case(&self) -> Self::Owned {
-        transform(self, uppercase, |s| s.push('-'))
+        AsShoutyKebabCase(self).to_string()
+    }
+}
+
+/// This wrapper performs a kebab case conversion in [`fmt::Display`].
+///
+/// ## Example:
+///
+/// ```
+/// use heck::AsShoutyKebabCase;
+///
+/// let sentence = "We are going to inherit the earth.";
+/// assert_eq!(format!("{}", AsShoutyKebabCase(sentence)), "WE-ARE-GOING-TO-INHERIT-THE-EARTH");
+/// ```
+pub struct AsShoutyKebabCase<T: AsRef<str>>(pub T);
+
+impl<T: AsRef<str>> fmt::Display for AsShoutyKebabCase<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        transform(self.0.as_ref(), uppercase, |f| write!(f, "-"), f)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::ShoutyKebabCase;
+    use super::ToShoutyKebabCase;
 
     macro_rules! t {
         ($t:ident : $s1:expr => $s2:expr) => {
@@ -45,6 +65,7 @@
     t!(test6: "SHOUTY_SNAKE_CASE" => "SHOUTY-SNAKE-CASE");
     t!(test7: "snake_case" => "SNAKE-CASE");
     t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "THIS-CONTAINS-ALL-KINDS-OF-WORD-BOUNDARIES");
+    #[cfg(feature = "unicode")]
     t!(test9: "XΣXΣ baffle" => "XΣXΣ-BAFFLE");
     t!(test10: "XMLHttpRequest" => "XML-HTTP-REQUEST");
     t!(test11: "SHOUTY-KEBAB-CASE" => "SHOUTY-KEBAB-CASE");
diff --git a/third_party/rust/heck/v0_3/crate/src/shouty_snake.rs b/third_party/rust/heck/v0_4/crate/src/shouty_snake.rs
similarity index 62%
rename from third_party/rust/heck/v0_3/crate/src/shouty_snake.rs
rename to third_party/rust/heck/v0_4/crate/src/shouty_snake.rs
index 8f4289a0..d5043755 100644
--- a/third_party/rust/heck/v0_3/crate/src/shouty_snake.rs
+++ b/third_party/rust/heck/v0_4/crate/src/shouty_snake.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use crate::{transform, uppercase};
 
 /// This trait defines a shouty snake case conversion.
@@ -8,39 +10,57 @@
 /// ## Example:
 ///
 /// ```rust
-/// use heck::ShoutySnakeCase;
-///     
+/// use heck::ToShoutySnakeCase;
+///
 /// let sentence = "That world is growing in this minute.";
 /// assert_eq!(sentence.to_shouty_snake_case(), "THAT_WORLD_IS_GROWING_IN_THIS_MINUTE");
 /// ```
-pub trait ShoutySnakeCase: ToOwned {
+pub trait ToShoutySnakeCase: ToOwned {
     /// Convert this type to shouty snake case.
     fn to_shouty_snake_case(&self) -> Self::Owned;
 }
 
-/// Oh heck, ShoutySnekCase is an alias for ShoutySnakeCase. See ShoutySnakeCase
-/// for more documentation.
-pub trait ShoutySnekCase: ToOwned {
+/// Oh heck, ToShoutySnekCase is an alias for ToShoutySnakeCase. See
+/// ToShoutySnakeCase for more documentation.
+pub trait ToShoutySnekCase: ToOwned {
     /// CONVERT THIS TYPE TO SNEK CASE.
     #[allow(non_snake_case)]
     fn TO_SHOUTY_SNEK_CASE(&self) -> Self::Owned;
 }
 
-impl<T: ?Sized + ShoutySnakeCase> ShoutySnekCase for T {
+impl<T: ?Sized + ToShoutySnakeCase> ToShoutySnekCase for T {
     fn TO_SHOUTY_SNEK_CASE(&self) -> Self::Owned {
         self.to_shouty_snake_case()
     }
 }
 
-impl ShoutySnakeCase for str {
+impl ToShoutySnakeCase for str {
     fn to_shouty_snake_case(&self) -> Self::Owned {
-        transform(self, uppercase, |s| s.push('_'))
+        AsShoutySnakeCase(self).to_string()
+    }
+}
+
+/// This wrapper performs a shouty snake  case conversion in [`fmt::Display`].
+///
+/// ## Example:
+///
+/// ```
+/// use heck::AsShoutySnakeCase;
+///
+/// let sentence = "That world is growing in this minute.";
+/// assert_eq!(format!("{}", AsShoutySnakeCase(sentence)), "THAT_WORLD_IS_GROWING_IN_THIS_MINUTE");
+/// ```
+pub struct AsShoutySnakeCase<T: AsRef<str>>(pub T);
+
+impl<T: AsRef<str>> fmt::Display for AsShoutySnakeCase<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        transform(self.0.as_ref(), uppercase, |f| write!(f, "_"), f)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::ShoutySnakeCase;
+    use super::ToShoutySnakeCase;
 
     macro_rules! t {
         ($t:ident : $s1:expr => $s2:expr) => {
@@ -59,6 +79,7 @@
     t!(test6: "SHOUTY_SNAKE_CASE" => "SHOUTY_SNAKE_CASE");
     t!(test7: "snake_case" => "SNAKE_CASE");
     t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "THIS_CONTAINS_ALL_KINDS_OF_WORD_BOUNDARIES");
+    #[cfg(feature = "unicode")]
     t!(test9: "XΣXΣ baffle" => "XΣXΣ_BAFFLE");
     t!(test10: "XMLHttpRequest" => "XML_HTTP_REQUEST");
 }
diff --git a/third_party/rust/heck/v0_3/crate/src/snake.rs b/third_party/rust/heck/v0_4/crate/src/snake.rs
similarity index 71%
rename from third_party/rust/heck/v0_3/crate/src/snake.rs
rename to third_party/rust/heck/v0_4/crate/src/snake.rs
index c1ad376..127a8642 100644
--- a/third_party/rust/heck/v0_3/crate/src/snake.rs
+++ b/third_party/rust/heck/v0_4/crate/src/snake.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use crate::{lowercase, transform};
 
 /// This trait defines a snake case conversion.
@@ -7,38 +9,56 @@
 /// ## Example:
 ///
 /// ```rust
-/// use heck::SnakeCase;
+/// use heck::ToSnakeCase;
 ///
 /// let sentence = "We carry a new world here, in our hearts.";
 /// assert_eq!(sentence.to_snake_case(), "we_carry_a_new_world_here_in_our_hearts");
 /// ```
-pub trait SnakeCase: ToOwned {
+pub trait ToSnakeCase: ToOwned {
     /// Convert this type to snake case.
     fn to_snake_case(&self) -> Self::Owned;
 }
 
-/// Oh heck, SnekCase is an alias for SnakeCase. See SnakeCase for
+/// Oh heck, SnekCase is an alias for ToSnakeCase. See ToSnakeCase for
 /// more documentation.
-pub trait SnekCase: ToOwned {
+pub trait ToSnekCase: ToOwned {
     /// Convert this type to snek case.
     fn to_snek_case(&self) -> Self::Owned;
 }
 
-impl<T: ?Sized + SnakeCase> SnekCase for T {
+impl<T: ?Sized + ToSnakeCase> ToSnekCase for T {
     fn to_snek_case(&self) -> Self::Owned {
         self.to_snake_case()
     }
 }
 
-impl SnakeCase for str {
+impl ToSnakeCase for str {
     fn to_snake_case(&self) -> String {
-        transform(self, lowercase, |s| s.push('_'))
+        AsSnakeCase(self).to_string()
+    }
+}
+
+/// This wrapper performs a snake case conversion in [`fmt::Display`].
+///
+/// ## Example:
+///
+/// ```
+/// use heck::AsSnakeCase;
+///
+/// let sentence = "We carry a new world here, in our hearts.";
+/// assert_eq!(format!("{}", AsSnakeCase(sentence)), "we_carry_a_new_world_here_in_our_hearts");
+/// ```
+pub struct AsSnakeCase<T: AsRef<str>>(pub T);
+
+impl<T: AsRef<str>> fmt::Display for AsSnakeCase<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        transform(self.0.as_ref(), lowercase, |f| write!(f, "_"), f)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::SnakeCase;
+    use super::ToSnakeCase;
 
     macro_rules! t {
         ($t:ident : $s1:expr => $s2:expr) => {
@@ -57,6 +77,7 @@
     t!(test6: "SHOUTY_SNAKE_CASE" => "shouty_snake_case");
     t!(test7: "snake_case" => "snake_case");
     t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "this_contains_all_kinds_of_word_boundaries");
+    #[cfg(feature = "unicode")]
     t!(test9: "XΣXΣ baffle" => "xσxς_baffle");
     t!(test10: "XMLHttpRequest" => "xml_http_request");
     t!(test11: "FIELD_NAME11" => "field_name11");
diff --git a/third_party/rust/heck/v0_3/crate/src/title.rs b/third_party/rust/heck/v0_4/crate/src/title.rs
similarity index 65%
rename from third_party/rust/heck/v0_3/crate/src/title.rs
rename to third_party/rust/heck/v0_4/crate/src/title.rs
index 015a9fa..fdf175b 100644
--- a/third_party/rust/heck/v0_3/crate/src/title.rs
+++ b/third_party/rust/heck/v0_4/crate/src/title.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use crate::{capitalize, transform};
 
 /// This trait defines a title case conversion.
@@ -8,25 +10,43 @@
 /// ## Example:
 ///
 /// ```rust
-/// use heck::TitleCase;
+/// use heck::ToTitleCase;
 ///
 /// let sentence = "We have always lived in slums and holes in the wall.";
 /// assert_eq!(sentence.to_title_case(), "We Have Always Lived In Slums And Holes In The Wall");
 /// ```
-pub trait TitleCase: ToOwned {
+pub trait ToTitleCase: ToOwned {
     /// Convert this type to title case.
     fn to_title_case(&self) -> Self::Owned;
 }
 
-impl TitleCase for str {
+impl ToTitleCase for str {
     fn to_title_case(&self) -> String {
-        transform(self, capitalize, |s| s.push(' '))
+        AsTitleCase(self).to_string()
+    }
+}
+
+/// This wrapper performs a title case conversion in [`fmt::Display`].
+///
+/// ## Example:
+///
+/// ```
+/// use heck::AsTitleCase;
+///
+/// let sentence = "We have always lived in slums and holes in the wall.";
+/// assert_eq!(format!("{}", AsTitleCase(sentence)), "We Have Always Lived In Slums And Holes In The Wall");
+/// ```
+pub struct AsTitleCase<T: AsRef<str>>(pub T);
+
+impl<T: AsRef<str>> fmt::Display for AsTitleCase<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        transform(self.0.as_ref(), capitalize, |f| write!(f, " "), f)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::TitleCase;
+    use super::ToTitleCase;
 
     macro_rules! t {
         ($t:ident : $s1:expr => $s2:expr) => {
@@ -45,6 +65,7 @@
     t!(test6: "SHOUTY_SNAKE_CASE" => "Shouty Snake Case");
     t!(test7: "snake_case" => "Snake Case");
     t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "This Contains All Kinds Of Word Boundaries");
+    #[cfg(feature = "unicode")]
     t!(test9: "XΣXΣ baffle" => "Xσxς Baffle");
     t!(test10: "XMLHttpRequest" => "Xml Http Request");
 }
diff --git a/third_party/rust/heck/v0_4/crate/src/upper_camel.rs b/third_party/rust/heck/v0_4/crate/src/upper_camel.rs
new file mode 100644
index 0000000..70bf4ac
--- /dev/null
+++ b/third_party/rust/heck/v0_4/crate/src/upper_camel.rs
@@ -0,0 +1,84 @@
+use std::fmt;
+
+use crate::{capitalize, transform};
+
+/// This trait defines an upper camel case conversion.
+///
+/// In UpperCamelCase, word boundaries are indicated by capital letters,
+/// including the first word.
+///
+/// ## Example:
+///
+/// ```rust
+/// use heck::ToUpperCamelCase;
+///
+/// let sentence = "We are not in the least afraid of ruins.";
+/// assert_eq!(sentence.to_upper_camel_case(), "WeAreNotInTheLeastAfraidOfRuins");
+/// ```
+pub trait ToUpperCamelCase: ToOwned {
+    /// Convert this type to upper camel case.
+    fn to_upper_camel_case(&self) -> Self::Owned;
+}
+
+impl ToUpperCamelCase for str {
+    fn to_upper_camel_case(&self) -> String {
+        AsUpperCamelCase(self).to_string()
+    }
+}
+
+/// ToPascalCase is an alias for ToUpperCamelCase. See ToUpperCamelCase for more
+/// documentation.
+pub trait ToPascalCase: ToOwned {
+    /// Convert this type to upper camel case.
+    fn to_pascal_case(&self) -> Self::Owned;
+}
+
+impl<T: ?Sized + ToUpperCamelCase> ToPascalCase for T {
+    fn to_pascal_case(&self) -> Self::Owned {
+        self.to_upper_camel_case()
+    }
+}
+
+/// This wrapper performs a upper camel case conversion in [`fmt::Display`].
+///
+/// ## Example:
+///
+/// ```
+/// use heck::AsUpperCamelCase;
+///
+/// let sentence = "We are not in the least afraid of ruins.";
+/// assert_eq!(format!("{}", AsUpperCamelCase(sentence)), "WeAreNotInTheLeastAfraidOfRuins");
+/// ```
+pub struct AsUpperCamelCase<T: AsRef<str>>(pub T);
+
+impl<T: AsRef<str>> fmt::Display for AsUpperCamelCase<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        transform(self.0.as_ref(), capitalize, |_| Ok(()), f)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::ToUpperCamelCase;
+
+    macro_rules! t {
+        ($t:ident : $s1:expr => $s2:expr) => {
+            #[test]
+            fn $t() {
+                assert_eq!($s1.to_upper_camel_case(), $s2)
+            }
+        };
+    }
+
+    t!(test1: "CamelCase" => "CamelCase");
+    t!(test2: "This is Human case." => "ThisIsHumanCase");
+    t!(test3: "MixedUP_CamelCase, with some Spaces" => "MixedUpCamelCaseWithSomeSpaces");
+    t!(test4: "mixed_up_ snake_case, with some _spaces" => "MixedUpSnakeCaseWithSomeSpaces");
+    t!(test5: "kebab-case" => "KebabCase");
+    t!(test6: "SHOUTY_SNAKE_CASE" => "ShoutySnakeCase");
+    t!(test7: "snake_case" => "SnakeCase");
+    t!(test8: "this-contains_ ALLKinds OfWord_Boundaries" => "ThisContainsAllKindsOfWordBoundaries");
+    #[cfg(feature = "unicode")]
+    t!(test9: "XΣXΣ baffle" => "XσxςBaffle");
+    t!(test10: "XMLHttpRequest" => "XmlHttpRequest");
+}
diff --git a/third_party/rust/strum_macros/v0_23/crate/.cargo_vcs_info.json b/third_party/rust/strum_macros/v0_23/crate/.cargo_vcs_info.json
deleted file mode 100644
index 72cacbd..0000000
--- a/third_party/rust/strum_macros/v0_23/crate/.cargo_vcs_info.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "git": {
-    "sha1": "5d733fd1f35690c51b43d20296548253c77665ac"
-  }
-}
diff --git a/third_party/rust/strum_macros/v0_23/BUILD.gn b/third_party/rust/strum_macros/v0_24/BUILD.gn
similarity index 93%
rename from third_party/rust/strum_macros/v0_23/BUILD.gn
rename to third_party/rust/strum_macros/v0_24/BUILD.gn
index f570764..16141e04 100644
--- a/third_party/rust/strum_macros/v0_23/BUILD.gn
+++ b/third_party/rust/strum_macros/v0_24/BUILD.gn
@@ -6,7 +6,7 @@
 
 cargo_crate("lib") {
   crate_name = "strum_macros"
-  epoch = "0.23"
+  epoch = "0.24"
   crate_type = "proc-macro"
 
   # Only for usage from third-party crates. Add the crate to
@@ -19,7 +19,7 @@
   sources = [ "crate/src/lib.rs" ]
   edition = "2018"
   deps = [
-    "//third_party/rust/heck/v0_3:lib",
+    "//third_party/rust/heck/v0_4:lib",
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
     "//third_party/rust/rustversion/v1:lib",
diff --git a/third_party/rust/strum_macros/v0_23/README.chromium b/third_party/rust/strum_macros/v0_24/README.chromium
similarity index 77%
rename from third_party/rust/strum_macros/v0_23/README.chromium
rename to third_party/rust/strum_macros/v0_24/README.chromium
index e156d8df..2181401e 100644
--- a/third_party/rust/strum_macros/v0_23/README.chromium
+++ b/third_party/rust/strum_macros/v0_24/README.chromium
@@ -1,6 +1,6 @@
 Name: strum_macros
 URL: https://crates.io/crates/strum_macros
 Description: Helpful macros for working with enums and strings
-Version: 0.23.1
-Security Critical: no
+Version: 0.24.0
+Security Critical: yes
 License: MIT
diff --git a/third_party/rust/strum_macros/v0_24/crate/.cargo_vcs_info.json b/third_party/rust/strum_macros/v0_24/crate/.cargo_vcs_info.json
new file mode 100644
index 0000000..d913ec6
--- /dev/null
+++ b/third_party/rust/strum_macros/v0_24/crate/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "94460f006cc6ecce6fe77262a073372d283ad0fe"
+  },
+  "path_in_vcs": "strum_macros"
+}
\ No newline at end of file
diff --git a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml
similarity index 95%
rename from third_party/rust/strum_macros/v0_23/crate/Cargo.toml
rename to third_party/rust/strum_macros/v0_24/crate/Cargo.toml
index 61ecf76..be175e68 100644
--- a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml
+++ b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2018"
 name = "strum_macros"
-version = "0.23.1"
+version = "0.24.0"
 authors = ["Peter Glotfelty <peter.glotfelty@microsoft.com>"]
 description = "Helpful macros for working with enums and strings"
 homepage = "https://github.com/Peternator7/strum"
@@ -27,7 +27,7 @@
 name = "strum_macros"
 proc-macro = true
 [dependencies.heck]
-version = "0.3"
+version = "0.4"
 
 [dependencies.proc-macro2]
 version = "1.0"
@@ -42,4 +42,4 @@
 version = "1.0"
 features = ["parsing", "extra-traits"]
 [dev-dependencies.strum]
-version = "0.20"
+version = "0.23"
diff --git a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml.orig b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml.orig
similarity index 93%
rename from third_party/rust/strum_macros/v0_23/crate/Cargo.toml.orig
rename to third_party/rust/strum_macros/v0_24/crate/Cargo.toml.orig
index 140f2e9..8dff380 100644
--- a/third_party/rust/strum_macros/v0_23/crate/Cargo.toml.orig
+++ b/third_party/rust/strum_macros/v0_24/crate/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "strum_macros"
-version = "0.23.1"
+version = "0.24.0"
 edition = "2018"
 authors = ["Peter Glotfelty <peter.glotfelty@microsoft.com>"]
 license = "MIT"
@@ -19,11 +19,11 @@
 name = "strum_macros"
 
 [dependencies]
-heck = "0.3"
+heck = "0.4"
 proc-macro2 = "1.0"
 quote = "1.0"
 rustversion = "1.0"
 syn = { version = "1.0", features = ["parsing", "extra-traits"] }
 
 [dev-dependencies]
-strum = "0.20"
+strum = "0.23"
diff --git a/third_party/rust/strum_macros/v0_23/crate/LICENSE b/third_party/rust/strum_macros/v0_24/crate/LICENSE
similarity index 100%
rename from third_party/rust/strum_macros/v0_23/crate/LICENSE
rename to third_party/rust/strum_macros/v0_24/crate/LICENSE
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/case_style.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/case_style.rs
similarity index 88%
rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/case_style.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/case_style.rs
index 352a277..42538260 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/case_style.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/case_style.rs
@@ -1,10 +1,13 @@
-use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase};
+use heck::{
+    ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToUpperCamelCase,
+};
 use std::str::FromStr;
 use syn::{
     parse::{Parse, ParseStream},
     Ident, LitStr,
 };
 
+#[allow(clippy::enum_variant_names)]
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum CaseStyle {
     CamelCase,
@@ -52,7 +55,7 @@
 impl FromStr for CaseStyle {
     type Err = ();
 
-    fn from_str(text: &str) -> Result<CaseStyle, ()> {
+    fn from_str(text: &str) -> Result<Self, ()> {
         Ok(match text {
             "camel_case" | "PascalCase" => CaseStyle::PascalCase,
             "camelCase" => CaseStyle::CamelCase,
@@ -80,9 +83,9 @@
         let ident_string = self.to_string();
         if let Some(case_style) = case_style {
             match case_style {
-                CaseStyle::PascalCase => ident_string.to_camel_case(),
+                CaseStyle::PascalCase => ident_string.to_upper_camel_case(),
                 CaseStyle::KebabCase => ident_string.to_kebab_case(),
-                CaseStyle::MixedCase => ident_string.to_mixed_case(),
+                CaseStyle::MixedCase => ident_string.to_lower_camel_case(),
                 CaseStyle::ShoutySnakeCase => ident_string.to_shouty_snake_case(),
                 CaseStyle::SnakeCase => ident_string.to_snake_case(),
                 CaseStyle::TitleCase => ident_string.to_title_case(),
@@ -90,7 +93,7 @@
                 CaseStyle::LowerCase => ident_string.to_lowercase(),
                 CaseStyle::ScreamingKebabCase => ident_string.to_kebab_case().to_uppercase(),
                 CaseStyle::CamelCase => {
-                    let camel_case = ident_string.to_camel_case();
+                    let camel_case = ident_string.to_upper_camel_case();
                     let mut pascal = String::with_capacity(camel_case.len());
                     let mut it = camel_case.chars();
                     if let Some(ch) = it.next() {
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/metadata.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/metadata.rs
similarity index 91%
rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/metadata.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/metadata.rs
index 6279e6a..08ca384 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/metadata.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/metadata.rs
@@ -5,7 +5,7 @@
     parse2, parse_str,
     punctuated::Punctuated,
     spanned::Spanned,
-    Attribute, DeriveInput, Ident, LitBool, LitStr, Path, Token, Variant, Visibility,
+    Attribute, DeriveInput, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue, Path, Token, Variant, Visibility,
 };
 
 use super::case_style::CaseStyle;
@@ -137,7 +137,7 @@
     /// Get all the strum metadata associated with an enum.
     fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>>;
 
-    /// Get all the strum_discriminants metadata associated with an enum.
+    /// Get all the `strum_discriminants` metadata associated with an enum.
     fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>;
 }
 
@@ -164,6 +164,9 @@
         kw: kw::serialize,
         value: LitStr,
     },
+    Documentation {
+        value: LitStr,
+    },
     ToString {
         kw: kw::to_string,
         value: LitStr,
@@ -240,7 +243,7 @@
     fn parse(input: ParseStream) -> syn::Result<Self> {
         use syn::ext::IdentExt;
 
-        let k = Ident::parse_any(&input)?;
+        let k = Ident::parse_any(input)?;
         let _: Token![=] = input.parse()?;
         let v = input.parse()?;
 
@@ -253,6 +256,7 @@
         match self {
             VariantMeta::Message { kw, .. } => kw.span,
             VariantMeta::DetailedMessage { kw, .. } => kw.span,
+            VariantMeta::Documentation { value } => value.span(),
             VariantMeta::Serialize { kw, .. } => kw.span,
             VariantMeta::ToString { kw, .. } => kw.span,
             VariantMeta::Disabled(kw) => kw.span,
@@ -270,7 +274,15 @@
 
 impl VariantExt for Variant {
     fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>> {
-        get_metadata_inner("strum", &self.attrs)
+        let result = get_metadata_inner("strum", &self.attrs)?;
+        self.attrs.iter()
+            .filter(|attr| attr.path.is_ident("doc"))
+            .try_fold(result, |mut vec, attr| {
+            if let Meta::NameValue(MetaNameValue { lit: Lit::Str(value), .. }) = attr.parse_meta()? {
+                vec.push(VariantMeta::Documentation { value })
+            }
+            Ok(vec)
+        })
     }
 }
 
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/mod.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/mod.rs
similarity index 90%
rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/mod.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/mod.rs
index 30787b28..11aebc8 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/mod.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/mod.rs
@@ -15,7 +15,7 @@
     syn::Error::new(Span::call_site(), "This macro only supports enums.")
 }
 
-pub fn strum_discriminants_passthrough_error(span: impl Spanned) -> syn::Error {
+pub fn strum_discriminants_passthrough_error(span: &impl Spanned) -> syn::Error {
     syn::Error::new(
         span.span(),
         "expected a pass-through attribute, e.g. #[strum_discriminants(serde(rename = \"var0\"))]",
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/type_props.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/type_props.rs
similarity index 95%
rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/type_props.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/type_props.rs
index 39b0a2bd..cdca79f3 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/type_props.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/type_props.rs
@@ -99,10 +99,8 @@
 
 impl StrumTypeProperties {
     pub fn crate_module_path(&self) -> Path {
-        if let Some(path) = &self.crate_module_path {
-            parse_quote!(#path)
-        } else {
-            parse_quote!(::strum)
-        }
+        self.crate_module_path
+            .as_ref()
+            .map_or_else(|| parse_quote!(::strum), |path| parse_quote!(#path))
     }
 }
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/helpers/variant_props.rs b/third_party/rust/strum_macros/v0_24/crate/src/helpers/variant_props.rs
similarity index 87%
rename from third_party/rust/strum_macros/v0_23/crate/src/helpers/variant_props.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/helpers/variant_props.rs
index a7e948622..a39d3ea1 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/helpers/variant_props.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/helpers/variant_props.rs
@@ -16,6 +16,7 @@
     pub ascii_case_insensitive: Option<bool>,
     pub message: Option<LitStr>,
     pub detailed_message: Option<LitStr>,
+    pub documentation: Vec<LitStr>,
     pub string_props: Vec<(LitStr, LitStr)>,
     serialize: Vec<LitStr>,
     to_string: Option<LitStr>,
@@ -29,17 +30,13 @@
     }
 
     pub fn get_preferred_name(&self, case_style: Option<CaseStyle>) -> LitStr {
-        if let Some(to_string) = &self.to_string {
-            to_string.clone()
-        } else {
-            let mut serialized = self.serialize.clone();
-            serialized.sort_by_key(|s| s.value().len());
-            if let Some(n) = serialized.pop() {
-                n
-            } else {
-                self.ident_as_str(case_style)
-            }
-        }
+        self.to_string.as_ref().cloned().unwrap_or_else(|| {
+            self.serialize
+                .iter()
+                .max_by_key(|s| s.value().len())
+                .cloned()
+                .unwrap_or_else(|| self.ident_as_str(case_style))
+        })
     }
 
     pub fn get_serializations(&self, case_style: Option<CaseStyle>) -> Vec<LitStr> {
@@ -58,8 +55,10 @@
 
 impl HasStrumVariantProperties for Variant {
     fn get_variant_properties(&self) -> syn::Result<StrumVariantProperties> {
-        let mut output = StrumVariantProperties::default();
-        output.ident = Some(self.ident.clone());
+        let mut output = StrumVariantProperties {
+            ident: Some(self.ident.clone()),
+            ..Default::default()
+        };
 
         let mut message_kw = None;
         let mut detailed_message_kw = None;
@@ -85,6 +84,9 @@
                     detailed_message_kw = Some(kw);
                     output.detailed_message = Some(value);
                 }
+                VariantMeta::Documentation { value } => {
+                    output.documentation.push(value);
+                }
                 VariantMeta::Serialize { value, .. } => {
                     output.serialize.push(value);
                 }
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/lib.rs b/third_party/rust/strum_macros/v0_24/crate/src/lib.rs
similarity index 92%
rename from third_party/rust/strum_macros/v0_23/crate/src/lib.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/lib.rs
index 73185fe9..30d3edd 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/lib.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/lib.rs
@@ -30,7 +30,7 @@
 
 /// Converts strings to enum variants based on their name.
 ///
-/// auto-derives `std::str::FromStr` on the enum (for Rust 1.34 and above, std::convert::TryFrom<&str>
+/// auto-derives `std::str::FromStr` on the enum (for Rust 1.34 and above, `std::convert::TryFrom<&str>`
 /// will be derived as well). Each variant of the enum will match on it's own name.
 /// This can be overridden using `serialize="DifferentName"` or `to_string="DifferentName"`
 /// on the attribute as shown below.
@@ -47,7 +47,7 @@
 /// See the [Additional Attributes](https://docs.rs/strum/0.22/strum/additional_attributes/index.html)
 /// Section for more information on using this feature.
 ///
-/// # Example howto use EnumString
+/// # Example howto use `EnumString`
 /// ```
 /// use std::str::FromStr;
 /// use strum_macros::EnumString;
@@ -78,14 +78,14 @@
 /// impl std::str::FromStr for Color {
 ///     type Err = ::strum::ParseError;
 ///
-///     fn from_str(s: &str) -> ::std::result::Result<Color, Self::Err> {
+///     fn from_str(s: &str) -> ::core::result::Result<Color, Self::Err> {
 ///         match s {
-///             "Red" => ::std::result::Result::Ok(Color::Red),
-///             "Green" => ::std::result::Result::Ok(Color::Green { range:Default::default() }),
-///             "blue" => ::std::result::Result::Ok(Color::Blue(Default::default())),
-///             "b" => ::std::result::Result::Ok(Color::Blue(Default::default())),
-///             s if s.eq_ignore_ascii_case("Black") => ::std::result::Result::Ok(Color::Black),
-///             _ => ::std::result::Result::Err(::strum::ParseError::VariantNotFound),
+///             "Red" => ::core::result::Result::Ok(Color::Red),
+///             "Green" => ::core::result::Result::Ok(Color::Green { range:Default::default() }),
+///             "blue" => ::core::result::Result::Ok(Color::Blue(Default::default())),
+///             "b" => ::core::result::Result::Ok(Color::Blue(Default::default())),
+///             s if s.eq_ignore_ascii_case("Black") => ::core::result::Result::Ok(Color::Black),
+///             _ => ::core::result::Result::Err(::strum::ParseError::VariantNotFound),
 ///         }
 ///     }
 /// }
@@ -160,7 +160,7 @@
     toks.into()
 }
 
-/// Implements Strum::VariantNames which adds an associated constant `VARIANTS` which is an array of discriminant names.
+/// Implements `Strum::VariantNames` which adds an associated constant `VARIANTS` which is an array of discriminant names.
 ///
 /// Adds an `impl` block for the `enum` that adds a static `VARIANTS` array of `&'static str` that are the discriminant names.
 /// This will respect the `serialize_all` attribute on the `enum` (like `#[strum(serialize_all = "snake_case")]`.
@@ -201,7 +201,7 @@
 
     let toks = macros::as_ref_str::as_static_str_inner(
         &ast,
-        macros::as_ref_str::GenerateTraitVariant::AsStaticStr,
+        &macros::as_ref_str::GenerateTraitVariant::AsStaticStr,
     )
     .unwrap_or_else(|err| err.to_compile_error());
     debug_print_generated(&ast, &toks);
@@ -243,7 +243,7 @@
 
     let toks = macros::as_ref_str::as_static_str_inner(
         &ast,
-        macros::as_ref_str::GenerateTraitVariant::From,
+        &macros::as_ref_str::GenerateTraitVariant::From,
     )
     .unwrap_or_else(|err| err.to_compile_error());
     debug_print_generated(&ast, &toks);
@@ -471,6 +471,11 @@
 /// Encode strings into the enum itself. The `strum_macros::EmumMessage` macro implements the `strum::EnumMessage` trait.
 /// `EnumMessage` looks for `#[strum(message="...")]` attributes on your variants.
 /// You can also provided a `detailed_message="..."` attribute to create a seperate more detailed message than the first.
+/// 
+/// `EnumMessage` also exposes the variants doc comments through `get_documentation()`. This is useful in some scenarios,
+/// but `get_message` should generally be preferred. Rust doc comments are intended for developer facing documentation,
+/// not end user messaging.
+/// 
 /// ```
 /// // You need to bring the trait into scope to use it
 /// use strum::EnumMessage;
@@ -479,6 +484,7 @@
 /// #[derive(strum_macros::EnumMessage, Debug)]
 /// #[allow(dead_code)]
 /// enum Color {
+///     /// Danger color.
 ///     #[strum(message = "Red", detailed_message = "This is very red")]
 ///     Red,
 ///     #[strum(message = "Simply Green")]
@@ -490,18 +496,25 @@
 /// // Generated code looks like more or less like this:
 /// /*
 /// impl ::strum::EnumMessage for Color {
-///     fn get_message(&self) -> ::std::option::Option<&'static str> {
+///     fn get_message(&self) -> ::core::option::Option<&'static str> {
 ///         match self {
-///             &Color::Red => ::std::option::Option::Some("Red"),
-///             &Color::Green {..} => ::std::option::Option::Some("Simply Green"),
+///             &Color::Red => ::core::option::Option::Some("Red"),
+///             &Color::Green {..} => ::core::option::Option::Some("Simply Green"),
 ///             _ => None
 ///         }
 ///     }
 ///
-///     fn get_detailed_message(&self) -> ::std::option::Option<&'static str> {
+///     fn get_detailed_message(&self) -> ::core::option::Option<&'static str> {
 ///         match self {
-///             &Color::Red => ::std::option::Option::Some("This is very red"),
-///             &Color::Green {..}=> ::std::option::Option::Some("Simply Green"),
+///             &Color::Red => ::core::option::Option::Some("This is very red"),
+///             &Color::Green {..}=> ::core::option::Option::Some("Simply Green"),
+///             _ => None
+///         }
+///     }
+///
+///     fn get_documentation(&self) -> ::std::option::Option<&'static str> {
+///         match self {
+///             &Color::Red => ::std::option::Option::Some("Danger color."),
 ///             _ => None
 ///         }
 ///     }
@@ -528,6 +541,7 @@
 /// let c = Color::Red;
 /// assert_eq!("Red", c.get_message().unwrap());
 /// assert_eq!("This is very red", c.get_detailed_message().unwrap());
+/// assert_eq!("Danger color.", c.get_documentation().unwrap());
 /// assert_eq!(["Red"], c.get_serializations());
 /// ```
 #[proc_macro_derive(EnumMessage, attributes(strum))]
@@ -613,7 +627,7 @@
 /// // Bring trait into scope
 /// use std::str::FromStr;
 /// use strum::{IntoEnumIterator, EnumMessage};
-/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString, EnumMessage};
+/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString};
 ///
 /// #[derive(Debug)]
 /// struct NonDefault;
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_count.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_count.rs
similarity index 100%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_count.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_count.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_discriminants.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_discriminants.rs
similarity index 93%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_discriminants.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_discriminants.rs
index e4b9af20..66eee46b4 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_discriminants.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_discriminants.rs
@@ -1,7 +1,7 @@
 use proc_macro2::{Span, TokenStream, TokenTree};
 use quote::{quote, ToTokens};
 use syn::parse_quote;
-use syn::{Data, DeriveInput};
+use syn::{Data, DeriveInput, Fields};
 
 use crate::helpers::{non_enum_error, strum_discriminants_passthrough_error, HasTypeProperties};
 
@@ -30,10 +30,7 @@
     };
 
     // Work out the name
-    let default_name = syn::Ident::new(
-        &format!("{}Discriminants", name.to_string()),
-        Span::call_site(),
-    );
+    let default_name = syn::Ident::new(&format!("{}Discriminants", name), Span::call_site());
 
     let discriminants_name = type_properties.discriminant_name.unwrap_or(default_name);
     let discriminants_vis = type_properties
@@ -69,11 +66,11 @@
                     let passthrough_attribute = match passthrough_group {
                         TokenTree::Group(ref group) => group.stream(),
                         _ => {
-                            return Err(strum_discriminants_passthrough_error(passthrough_group));
+                            return Err(strum_discriminants_passthrough_error(&passthrough_group));
                         }
                     };
                     if passthrough_attribute.is_empty() {
-                        return Err(strum_discriminants_passthrough_error(passthrough_group));
+                        return Err(strum_discriminants_passthrough_error(&passthrough_group));
                     }
                     Ok(quote! { #[#passthrough_attribute] })
                 } else {
@@ -108,14 +105,12 @@
         .iter()
         .map(|variant| {
             let ident = &variant.ident;
-
-            use syn::Fields::*;
             let params = match &variant.fields {
-                Unit => quote! {},
-                Unnamed(_fields) => {
+                Fields::Unit => quote! {},
+                Fields::Unnamed(_fields) => {
                     quote! { (..) }
                 }
-                Named(_fields) => {
+                Fields::Named(_fields) => {
                     quote! { { .. } }
                 }
             };
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_iter.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_iter.rs
similarity index 93%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_iter.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_iter.rs
index 515160c..3dc2abd 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_iter.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_iter.rs
@@ -1,6 +1,6 @@
 use proc_macro2::{Span, TokenStream};
 use quote::quote;
-use syn::{Data, DeriveInput, Ident};
+use syn::{Data, DeriveInput, Fields, Ident};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
@@ -35,21 +35,19 @@
     let mut arms = Vec::new();
     let mut idx = 0usize;
     for variant in variants {
-        use syn::Fields::*;
-
         if variant.get_variant_properties()?.disabled.is_some() {
             continue;
         }
 
         let ident = &variant.ident;
         let params = match &variant.fields {
-            Unit => quote! {},
-            Unnamed(fields) => {
-                let defaults = ::std::iter::repeat(quote!(::core::default::Default::default()))
+            Fields::Unit => quote! {},
+            Fields::Unnamed(fields) => {
+                let defaults = ::core::iter::repeat(quote!(::core::default::Default::default()))
                     .take(fields.unnamed.len());
                 quote! { (#(#defaults),*) }
             }
-            Named(fields) => {
+            Fields::Named(fields) => {
                 let fields = fields
                     .named
                     .iter()
@@ -67,7 +65,7 @@
     let iter_name = syn::parse_str::<Ident>(&format!("{}Iter", name)).unwrap();
 
     Ok(quote! {
-        #[allow(missing_docs)]
+        #[doc = "An iterator over the variants of [Self]"]
         #vis struct #iter_name #ty_generics {
             idx: usize,
             back_idx: usize,
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_messages.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_messages.rs
similarity index 64%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_messages.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_messages.rs
index 6e599d0..c056108 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_messages.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_messages.rs
@@ -1,6 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::{Data, DeriveInput};
+use syn::{Data, DeriveInput, Fields, LitStr};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
@@ -17,19 +17,20 @@
 
     let mut arms = Vec::new();
     let mut detailed_arms = Vec::new();
+    let mut documentation_arms = Vec::new();
     let mut serializations = Vec::new();
 
     for variant in variants {
         let variant_properties = variant.get_variant_properties()?;
         let messages = variant_properties.message.as_ref();
         let detailed_messages = variant_properties.detailed_message.as_ref();
+        let documentation = &variant_properties.documentation;
         let ident = &variant.ident;
 
-        use syn::Fields::*;
         let params = match variant.fields {
-            Unit => quote! {},
-            Unnamed(..) => quote! { (..) },
-            Named(..) => quote! { {..} },
+            Fields::Unit => quote! {},
+            Fields::Unnamed(..) => quote! { (..) },
+            Fields::Named(..) => quote! { {..} },
         };
 
         // You can't disable getting the serializations.
@@ -65,10 +66,34 @@
 
         if let Some(msg) = detailed_messages {
             let params = params.clone();
-            // Push the simple message.
+            // Push the detailed message.
             detailed_arms
                 .push(quote! { &#name::#ident #params => ::core::option::Option::Some(#msg) });
         }
+
+        if !documentation.is_empty() {
+            let params = params.clone();
+            // Strip a single leading space from each documentation line.
+            let documentation: Vec<LitStr> = documentation.iter().map(|lit_str| {
+                let line = lit_str.value();
+                if line.starts_with(' ') {
+                    LitStr::new(&line.as_str()[1..], lit_str.span())
+                } else {
+                    lit_str.clone()
+                }
+            }).collect();
+            if documentation.len() == 1 {
+                let text = &documentation[0];
+                documentation_arms
+                    .push(quote! { &#name::#ident #params => ::core::option::Option::Some(#text) });
+            } else {
+                // Push the documentation.
+                documentation_arms
+                    .push(quote! {
+                        &#name::#ident #params => ::core::option::Option::Some(concat!(#(concat!(#documentation, "\n")),*))
+                    });
+            }
+        }
     }
 
     if arms.len() < variants.len() {
@@ -79,6 +104,10 @@
         detailed_arms.push(quote! { _ => ::core::option::Option::None });
     }
 
+    if documentation_arms.len() < variants.len() {
+        documentation_arms.push(quote! { _ => ::core::option::Option::None });
+    }
+
     Ok(quote! {
         impl #impl_generics #strum_module_path::EnumMessage for #name #ty_generics #where_clause {
             fn get_message(&self) -> ::core::option::Option<&'static str> {
@@ -93,6 +122,12 @@
                 }
             }
 
+            fn get_documentation(&self) -> ::core::option::Option<&'static str> {
+                match self {
+                    #(#documentation_arms),*
+                }
+            }
+
             fn get_serializations(&self) -> &'static [&'static str] {
                 match self {
                     #(#serializations),*
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_properties.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_properties.rs
similarity index 90%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_properties.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_properties.rs
index 58265a74..0fe389e 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_properties.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_properties.rs
@@ -1,6 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::{Data, DeriveInput};
+use syn::{Data, DeriveInput, Fields};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
@@ -26,15 +26,14 @@
             continue;
         }
 
-        use syn::Fields::*;
         let params = match variant.fields {
-            Unit => quote! {},
-            Unnamed(..) => quote! { (..) },
-            Named(..) => quote! { {..} },
+            Fields::Unit => quote! {},
+            Fields::Unnamed(..) => quote! { (..) },
+            Fields::Named(..) => quote! { {..} },
         };
 
         for (key, value) in variant_properties.string_props {
-            string_arms.push(quote! { #key => ::core::option::Option::Some( #value )})
+            string_arms.push(quote! { #key => ::core::option::Option::Some( #value )});
         }
 
         string_arms.push(quote! { _ => ::core::option::Option::None });
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/enum_variant_names.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/enum_variant_names.rs
similarity index 100%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/enum_variant_names.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/enum_variant_names.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/from_repr.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/from_repr.rs
similarity index 89%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/from_repr.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/from_repr.rs
index 7fdb1b5..ec9f5577 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/from_repr.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/from_repr.rs
@@ -1,6 +1,7 @@
+use heck::ToShoutySnakeCase;
 use proc_macro2::{Span, TokenStream};
 use quote::{format_ident, quote};
-use syn::{Data, DeriveInput, PathArguments, Type, TypeParen};
+use syn::{Data, DeriveInput, Fields, PathArguments, Type, TypeParen};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties};
 
@@ -66,22 +67,20 @@
     let mut has_additional_data = false;
     let mut prev_const_var_ident = None;
     for variant in variants {
-        use syn::Fields::*;
-
         if variant.get_variant_properties()?.disabled.is_some() {
             continue;
         }
 
         let ident = &variant.ident;
         let params = match &variant.fields {
-            Unit => quote! {},
-            Unnamed(fields) => {
+            Fields::Unit => quote! {},
+            Fields::Unnamed(fields) => {
                 has_additional_data = true;
-                let defaults = ::std::iter::repeat(quote!(::core::default::Default::default()))
+                let defaults = ::core::iter::repeat(quote!(::core::default::Default::default()))
                     .take(fields.unnamed.len());
                 quote! { (#(#defaults),*) }
             }
-            Named(fields) => {
+            Fields::Named(fields) => {
                 has_additional_data = true;
                 let fields = fields
                     .named
@@ -91,7 +90,6 @@
             }
         };
 
-        use heck::ShoutySnakeCase;
         let const_var_str = format!("{}_DISCRIMINANT", variant.ident).to_shouty_snake_case();
         let const_var_ident = format_ident!("{}", const_var_str);
 
@@ -115,7 +113,7 @@
         quote! {}
     } else {
         #[rustversion::before(1.46)]
-        fn filter_by_rust_version(s: TokenStream) -> TokenStream {
+        fn filter_by_rust_version(_: TokenStream) -> TokenStream {
             quote! {}
         }
 
@@ -127,7 +125,9 @@
     };
 
     Ok(quote! {
+        #[allow(clippy::use_self)]
         impl #impl_generics #name #ty_generics #where_clause {
+            #[doc = "Try to create [Self] from the raw representation"]
             #vis #const_if_possible fn from_repr(discriminant: #discriminant_type) -> Option<#name #ty_generics> {
                 #(#constant_defs)*
                 match discriminant {
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/mod.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/mod.rs
similarity index 100%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/mod.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/mod.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/as_ref_str.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/as_ref_str.rs
similarity index 93%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/as_ref_str.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/as_ref_str.rs
index b487b36..4dfdc4b8 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/as_ref_str.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/as_ref_str.rs
@@ -1,6 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::{parse_quote, Data, DeriveInput};
+use syn::{parse_quote, Data, DeriveInput, Fields};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
@@ -15,7 +15,6 @@
     let type_properties = ast.get_type_properties()?;
 
     for variant in variants {
-        use syn::Fields::*;
         let ident = &variant.ident;
         let variant_properties = variant.get_variant_properties()?;
 
@@ -28,9 +27,9 @@
         // (i.e. always `enum.as_ref().to_string() == enum.to_string()`).
         let output = variant_properties.get_preferred_name(type_properties.case_style);
         let params = match variant.fields {
-            Unit => quote! {},
-            Unnamed(..) => quote! { (..) },
-            Named(..) => quote! { {..} },
+            Fields::Unit => quote! {},
+            Fields::Unnamed(..) => quote! { (..) },
+            Fields::Named(..) => quote! { {..} },
         };
 
         arms.push(quote! { #name::#ident #params => #output });
@@ -70,7 +69,7 @@
 
 pub fn as_static_str_inner(
     ast: &DeriveInput,
-    trait_variant: GenerateTraitVariant,
+    trait_variant: &GenerateTraitVariant,
 ) -> syn::Result<TokenStream> {
     let name = &ast.ident;
     let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/display.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/display.rs
similarity index 100%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/display.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/display.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/from_string.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/from_string.rs
similarity index 91%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/from_string.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/from_string.rs
index 526472e..d0754d2 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/from_string.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/from_string.rs
@@ -69,7 +69,7 @@
             Fields::Unit => quote! {},
             Fields::Unnamed(fields) => {
                 let defaults =
-                    ::std::iter::repeat(quote!(Default::default())).take(fields.unnamed.len());
+                    ::core::iter::repeat(quote!(Default::default())).take(fields.unnamed.len());
                 quote! { (#(#defaults),*) }
             }
             Fields::Named(fields) => {
@@ -100,10 +100,10 @@
 
     let try_from_str = try_from_str(
         name,
-        impl_generics,
-        ty_generics,
+        &impl_generics,
+        &ty_generics,
         where_clause,
-        strum_module_path,
+        &strum_module_path,
     );
 
     Ok(quote! {
@@ -115,10 +115,10 @@
 #[rustversion::before(1.34)]
 fn try_from_str(
     _name: &proc_macro2::Ident,
-    _impl_generics: syn::ImplGenerics,
-    _ty_generics: syn::TypeGenerics,
+    _impl_generics: &syn::ImplGenerics,
+    _ty_generics: &syn::TypeGenerics,
     _where_clause: Option<&syn::WhereClause>,
-    _strum_module_path: syn::Path,
+    _strum_module_path: &syn::Path,
 ) -> TokenStream {
     Default::default()
 }
@@ -126,10 +126,10 @@
 #[rustversion::since(1.34)]
 fn try_from_str(
     name: &proc_macro2::Ident,
-    impl_generics: syn::ImplGenerics,
-    ty_generics: syn::TypeGenerics,
+    impl_generics: &syn::ImplGenerics,
+    ty_generics: &syn::TypeGenerics,
     where_clause: Option<&syn::WhereClause>,
-    strum_module_path: syn::Path,
+    strum_module_path: &syn::Path,
 ) -> TokenStream {
     quote! {
         #[allow(clippy::use_self)]
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/mod.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/mod.rs
similarity index 100%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/mod.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/mod.rs
diff --git a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/to_string.rs b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/to_string.rs
similarity index 89%
rename from third_party/rust/strum_macros/v0_23/crate/src/macros/strings/to_string.rs
rename to third_party/rust/strum_macros/v0_24/crate/src/macros/strings/to_string.rs
index a22b674..7e4c483 100644
--- a/third_party/rust/strum_macros/v0_23/crate/src/macros/strings/to_string.rs
+++ b/third_party/rust/strum_macros/v0_24/crate/src/macros/strings/to_string.rs
@@ -1,6 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::{Data, DeriveInput};
+use syn::{Data, DeriveInput, Fields};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
@@ -15,7 +15,6 @@
     let type_properties = ast.get_type_properties()?;
     let mut arms = Vec::new();
     for variant in variants {
-        use syn::Fields::*;
         let ident = &variant.ident;
         let variant_properties = variant.get_variant_properties()?;
 
@@ -27,9 +26,9 @@
         let output = variant_properties.get_preferred_name(type_properties.case_style);
 
         let params = match variant.fields {
-            Unit => quote! {},
-            Unnamed(..) => quote! { (..) },
-            Named(..) => quote! { {..} },
+            Fields::Unit => quote! {},
+            Fields::Unnamed(..) => quote! { (..) },
+            Fields::Named(..) => quote! { {..} },
         };
 
         arms.push(quote! { #name::#ident #params => ::std::string::String::from(#output) });
diff --git a/third_party/rust/third_party.toml b/third_party/rust/third_party.toml
index 069cd62..d0cee63 100644
--- a/third_party/rust/third_party.toml
+++ b/third_party/rust/third_party.toml
@@ -37,8 +37,11 @@
 cxxbridge-cmd = "1"
 cxx = "1"
 serde = "1"
-autocxx = "0.16"
-autocxx-gen = "0.16"
+autocxx = "0.17"
+
+[dependencies.autocxx-gen]
+version = "0.17"
+features = [ "static" ]
 
 [dependencies.serde_json_lenient]
 version = "0.1"
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium
index 636339fd..858cdf1 100644
--- a/third_party/wpt_tools/README.chromium
+++ b/third_party/wpt_tools/README.chromium
@@ -1,7 +1,7 @@
 Name: web-platform-tests - Test Suites for Web Platform specifications
 Short Name: wpt
 URL: https://github.com/web-platform-tests/wpt/
-Version: e0a72ad1cebe5f0cb794627777754decb3597e40
+Version: 87c66f4e4498996d2b1d9cd2e0cf9c0f0a3f6a3d
 License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html)
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/third_party/wpt_tools/wpt/resources/testdriver-actions.js b/third_party/wpt_tools/wpt/resources/testdriver-actions.js
index ef097961..3e5ba74 100644
--- a/third_party/wpt_tools/wpt/resources/testdriver-actions.js
+++ b/third_party/wpt_tools/wpt/resources/testdriver-actions.js
@@ -29,7 +29,7 @@
    *    .keyDown("p")
    *    .keyUp("p");
    *
-   * actions.send();
+   * await actions.send();
    *
    * @param {number} [defaultTickDuration] - The default duration of a
    * tick. Be default this is set ot 16ms, which is one frame time
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt
index 6276db1..01e467a 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_edge.txt
@@ -1 +1 @@
-selenium==4.1.2
+selenium==4.1.3
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt
index b385a35..a829279 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_ie.txt
@@ -1,2 +1,2 @@
 mozprocess==1.3.0
-selenium==4.1.2
+selenium==4.1.3
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt
index b385a35..a829279 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_opera.txt
@@ -1,2 +1,2 @@
 mozprocess==1.3.0
-selenium==4.1.2
+selenium==4.1.3
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt
index cc5bdf9..7a28278 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_sauce.txt
@@ -1,2 +1,2 @@
-selenium==4.1.2
+selenium==4.1.3
 requests==2.27.1
diff --git a/third_party/zlib/google/test/data/Mixed Paths.zip b/third_party/zlib/google/test/data/Mixed Paths.zip
new file mode 100644
index 0000000..2af418b0
--- /dev/null
+++ b/third_party/zlib/google/test/data/Mixed Paths.zip
Binary files differ
diff --git a/third_party/zlib/google/zip.cc b/third_party/zlib/google/zip.cc
index 7f9af5b..1a43196e 100644
--- a/third_party/zlib/google/zip.cc
+++ b/third_party/zlib/google/zip.cc
@@ -206,7 +206,9 @@
   while (const ZipReader::Entry* const entry = reader.Next()) {
     if (entry->is_unsafe) {
       LOG(ERROR) << "Found unsafe entry " << Redact(entry->path) << " in ZIP";
-      return false;
+      if (!options.continue_on_error)
+        return false;
+      continue;
     }
 
     if (options.filter && !options.filter.Run(entry->path)) {
@@ -218,7 +220,8 @@
       // It's a directory.
       if (!directory_creator.Run(entry->path)) {
         LOG(ERROR) << "Cannot create directory " << Redact(entry->path);
-        return false;
+        if (!options.continue_on_error)
+          return false;
       }
 
       continue;
@@ -229,7 +232,8 @@
     if (!writer || !reader.ExtractCurrentEntry(writer.get())) {
       LOG(ERROR) << "Cannot extract file " << Redact(entry->path)
                  << " from ZIP";
-      return false;
+      if (!options.continue_on_error)
+        return false;
     }
   }
 
diff --git a/third_party/zlib/google/zip.h b/third_party/zlib/google/zip.h
index 621f0d9..25ec655 100644
--- a/third_party/zlib/google/zip.h
+++ b/third_party/zlib/google/zip.h
@@ -182,6 +182,9 @@
 
   // Password to decrypt the encrypted files.
   std::string password;
+
+  // Should ignore errors when extracting files?
+  bool continue_on_error = false;
 };
 
 typedef base::RepeatingCallback<std::unique_ptr<WriterDelegate>(
diff --git a/third_party/zlib/google/zip_unittest.cc b/third_party/zlib/google/zip_unittest.cc
index cf54991..8fbec32 100644
--- a/third_party/zlib/google/zip_unittest.cc
+++ b/third_party/zlib/google/zip_unittest.cc
@@ -471,8 +471,9 @@
   EXPECT_EQ("This is not encrypted.\n", contents);
 
   // No rubbish file should be left behind.
-  EXPECT_FALSE(
-      base::PathExists(test_dir_.AppendASCII("Encrypted ZipCrypto.txt")));
+  EXPECT_THAT(
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+      UnorderedElementsAre("ClearText.txt"));
 }
 
 TEST_F(ZipTest, UnzipEncryptedWithNoPassword) {
@@ -491,8 +492,25 @@
   EXPECT_EQ("This is not encrypted.\n", contents);
 
   // No rubbish file should be left behind.
-  EXPECT_FALSE(
-      base::PathExists(test_dir_.AppendASCII("Encrypted ZipCrypto.txt")));
+  EXPECT_THAT(
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+      UnorderedElementsAre("ClearText.txt"));
+}
+
+TEST_F(ZipTest, UnzipEncryptedContinueOnError) {
+  EXPECT_TRUE(
+      zip::Unzip(GetDataDirectory().AppendASCII("Different Encryptions.zip"),
+                 test_dir_, {.continue_on_error = true}));
+
+  std::string contents;
+  EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("ClearText.txt"),
+                                     &contents));
+  EXPECT_EQ("This is not encrypted.\n", contents);
+
+  // No rubbish file should be left behind.
+  EXPECT_THAT(
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+      UnorderedElementsAre("ClearText.txt"));
 }
 
 TEST_F(ZipTest, UnzipWrongCrc) {
@@ -500,7 +518,9 @@
       zip::Unzip(GetDataDirectory().AppendASCII("Wrong CRC.zip"), test_dir_));
 
   // No rubbish file should be left behind.
-  EXPECT_FALSE(base::PathExists(test_dir_.AppendASCII("Corrupted.txt")));
+  EXPECT_THAT(
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+      UnorderedElementsAre());
 }
 
 TEST_F(ZipTest, UnzipRepeatedDirName) {
@@ -637,6 +657,148 @@
 #endif
 }
 
+TEST_F(ZipTest, UnzipDifferentCasesContinueOnError) {
+  EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII(
+                             "Repeated File Name With Different Cases.zip"),
+                         test_dir_, {.continue_on_error = true}));
+
+  std::string contents;
+
+#if defined(OS_WIN) || defined(OS_MAC)
+  // Only the first file (with mixed case) has been extracted.
+  EXPECT_THAT(
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+      UnorderedElementsAre("Case"));
+
+  EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents));
+  EXPECT_EQ("Mixed case 111", contents);
+#else
+  // All the files have been extracted.
+  EXPECT_THAT(
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES),
+      UnorderedElementsAre("Case", "case", "CASE"));
+
+  EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("Case"), &contents));
+  EXPECT_EQ("Mixed case 111", contents);
+
+  EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("case"), &contents));
+  EXPECT_EQ("Lower case 22", contents);
+
+  EXPECT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("CASE"), &contents));
+  EXPECT_EQ("Upper case 3", contents);
+#endif
+}
+
+TEST_F(ZipTest, UnzipMixedPaths) {
+  EXPECT_TRUE(zip::Unzip(GetDataDirectory().AppendASCII("Mixed Paths.zip"),
+                         test_dir_, {.continue_on_error = true}));
+
+  std::unordered_set<std::string> want_paths = {
+#ifdef OS_WIN
+      "Dot",               //
+      "Space→",            //
+      "a\\b",              //
+      "u\\v\\w\\x\\y\\z",  //
+      "←Backslash2",       //
+#else
+      " ",                        // Invalid on Windows
+      "Angle <>",                 // Invalid on Windows
+      "Backslash1→\\",            //
+      "Backspace \x08",           // Invalid on Windows
+      "Bell \a",                  // Invalid on Windows
+      "C:",                       // Invalid on Windows
+      "C:\\",                     // Absolute path on Windows
+      "C:\\Temp",                 // Absolute path on Windows
+      "C:\\Temp\\",               // Absolute path on Windows
+      "C:\\Temp\\File",           // Absolute path on Windows
+      "Carriage Return \r",       // Invalid on Windows
+      "Colon :",                  // Invalid on Windows
+      "Dot .",                    // Becomes "Dot" on Windows
+      "Double quote \"",          // Invalid on Windows
+      "Escape \x1B",              // Invalid on Windows
+      "Line Feed \n",             // Invalid on Windows
+      "NUL .txt",                 // Disappears on Windows
+      "NUL",                      // Disappears on Windows
+      "NUL..txt",                 // Disappears on Windows
+      "NUL.tar.gz",               // Disappears on Windows
+      "NUL.txt",                  // Disappears on Windows
+      "Pipe |",                   // Invalid on Windows
+      "Question ?",               // Invalid on Windows
+      "Space→ ",                  // Becomes "Space→" on Windows
+      "Star *",                   // Invalid on Windows
+      "Tab \t",                   // Invalid on Windows
+      "\\\\server\\share\\file",  // Absolute path on Windows
+      "\\←Backslash2",            // Becomes "←Backslash2" on Windows
+      "a/b",                      //
+      "u/v/w/x/y/z",              //
+#ifndef OS_MAC
+      "CASE",                     //
+      "Case",                     //
+#endif
+#endif
+      " NUL.txt",                  //
+      " ←Space",                   //
+      "$HOME",                     //
+      "%TMP",                      //
+      "-",                         //
+      "...Tree",                   //
+      "..Two",                     //
+      ".One",                      //
+      "Ampersand &",               //
+      "At @",                      //
+      "Backslash3→\\←Backslash4",  //
+      "Backtick `",                //
+      "Caret ^",                   //
+      "Comma ,",                   //
+      "Curly {}",                  //
+      "Dash -",                    //
+      "Delete \x7F",               //
+      "Dollar $",                  //
+      "Equal =",                   //
+      "Euro €",                    //
+      "Exclamation !",             //
+      "FileOrDir",                 //
+      "First",                     //
+      "Hash #",                    //
+      "Last",                      //
+      "Percent %",                 //
+      "Plus +",                    //
+      "Quote '",                   //
+      "Round ()",                  //
+      "Semicolon ;",               //
+      "Smile \U0001F642",          //
+      "Square []",                 //
+      "String Terminator \u009C",  //
+      "Tilde ~",                   //
+      "Underscore _",              //
+      "case",                      //
+      "~",                         //
+  };
+
+  const std::vector<std::string> got_paths =
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::FILES);
+
+  for (const std::string& path : got_paths) {
+    EXPECT_TRUE(want_paths.erase(path))
+        << "Found unexpected file: " << std::quoted(path);
+  }
+
+  for (const std::string& path : want_paths) {
+    EXPECT_TRUE(false) << "Cannot find expected file: " << std::quoted(path);
+  }
+
+  EXPECT_THAT(
+      GetRelativePaths(test_dir_, base::FileEnumerator::FileType::DIRECTORIES),
+      UnorderedElementsAre(
+#ifdef OS_WIN
+          "Backslash3→", "Empty", "a", "u", "u\\v", "u\\v\\w", "u\\v\\w\\x",
+          "u\\v\\w\\x\\y"
+#else
+          "Empty", "a", "u", "u/v", "u/v/w", "u/v/w/x", "u/v/w/x/y"
+#endif
+          ));
+}
+
 TEST_F(ZipTest, UnzipWithDelegates) {
   auto dir_creator =
       base::BindLambdaForTesting([this](const base::FilePath& entry_path) {
@@ -664,6 +826,34 @@
   EXPECT_TRUE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt")));
 }
 
+TEST_F(ZipTest, UnzipOnlyDirectories) {
+  auto dir_creator =
+      base::BindLambdaForTesting([this](const base::FilePath& entry_path) {
+        return base::CreateDirectory(test_dir_.Append(entry_path));
+      });
+
+  // Always return a null WriterDelegate.
+  auto writer =
+      base::BindLambdaForTesting([](const base::FilePath& entry_path) {
+        return std::unique_ptr<zip::WriterDelegate>();
+      });
+
+  base::File file(GetDataDirectory().AppendASCII("test.zip"),
+                  base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ);
+  EXPECT_TRUE(zip::Unzip(file.GetPlatformFile(), writer, dir_creator,
+                         {.continue_on_error = true}));
+  base::FilePath dir = test_dir_;
+  base::FilePath dir_foo = dir.AppendASCII("foo");
+  base::FilePath dir_foo_bar = dir_foo.AppendASCII("bar");
+  EXPECT_FALSE(base::PathExists(dir.AppendASCII("foo.txt")));
+  EXPECT_TRUE(base::DirectoryExists(dir_foo));
+  EXPECT_FALSE(base::PathExists(dir_foo.AppendASCII("bar.txt")));
+  EXPECT_TRUE(base::DirectoryExists(dir_foo_bar));
+  EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII(".hidden")));
+  EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("baz.txt")));
+  EXPECT_FALSE(base::PathExists(dir_foo_bar.AppendASCII("quux.txt")));
+}
+
 // Tests that a ZIP archive containing SJIS-encoded file names can be correctly
 // extracted if the encoding is specified.
 TEST_F(ZipTest, UnzipSjis) {
diff --git a/tools/android/avd/proto/creation/generic_android24.textpb b/tools/android/avd/proto/creation/generic_android24.textpb
new file mode 100644
index 0000000..9e84d794
--- /dev/null
+++ b/tools/android/avd/proto/creation/generic_android24.textpb
@@ -0,0 +1,39 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Configuration for a generic x86 android-24 AVD.
+
+emulator_package {
+  package_name: "chromium/third_party/android_sdk/public/emulator"
+  version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC"  # 31.2.8
+  dest_path: "generic_android24"
+}
+
+system_image_package {
+  package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis/x86"
+  version: "GWn2zCnRX5fGPkQMCRucE4tNEjCJeS__h0WFMm9_QPgC"  # 27
+  dest_path: "generic_android24"
+}
+system_image_name: "system-images;android-24;google_apis;x86"
+
+avd_package {
+  package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis/x86"
+  dest_path: "generic_android24"
+}
+avd_name: "android_24_google_apis_x86"
+
+avd_settings {
+  screen {
+    density: 480
+    height: 1920
+    width: 1080
+  }
+  advanced_features {
+    key: "GLESDynamicVersion"
+    value: "on"
+  }
+  # Tests can run into low memory issue with the default ram size 1024MB
+  # Incease to 2048MB, which is the same as that on Nexus 5X
+  ram_size: 2048
+}
diff --git a/tools/android/avd/proto/creation/generic_playstore_android24.textpb b/tools/android/avd/proto/creation/generic_playstore_android24.textpb
new file mode 100644
index 0000000..b47d525
--- /dev/null
+++ b/tools/android/avd/proto/creation/generic_playstore_android24.textpb
@@ -0,0 +1,39 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Configuration for a generic x86 android-24 AVD with the Playstore.
+
+emulator_package {
+  package_name: "chromium/third_party/android_sdk/public/emulator"
+  version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC"  # 31.2.8
+  dest_path: "generic_playstore_android24"
+}
+
+system_image_package {
+  package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis_playstore/x86"
+  version: "Ojw5nKhSYXGt87HR42bLyeDhuHUByNUnuItEn7t6n3IC"  # 19
+  dest_path: "generic_playstore_android24"
+}
+system_image_name: "system-images;android-24;google_apis_playstore;x86"
+
+avd_package {
+  package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis_playstore/x86"
+  dest_path: "generic_playstore_android24"
+}
+avd_name: "android_24_google_apis_playstore_x86"
+
+avd_settings {
+  screen {
+    density: 480
+    height: 1920
+    width: 1080
+  }
+  advanced_features {
+    key: "GLESDynamicVersion"
+    value: "on"
+  }
+  # Tests can run into low memory issue with the default ram size 1024MB
+  # Incease to 2048MB, which is the same as that on Nexus 5X
+  ram_size: 2048
+}
diff --git a/tools/android/avd/proto/generic_android24.textpb b/tools/android/avd/proto/generic_android24.textpb
new file mode 100644
index 0000000..62d5283
--- /dev/null
+++ b/tools/android/avd/proto/generic_android24.textpb
@@ -0,0 +1,26 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Configuration for a generic x86 android-24 AVD (userdebug build).
+
+emulator_package {
+  package_name: "chromium/third_party/android_sdk/public/emulator"
+  version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC"  # 31.2.8
+  dest_path: "generic_android24"
+}
+
+system_image_package {
+  package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis/x86"
+  version: "GWn2zCnRX5fGPkQMCRucE4tNEjCJeS__h0WFMm9_QPgC"  # 27
+  dest_path: "generic_android24"
+}
+system_image_name: "system-images;android-24;google_apis;x86"
+
+avd_package {
+  package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis/x86"
+  # Created in https://ci.chromium.org/b/8819381886161538497
+  version: "64fH2yjR-kDMol3lCazqC1funlcJZnmbptglSdfjTlEC"
+  dest_path: "generic_android24"
+}
+avd_name: "android_24_google_apis_x86"
diff --git a/tools/android/avd/proto/generic_playstore_android24.textpb b/tools/android/avd/proto/generic_playstore_android24.textpb
new file mode 100644
index 0000000..bc5c2b7
--- /dev/null
+++ b/tools/android/avd/proto/generic_playstore_android24.textpb
@@ -0,0 +1,26 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Configuration for a generic x86 android-24 AVD with playstore (user build)
+
+emulator_package {
+  package_name: "chromium/third_party/android_sdk/public/emulator"
+  version: "KUbHEU8j3yFnPWB_61mplm5-Mpm1bJ-cRDeDzTHK3hkC"  # 31.2.8
+  dest_path: "generic_playstore_android24"
+}
+
+system_image_package {
+  package_name: "chromium/third_party/android_sdk/public/system-images/android-24/google_apis_playstore/x86"
+  version: "Ojw5nKhSYXGt87HR42bLyeDhuHUByNUnuItEn7t6n3IC"  # 19
+  dest_path: "generic_playstore_android24"
+}
+system_image_name: "system-images;android-24;google_apis_playstore;x86"
+
+avd_package {
+  package_name: "chromium/third_party/android_sdk/public/avds/android-24/google_apis_playstore/x86"
+  # Created in https://ci.chromium.org/b/8819381886161538497
+  version: "3pqakRkidFSzF6rs0z2BOg19ojnQsqYdJssqyNT-4dcC"
+  dest_path: "generic_playstore_android24"
+}
+avd_name: "android_24_google_apis_playstore_x86"
diff --git a/tools/auto-nav.py b/tools/auto-nav.py
index 17b61b68..698b718 100644
--- a/tools/auto-nav.py
+++ b/tools/auto-nav.py
@@ -52,7 +52,8 @@
   import psutil
   from selenium import webdriver
 except ImportError:
-  print('Error importing required modules. Run with vpython3 instead of python.')
+  print('Error importing required modules. Run with vpython3 instead of '
+        'python.')
   sys.exit(1)
 
 DEFAULT_INTERVAL = 1
@@ -74,8 +75,8 @@
 # line.
 def ParseArgs():
   # Customize usage and help to include options to be passed to chrome.exe.
-  usage_text = '''%(prog)s [-h] [--interval INTERVAL] [--wait]
-                   [--idlewakeups_dir IDLEWAKEUPS_DIR]
+  usage_text = '''%(prog)s [-h] [--interval INTERVAL] [--start_prompt]
+                   [--exit_prompt] [--idlewakeups_dir IDLEWAKEUPS_DIR]
                    chrome_dir num_navigations url [url ...]
                    [-- --chrome_option ...]'''
   additional_help_text = '''optional arguments to chrome.exe, example:
diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py
index 3727afd..96e7750 100755
--- a/tools/bisect-builds.py
+++ b/tools/bisect-builds.py
@@ -1116,6 +1116,10 @@
     revision_details = json.loads(response.read())
     revision_text = revision_details['chromium_base_position']
 
+    if not revision_text:
+        raise Exception(
+            "No 'chromium_base_position' matching %s found." % chromium_base_position)
+
   # Translate from text commit position to integer commit position.
   return int(revision_text)
 
diff --git a/tools/grit/grit/node/base_unittest.py b/tools/grit/grit/node/base_unittest.py
index 285ba4c..6cf9b11 100755
--- a/tools/grit/grit/node/base_unittest.py
+++ b/tools/grit/grit/node/base_unittest.py
@@ -210,6 +210,14 @@
     AssertExpr(False, "'foo' in defs", {'baz': 'bar'}, 'ios', {})
     AssertExpr(False, "'foo' in defs", {}, 'ios', {})
     AssertExpr(True, "is_linux", {}, 'linux2', {})
+    AssertExpr(True, "is_linux", {'is_chromeos': True}, 'linux2', {})
+    AssertExpr(True, "is_linux", {'chromeos': True}, 'linux2', {})
+    AssertExpr(True, "is_linux", {'lacros': True}, 'linux2', {})
+    # TODO(crbug.com/1307455): These two should be False once fixed.
+    AssertExpr(True, "is_linux", {'chromeos_ash': True}, 'linux2', {})
+    AssertExpr(True, "is_linux", {'chromeos_lacros': True}, 'linux2', {})
+    AssertExpr(False, "is_chromeos", {}, 'linux2', {})
+    AssertExpr(False, "is_fuchsia", {}, 'linux2', {})
     AssertExpr(False, "is_linux", {}, 'win32', {})
     AssertExpr(True, "is_macosx", {}, 'darwin', {})
     AssertExpr(False, "is_macosx", {}, 'ios', {})
@@ -220,11 +228,15 @@
     AssertExpr(True, "is_ios", {}, 'ios', {})
     AssertExpr(False, "is_ios", {}, 'darwin', {})
     AssertExpr(True, "is_posix", {}, 'linux2', {})
+    AssertExpr(True, "is_posix", {'chromeos_ash': True}, 'linux2', {})
+    AssertExpr(True, "is_posix", {'chromeos_lacros': True}, 'linux2', {})
     AssertExpr(True, "is_posix", {}, 'darwin', {})
     AssertExpr(True, "is_posix", {}, 'android', {})
     AssertExpr(True, "is_posix", {}, 'ios', {})
     AssertExpr(True, "is_posix", {}, 'freebsd7', {})
+    AssertExpr(False, "is_posix", {}, 'fuchsia', {})
     AssertExpr(False, "is_posix", {}, 'win32', {})
+    AssertExpr(True, "is_fuchsia", {}, 'fuchsia', {})
     AssertExpr(True, "pp_ifdef('foo')", {'foo': True}, 'win32', {})
     AssertExpr(True, "pp_ifdef('foo')", {'foo': False}, 'win32', {})
     AssertExpr(False, "pp_ifdef('foo')", {'bar': True}, 'win32', {})
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 303fc78..9124835 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -296,12 +296,6 @@
         'goma': 'chromeos_amd64-generic_use_fake_dbus_clients_goma',
         'reclient': 'chromeos_amd64-generic_use_fake_dbus_clients_reclient',
       },
-      'GPU FYI Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient',
-      'GPU FYI Win x64 Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_debug_trybot_reclient',
-      'GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)': 'gpu_fyi_tests_dx12vk_release_trybot_reclient',
-      'GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_dx12vk_debug_trybot_reclient',
-      'GPU FYI XR Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient',
-      'GPU Win x64 Builder (dbg) (reclient shadow)': 'gpu_tests_debug_bot_reclient',
       'Libfuzzer Upload Chrome OS ASan': 'libfuzzer_chromeos_asan_release_bot',
       'Libfuzzer Upload Linux ASan': 'libfuzzer_asan_release_bot',
       'Libfuzzer Upload Linux ASan (reclient)': 'libfuzzer_asan_release_bot_reclient',
@@ -510,10 +504,16 @@
       'GPU FYI Mac arm64 Builder': 'gpu_fyi_tests_release_trybot_arm64',
       'GPU FYI Win Builder': 'gpu_fyi_tests_release_trybot_x86',
       'GPU FYI Win x64 Builder': 'gpu_fyi_tests_release_trybot',
+      'GPU FYI Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient',
       'GPU FYI Win x64 Builder (dbg)': 'gpu_fyi_tests_debug_trybot',
+      'GPU FYI Win x64 Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_debug_trybot_reclient',
       'GPU FYI Win x64 DX12 Vulkan Builder': 'gpu_fyi_tests_dx12vk_release_trybot',
+      'GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)': 'gpu_fyi_tests_dx12vk_release_trybot_reclient',
       'GPU FYI Win x64 DX12 Vulkan Builder (dbg)': 'gpu_fyi_tests_dx12vk_debug_trybot',
+      'GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)': 'gpu_fyi_tests_dx12vk_debug_trybot_reclient',
       'GPU FYI XR Win x64 Builder': 'gpu_fyi_tests_release_trybot',
+      'GPU FYI XR Win x64 Builder (reclient shadow)': 'gpu_fyi_tests_release_trybot_reclient',
+      'GPU Win x64 Builder (dbg) (reclient shadow)': 'gpu_tests_debug_bot_reclient',
       'Linux FYI GPU TSAN Release': 'gpu_fyi_tests_release_trybot_tsan_reclient',
       'Optional Android Release (Nexus 5X)': 'gpu_tests_android_release_trybot_arm64',
       'Optional Android Release (Pixel 4)': 'gpu_tests_android_release_trybot',
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index c5f8ed2..9d2316d8 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -155,95 +155,6 @@
       }
     }
   },
-  "GPU FYI Win x64 Builder (dbg) (reclient shadow)": {
-    "gn_args": {
-      "enable_nacl": false,
-      "ffmpeg_branding": "Chrome",
-      "internal_gles2_conform_tests": true,
-      "is_component_build": true,
-      "is_debug": true,
-      "is_gpu_fyi_bot": true,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "use_rbe": true,
-      "use_remoteexec": true
-    }
-  },
-  "GPU FYI Win x64 Builder (reclient shadow)": {
-    "gn_args": {
-      "blink_enable_generated_code_formatting": false,
-      "dcheck_always_on": true,
-      "enable_nacl": false,
-      "ffmpeg_branding": "Chrome",
-      "internal_gles2_conform_tests": true,
-      "is_component_build": false,
-      "is_debug": false,
-      "is_gpu_fyi_bot": true,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "use_rbe": true,
-      "use_remoteexec": true
-    }
-  },
-  "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)": {
-    "gn_args": {
-      "enable_nacl": false,
-      "enable_vulkan": true,
-      "ffmpeg_branding": "Chrome",
-      "internal_gles2_conform_tests": true,
-      "is_component_build": true,
-      "is_debug": true,
-      "is_gpu_fyi_bot": true,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "use_rbe": true,
-      "use_remoteexec": true
-    }
-  },
-  "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)": {
-    "gn_args": {
-      "blink_enable_generated_code_formatting": false,
-      "dcheck_always_on": true,
-      "enable_nacl": false,
-      "enable_vulkan": true,
-      "ffmpeg_branding": "Chrome",
-      "internal_gles2_conform_tests": true,
-      "is_component_build": false,
-      "is_debug": false,
-      "is_gpu_fyi_bot": true,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "use_rbe": true,
-      "use_remoteexec": true
-    }
-  },
-  "GPU FYI XR Win x64 Builder (reclient shadow)": {
-    "gn_args": {
-      "blink_enable_generated_code_formatting": false,
-      "dcheck_always_on": true,
-      "enable_nacl": false,
-      "ffmpeg_branding": "Chrome",
-      "internal_gles2_conform_tests": true,
-      "is_component_build": false,
-      "is_debug": false,
-      "is_gpu_fyi_bot": true,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "use_rbe": true,
-      "use_remoteexec": true
-    }
-  },
-  "GPU Win x64 Builder (dbg) (reclient shadow)": {
-    "gn_args": {
-      "ffmpeg_branding": "Chrome",
-      "is_component_build": true,
-      "is_debug": true,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "use_rbe": true,
-      "use_remoteexec": true
-    }
-  },
   "Libfuzzer Upload Chrome OS ASan": {
     "gn_args": {
       "archive_seed_corpus": false,
diff --git a/tools/mb/mb_config_expectations/chromium.gpu.fyi.json b/tools/mb/mb_config_expectations/chromium.gpu.fyi.json
index bce886a..6467fa81 100644
--- a/tools/mb/mb_config_expectations/chromium.gpu.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.gpu.fyi.json
@@ -225,6 +225,36 @@
       "use_goma": true
     }
   },
+  "GPU FYI Win x64 Builder (dbg) (reclient shadow)": {
+    "gn_args": {
+      "enable_nacl": false,
+      "ffmpeg_branding": "Chrome",
+      "internal_gles2_conform_tests": true,
+      "is_component_build": true,
+      "is_debug": true,
+      "is_gpu_fyi_bot": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "use_rbe": true,
+      "use_remoteexec": true
+    }
+  },
+  "GPU FYI Win x64 Builder (reclient shadow)": {
+    "gn_args": {
+      "blink_enable_generated_code_formatting": false,
+      "dcheck_always_on": true,
+      "enable_nacl": false,
+      "ffmpeg_branding": "Chrome",
+      "internal_gles2_conform_tests": true,
+      "is_component_build": false,
+      "is_debug": false,
+      "is_gpu_fyi_bot": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "use_rbe": true,
+      "use_remoteexec": true
+    }
+  },
   "GPU FYI Win x64 DX12 Vulkan Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
@@ -255,6 +285,38 @@
       "use_goma": true
     }
   },
+  "GPU FYI Win x64 DX12 Vulkan Builder (dbg) (reclient shadow)": {
+    "gn_args": {
+      "enable_nacl": false,
+      "enable_vulkan": true,
+      "ffmpeg_branding": "Chrome",
+      "internal_gles2_conform_tests": true,
+      "is_component_build": true,
+      "is_debug": true,
+      "is_gpu_fyi_bot": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "use_rbe": true,
+      "use_remoteexec": true
+    }
+  },
+  "GPU FYI Win x64 DX12 Vulkan Builder (reclient shadow)": {
+    "gn_args": {
+      "blink_enable_generated_code_formatting": false,
+      "dcheck_always_on": true,
+      "enable_nacl": false,
+      "enable_vulkan": true,
+      "ffmpeg_branding": "Chrome",
+      "internal_gles2_conform_tests": true,
+      "is_component_build": false,
+      "is_debug": false,
+      "is_gpu_fyi_bot": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "use_rbe": true,
+      "use_remoteexec": true
+    }
+  },
   "GPU FYI XR Win x64 Builder": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
@@ -270,6 +332,33 @@
       "use_goma": true
     }
   },
+  "GPU FYI XR Win x64 Builder (reclient shadow)": {
+    "gn_args": {
+      "blink_enable_generated_code_formatting": false,
+      "dcheck_always_on": true,
+      "enable_nacl": false,
+      "ffmpeg_branding": "Chrome",
+      "internal_gles2_conform_tests": true,
+      "is_component_build": false,
+      "is_debug": false,
+      "is_gpu_fyi_bot": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "use_rbe": true,
+      "use_remoteexec": true
+    }
+  },
+  "GPU Win x64 Builder (dbg) (reclient shadow)": {
+    "gn_args": {
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": true,
+      "is_debug": true,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "use_rbe": true,
+      "use_remoteexec": true
+    }
+  },
   "Linux FYI GPU TSAN Release": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
diff --git a/tools/memory/partition_allocator/BUILD.gn b/tools/memory/partition_allocator/BUILD.gn
index 6cbe9e02..fae79c6 100644
--- a/tools/memory/partition_allocator/BUILD.gn
+++ b/tools/memory/partition_allocator/BUILD.gn
@@ -26,6 +26,13 @@
       "//base",
     ]
   }
+  executable("pa_dump_heap") {
+    sources = [ "pa_dump_heap.cc" ]
+    deps = [
+      ":pa_tool_utils",
+      "//base",
+    ]
+  }
 }
 
 group("all") {
diff --git a/tools/memory/partition_allocator/inspect_utils.cc b/tools/memory/partition_allocator/inspect_utils.cc
index 013ed78c..5f6a53d 100644
--- a/tools/memory/partition_allocator/inspect_utils.cc
+++ b/tools/memory/partition_allocator/inspect_utils.cc
@@ -26,7 +26,6 @@
       static_cast<ssize_t>(size)) {
     return true;
   }
-
   return false;
 }
 
diff --git a/tools/memory/partition_allocator/inspect_utils.h b/tools/memory/partition_allocator/inspect_utils.h
index ca5e13cc..a909f7e 100644
--- a/tools/memory/partition_allocator/inspect_utils.h
+++ b/tools/memory/partition_allocator/inspect_utils.h
@@ -17,6 +17,7 @@
 
 #include "base/files/file.h"
 #include "base/posix/eintr_wrapper.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace partition_alloc::internal::tools {
 
@@ -36,6 +37,31 @@
 
 uintptr_t IndexThreadCacheNeedleArray(pid_t pid, int mem_fd, size_t index);
 
+// Allows to access an object copied from remote memory "as if" it were
+// local. Of course, dereferencing any pointer from within it will at best
+// fault.
+template <typename T>
+class RawBuffer {
+ public:
+  RawBuffer() = default;
+  const T* get() const { return reinterpret_cast<const T*>(buffer_); }
+  char* get_buffer() { return buffer_; }
+
+  static absl::optional<RawBuffer<T>> ReadFromMemFd(int mem_fd,
+                                                    uintptr_t address) {
+    RawBuffer<T> buf;
+    bool ok = ReadMemory(mem_fd, reinterpret_cast<unsigned long>(address),
+                         sizeof(T), buf.get_buffer());
+    if (!ok)
+      return absl::nullopt;
+
+    return {buf};
+  }
+
+ private:
+  alignas(T) char buffer_[sizeof(T)];
+};
+
 }  // namespace partition_alloc::internal::tools
 
 #endif  // TOOLS_MEMORY_PARTITION_ALLOCATOR_INSPECT_UTILS_H_
diff --git a/tools/memory/partition_allocator/pa_dump_heap.cc b/tools/memory/partition_allocator/pa_dump_heap.cc
new file mode 100644
index 0000000..42fa8bcf
--- /dev/null
+++ b/tools/memory/partition_allocator/pa_dump_heap.cc
@@ -0,0 +1,198 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Dumps PartitionAlloc's heap into a file.
+
+#include <cstdlib>
+#include <string>
+
+#include "base/allocator/partition_allocator/partition_root.h"
+#include "base/allocator/partition_allocator/thread_cache.h"
+#include "base/check.h"
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/thread_annotations.h"
+#include "base/values.h"
+#include "tools/memory/partition_allocator/inspect_utils.h"
+
+namespace partition_alloc::internal::tools {
+
+class HeapDumper {
+ public:
+  HeapDumper(pid_t pid, int mem_fd) : pid_(pid), mem_fd_(mem_fd) {}
+
+  void FindRoot() {
+    root_address_ = FindRootAddress(pid_, mem_fd_);
+    CHECK(root_address_);
+    auto root = RawBuffer<PartitionRoot<ThreadSafe>>::ReadFromMemFd(
+        mem_fd_, root_address_);
+    CHECK(root);
+    root_ = *root;
+  }
+
+  void DumpSuperPages() {
+    std::vector<uintptr_t> super_pages;
+    // There is no list of super page, only a list of extents. Walk the extent
+    // list to get all superpages.
+    uintptr_t extent_address =
+        reinterpret_cast<uintptr_t>(root_.get()->first_extent);
+    while (extent_address) {
+      auto extent =
+          RawBuffer<PartitionSuperPageExtentEntry<ThreadSafe>>::ReadFromMemFd(
+              mem_fd_, extent_address);
+      uintptr_t first_super_page_address = SuperPagesBeginFromExtent(
+          reinterpret_cast<PartitionSuperPageExtentEntry<ThreadSafe>*>(
+              extent_address));
+      for (uintptr_t super_page = first_super_page_address;
+           super_page < first_super_page_address +
+                            extent->get()->number_of_consecutive_super_pages *
+                                kSuperPageSize;
+           super_page += kSuperPageSize) {
+        super_pages.push_back(super_page);
+      }
+      extent_address = reinterpret_cast<uintptr_t>(extent->get()->next);
+    }
+
+    LOG(WARNING) << "Found " << super_pages.size() << std::hex
+                 << " super pages.";
+    for (uintptr_t super_page : super_pages) {
+      auto super_page_data =
+          std::make_unique<std::array<char, kSuperPageSize>>();
+      bool ok = ReadMemory(mem_fd_, super_page, kSuperPageSize,
+                           super_page_data->data());
+      if (!ok) {
+        LOG(WARNING) << base::StringPrintf("Cannot read from super page 0x%lx",
+                                           super_page);
+        continue;
+      }
+      super_pages_.emplace(super_page, std::move(super_page_data));
+    }
+    LOG(WARNING) << "Read all super pages";
+  }
+
+  base::Value Dump() const {
+    auto partition_page_to_value =
+        [](uintptr_t offset,
+           const std::array<char, kSuperPageSize>& data) -> base::Value {
+      auto ret = base::Value(base::Value::Type::DICTIONARY);
+      if (offset == 0) {
+        ret.SetKey("type", base::Value{"metadata"});
+      } else if (offset == kSuperPageSize - PartitionPageSize()) {
+        ret.SetKey("type", base::Value{"guard"});
+      } else {
+        ret.SetKey("type", base::Value{"payload"});
+      }
+
+      bool all_zeros = true;
+      for (size_t i = 0; i < PartitionPageSize(); i++) {
+        if (data[offset + i]) {
+          all_zeros = false;
+          break;
+        }
+      }
+      ret.SetKey("all_zeros", base::Value{all_zeros});
+      return ret;
+    };
+    auto super_page_to_value =
+        [&](uintptr_t address,
+            const std::array<char, kSuperPageSize>& data) -> base::Value {
+      auto ret = base::Value(base::Value::Type::DICTIONARY);
+      ret.SetKey("address", base::Value{base::StringPrintf("0x%lx", address)});
+
+      auto partition_pages = base::Value(base::Value::Type::LIST);
+      for (uintptr_t offset = 0; offset < kSuperPageSize;
+           offset += PartitionPageSize()) {
+        partition_pages.Append(partition_page_to_value(offset, data));
+      }
+      ret.SetKey("partition_pages", std::move(partition_pages));
+
+      return ret;
+    };
+
+    auto super_pages_value = base::Value(base::Value::Type::LIST);
+    for (const auto& address_data : super_pages_) {
+      super_pages_value.Append(
+          super_page_to_value(address_data.first, *address_data.second));
+    }
+
+    return super_pages_value;
+  }
+
+ private:
+  static uintptr_t FindRootAddress(pid_t pid,
+                                   int mem_fd) NO_THREAD_SAFETY_ANALYSIS {
+    uintptr_t tcache_registry_address =
+        IndexThreadCacheNeedleArray(pid, mem_fd, 1);
+    auto registry =
+        RawBuffer<base::internal::ThreadCacheRegistry>::ReadFromMemFd(
+            mem_fd, tcache_registry_address);
+    if (!registry)
+      return 0;
+
+    auto tcache_address =
+        reinterpret_cast<uintptr_t>(registry->get()->list_head_);
+    if (!tcache_address)
+      return 0;
+
+    auto tcache = RawBuffer<base::internal::ThreadCache>::ReadFromMemFd(
+        mem_fd, tcache_address);
+    if (!tcache)
+      return 0;
+
+    auto root_address = reinterpret_cast<uintptr_t>(tcache->get()->root_);
+    return root_address;
+  }
+
+  const pid_t pid_;
+  const int mem_fd_;
+  uintptr_t root_address_;
+  RawBuffer<PartitionRoot<ThreadSafe>> root_;
+  std::map<uintptr_t, std::unique_ptr<std::array<char, kSuperPageSize>>>
+      super_pages_;
+};
+
+}  // namespace partition_alloc::internal::tools
+
+int main(int argc, char** argv) {
+  base::CommandLine::Init(argc, argv);
+
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch("pid") || !command_line->HasSwitch("json")) {
+    LOG(ERROR) << "Usage:" << argv[0] << " --pid=<PID> --json=<FILENAME>";
+    return 1;
+  }
+
+  int pid = atoi(command_line->GetSwitchValueASCII("pid").c_str());
+  LOG(WARNING) << "PID = " << pid;
+
+  auto mem_fd = partition_alloc::internal::tools::OpenProcMem(pid);
+  partition_alloc::internal::tools::HeapDumper dumper{pid, mem_fd.get()};
+
+  {
+    partition_alloc::internal::tools::ScopedSigStopper stopper{pid};
+    dumper.FindRoot();
+    dumper.DumpSuperPages();
+  }
+
+  auto dump = dumper.Dump();
+  std::string json_string;
+  bool ok = base::JSONWriter::WriteWithOptions(
+      dump, base::JSONWriter::Options::OPTIONS_PRETTY_PRINT, &json_string);
+
+  if (ok) {
+    base::FilePath json_filename = command_line->GetSwitchValuePath("json");
+    auto f = base::File(json_filename, base::File::Flags::FLAG_OPEN_ALWAYS |
+                                           base::File::Flags::FLAG_WRITE);
+    if (f.IsValid()) {
+      f.WriteAtCurrentPos(json_string.c_str(), json_string.size());
+      LOG(WARNING) << "\n\nDumped JSON to " << json_filename;
+      return 0;
+    }
+  }
+
+  return 1;
+}
diff --git a/tools/memory/partition_allocator/pa_tcache_inspect.cc b/tools/memory/partition_allocator/pa_tcache_inspect.cc
index ad8914d..961407d 100644
--- a/tools/memory/partition_allocator/pa_tcache_inspect.cc
+++ b/tools/memory/partition_allocator/pa_tcache_inspect.cc
@@ -50,31 +50,6 @@
   return IndexThreadCacheNeedleArray(pid, mem_fd, 1);
 }
 
-// Allows to access an object copied from remote memory "as if" it were
-// local. Of course, dereferencing any pointer from within it will at best
-// fault.
-template <typename T>
-class RawBuffer {
- public:
-  RawBuffer() = default;
-  const T* get() const { return reinterpret_cast<const T*>(buffer_); }
-  char* get_buffer() { return buffer_; }
-
-  static absl::optional<RawBuffer<T>> ReadFromMemFd(int mem_fd,
-                                                    uintptr_t address) {
-    RawBuffer<T> buf;
-    bool ok = ReadMemory(mem_fd, reinterpret_cast<unsigned long>(address),
-                         sizeof(T), buf.get_buffer());
-    if (!ok)
-      return absl::nullopt;
-
-    return {buf};
-  }
-
- private:
-  alignas(T) char buffer_[sizeof(T)];
-};
-
 // List all thread names for a given PID.
 std::map<base::PlatformThreadId, std::string> ThreadNames(pid_t pid) {
   std::map<base::PlatformThreadId, std::string> result;
diff --git a/tools/memory/partition_allocator/plot_superpages.py b/tools/memory/partition_allocator/plot_superpages.py
new file mode 100755
index 0000000..f1c40d5b
--- /dev/null
+++ b/tools/memory/partition_allocator/plot_superpages.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Maps the superpage occupancy.
+
+To run this:
+
+1. Build pa_dump_heap at a close-enough revision to the Chrome instance you're
+   interested in.
+2. Run pa_dump_heap --pid=PID_OF_RUNNING_CHROME_PROCESS --json=output.json
+3. Run plot_superpages.py --json=output.json --output=output.png
+"""
+
+import argparse
+import json
+
+import matplotlib
+from matplotlib import pylab as plt
+import numpy as np
+
+
+def ParseJson(filename):
+  with open(filename, 'r') as f:
+    data = json.load(f)
+    return data
+
+
+def PlotData(data, output_filename):
+  num_superpages = len(data)
+  num_partition_pages = len(data[0]['partition_pages'])
+  data_np = np.zeros((num_superpages, num_partition_pages, 3))
+
+  address_to_superpage = {superpage['address']: superpage for superpage in data}
+  addresses = sorted(address_to_superpage.keys())
+
+  for superpage_index in range(len(addresses)):
+    superpage = address_to_superpage[addresses[superpage_index]]
+    for index, partition_page in enumerate(superpage['partition_pages']):
+      value = 0
+      if partition_page['type'] == 'metadata':
+        value = (0., 0., 0.)
+      elif partition_page['type'] == 'guard':
+        value = (.5, .5, .5)
+      elif partition_page['all_zeros']:
+        value = (1., 0., 0.)
+      else:
+        value = (0., 1., 0.)
+      data_np[superpage_index, index, :] = value
+
+  plt.figure(figsize=(20, 14))
+  plt.imshow(data_np)
+  plt.title('Super page map')
+  plt.yticks(ticks=range(len(addresses)), labels=addresses)
+  plt.xlabel('PartitionPage index')
+
+  handles = [
+      matplotlib.patches.Patch(facecolor=(0., 0., 0.), label='Metadata'),
+      matplotlib.patches.Patch(facecolor=(.5, .5, .5), label='Guard'),
+      matplotlib.patches.Patch(facecolor=(1., 0., 0.), label='Free'),
+      matplotlib.patches.Patch(facecolor=(0., 1., 0.), label='Allocated'),
+  ]
+  plt.legend(handles=handles, loc='lower right', fontsize=16)
+
+  plt.savefig(output_filename, bbox_inches='tight')
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--json',
+                      help='JSON dump from pa_tcache_inspect',
+                      required=True)
+  parser.add_argument('--output', help='Output file', required=True)
+
+  args = parser.parse_args()
+  data = ParseJson(args.json)
+  PlotData(data, args.output)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index cbb64fe9..503e4bf 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7621,6 +7621,7 @@
   <int value="2" label="User closed bubble via corner X or ESC"/>
   <int value="3" label="User did not interact with the bubble"/>
   <int value="4" label="Bubble lost focus and was deactivated"/>
+  <int value="5" label="User cancelled the bubble"/>
 </enum>
 
 <enum name="AutofillVirtualCardManualFallbackBubbleFieldClicked">
@@ -11454,6 +11455,15 @@
   <int value="3" label="Multiple pages, all of which are non-visible"/>
 </enum>
 
+<enum name="BrowsingTopicsCalculatorResultStatus">
+  <int value="0" label="Success"/>
+  <int value="1" label="Failure: permission denied"/>
+  <int value="2" label="Failure: api usage context query error"/>
+  <int value="3" label="Failure: annotation execution error"/>
+  <int value="4"
+      label="Failure: taxonomy version not supported in the Chrome binary"/>
+</enum>
+
 <enum name="BubbleDismissalReason">
   <obsolete>
     Deprecated 12/2020 as it is no longer used for analysis.
@@ -16722,6 +16732,12 @@
   <int value="3" label="Failed to assemble report"/>
 </enum>
 
+<enum name="ConversionAttributionSrcRequestStatus">
+  <int value="0" label="Requested"/>
+  <int value="1" label="Received"/>
+  <int value="2" label="Failed"/>
+</enum>
+
 <enum name="ConversionPostSendReportDeleteEvent">
   <obsolete>
     The error rate is not high enough to merit monitoring.
@@ -37150,7 +37166,7 @@
   <int value="3810" label="CanvasUseColorSpace"/>
   <int value="3811" label="SelectMenuElement"/>
   <int value="3812"
-      label="RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial"/>
+      label="OBSOLETE_RTCPeerConnectionSdpSemanticsPlanBWithReverseOriginTrial"/>
   <int value="3813" label="WebAppManifestCaptureLinks"/>
   <int value="3814" label="SanitizerAPICreated"/>
   <int value="3815" label="SanitizerAPIDefaultConfiguration"/>
@@ -37535,6 +37551,7 @@
   <int value="4179" label="V8FunctionPrototypeArguments"/>
   <int value="4180" label="V8FunctionPrototypeCaller"/>
   <int value="4181" label="BluetoothDeviceForget"/>
+  <int value="4182" label="TopicsAPI_BrowsingTopics_Method"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -37651,6 +37668,8 @@
   <int value="94" label="ClientHintUAFull"/>
   <int value="95" label="ClientHintUAWoW64"/>
   <int value="96" label="ClientHintPartitionedCookies"/>
+  <int value="97" label="BrowsingTopics"/>
+  <int value="98" label="BrowsingTopicsBackwardCompatible"/>
 </enum>
 
 <enum name="FeaturePolicyImageCompressionFormat">
@@ -52148,6 +52167,7 @@
   <int value="-1779753607" label="VizDisplayCompositor:disabled"/>
   <int value="-1778993296" label="ContextualSearchMlTapSuppression:disabled"/>
   <int value="-1778202807" label="DelayAsyncScriptExecution:enabled"/>
+  <int value="-1777540083" label="LocationBarModelOptimizations:disabled"/>
   <int value="-1776351704" label="DesktopPWAsOmniboxInstall:disabled"/>
   <int value="-1775842908" label="EnableOAuthIpp:disabled"/>
   <int value="-1774818943" label="VrWebInputEditing:enabled"/>
@@ -52367,6 +52387,7 @@
   <int value="-1617805422" label="WebViewUseMetricsUploadService:enabled"/>
   <int value="-1617183455" label="OfflineRecentPages:disabled"/>
   <int value="-1616855537" label="enable-manual-password-generation:disabled"/>
+  <int value="-1616638720" label="AutofillShadowDOM:enabled"/>
   <int value="-1615901413" label="NewMacNotificationAPI:enabled"/>
   <int value="-1615727417" label="OmniboxSearchReadyIncognito:enabled"/>
   <int value="-1615704396"
@@ -52869,6 +52890,7 @@
   <int value="-1294050129" label="ContentFullscreen:disabled"/>
   <int value="-1293987566" label="OmniboxZeroSuggestionsOnNTPRealbox:disabled"/>
   <int value="-1293566107" label="DocumentTransition:disabled"/>
+  <int value="-1292766410" label="IntentChipSkipsPicker:disabled"/>
   <int value="-1292615467"
       label="OmniboxSuggestionTransparencyOptions:disabled"/>
   <int value="-1291963295" label="ComputePressure:disabled"/>
@@ -52893,6 +52915,7 @@
   <int value="-1283164264" label="CroshSWA:disabled"/>
   <int value="-1282992935"
       label="AutofillLocalCardMigrationShowFeedback:disabled"/>
+  <int value="-1282488703" label="LocationBarModelOptimizations:enabled"/>
   <int value="-1281465357" label="MacV2GPUSandbox:disabled"/>
   <int value="-1280673966" label="PluginVmFullscreen:disabled"/>
   <int value="-1280274244" label="OmniboxClosePopupWithEscape:disabled"/>
@@ -53610,6 +53633,7 @@
   <int value="-790544721"
       label="OverrideUnsupportedPageLanguageForHrefTranslate:disabled"/>
   <int value="-790036192" label="overscroll-start-threshold"/>
+  <int value="-788070719" label="PartitionedCookiesBypassOriginTrial:disabled"/>
   <int value="-787969387" label="HTTPSServerPreviewsUsingURLLoader:enabled"/>
   <int value="-787885873" label="BorealisForceBetaClient:disabled"/>
   <int value="-787876637" label="HomeLauncherGestures:enabled"/>
@@ -54052,6 +54076,7 @@
       label="OmniboxHistoryQuickProviderAllowMidwordContinuations:disabled"/>
   <int value="-482259889" label="MacMDDownloadShelf:disabled"/>
   <int value="-482188490" label="ImpulseScrollAnimations:disabled"/>
+  <int value="-480263549" label="AutofillShadowDOM:disabled"/>
   <int value="-478462945" label="enable-ephemeral-apps"/>
   <int value="-477101783" label="HandwritingGesture:disabled"/>
   <int value="-475049740" label="disable-vr-shell"/>
@@ -54180,6 +54205,7 @@
       label="OmniboxUIExperimentHideSteadyStateUrlPathQueryAndRefOnInteraction:enabled"/>
   <int value="-389283574" label="IPH_PasswordsAccountStorage:disabled"/>
   <int value="-387606010" label="ArcBootCompletedBroadcast:enabled"/>
+  <int value="-387217874" label="LinkCapturingInfoBar:enabled"/>
   <int value="-385461103" label="XsurfaceMetricsReporting:enabled"/>
   <int value="-385337473" label="enable-fast-unload"/>
   <int value="-384589459" label="disable-supervised-user-safesites"/>
@@ -54866,6 +54892,7 @@
   <int value="92327255" label="DisplayMoveWindowAccels:disabled"/>
   <int value="93832899" label="NtpCustomizationMenuV2:enabled"/>
   <int value="97091906" label="AutofillSaveCardShowNoThanks:enabled"/>
+  <int value="97976428" label="CastUseBlocklistForRemotingQuery:enabled"/>
   <int value="98029688" label="MediaAppDisplayExif:disabled"/>
   <int value="98134240" label="material-design-ink-drop-animation-speed"/>
   <int value="98218116" label="ContextualSearchLongpressResolve:disabled"/>
@@ -54904,6 +54931,7 @@
   <int value="121911155" label="DesktopPWAsElidedExtensionsMenu:enabled"/>
   <int value="122959683" label="WebViewJavaJsBridgeMojo:enabled"/>
   <int value="123097915" label="FaviconsFromWebManifest:enabled"/>
+  <int value="124590378" label="LensStandalone:disabled"/>
   <int value="125581289" label="WebRtcHWVP8Encoding:disabled"/>
   <int value="125934378" label="enable-password-link"/>
   <int value="127338155" label="NtpSafeBrowsingModule:disabled"/>
@@ -55051,6 +55079,7 @@
   <int value="239330938" label="LauncherAppSort:enabled"/>
   <int value="240856309" label="MimeHandlerViewInCrossProcessFrame:enabled"/>
   <int value="241187301" label="BrowserTouchBar:disabled"/>
+  <int value="241846183" label="PartitionedCookiesBypassOriginTrial:enabled"/>
   <int value="244697230" label="enable-theme-color-in-tabbed-mode"/>
   <int value="245100553" label="PcieBillboardNotification:enabled"/>
   <int value="245896533" label="SearchSuggestionsOnLocalNtp:enabled"/>
@@ -55168,6 +55197,7 @@
   <int value="317432596" label="DisplayLocking:disabled"/>
   <int value="317889969" label="AutofillAddressProfileSavePrompt:disabled"/>
   <int value="318277328" label="WebViewAppsPackageNamesAllowlist:enabled"/>
+  <int value="318704417" label="LensStandalone:enabled"/>
   <int value="319683583" label="ContentSuggestionsDebugLog:enabled"/>
   <int value="320121752" label="DelegateOverscrollSwipes:disabled"/>
   <int value="321366101" label="WebXRARDOMOverlay:enabled"/>
@@ -56013,6 +56043,7 @@
   <int value="884106779" label="supervised-user-safesites"/>
   <int value="884892326" label="CSSCascadeLayers:enabled"/>
   <int value="885186849" label="finch-seed-expiration-age"/>
+  <int value="885943836" label="IntentChipSkipsPicker:enabled"/>
   <int value="885971656" label="EnablePlayStoreAppSearch:enabled"/>
   <int value="886907524" label="autoplay-policy"/>
   <int value="886990840" label="ImeMojoDecoder:enabled"/>
@@ -56401,6 +56432,7 @@
   <int value="1162622865" label="EarlyCodeCache:disabled"/>
   <int value="1162693272" label="MacCoreLocationImplementation:disabled"/>
   <int value="1163255347" label="ash-enable-touch-view-touch-feedback"/>
+  <int value="1163588337" label="CastForceEnableRemotingQuery:enabled"/>
   <int value="1164377197" label="SwipingFromLeftEdgeToGoBack:enabled"/>
   <int value="1164460660" label="AutofillEnableVirtualCard:enabled"/>
   <int value="1165682330" label="AutofillSaveAndFillVPA:disabled"/>
@@ -56943,6 +56975,7 @@
   <int value="1540487070" label="ContextualSearchDebug:enabled"/>
   <int value="1541723759" label="ServiceWorkerNavigationPreload:disabled"/>
   <int value="1543027970" label="EnableDisplayZoomSetting:disabled"/>
+  <int value="1543349770" label="CastUseBlocklistForRemotingQuery:disabled"/>
   <int value="1546652609" label="SharingHubDesktopOmnibox:disabled"/>
   <int value="1548776701" label="AllBookmarks:disabled"/>
   <int value="1548942246" label="PassiveDocumentEventListeners:disabled"/>
@@ -57182,9 +57215,11 @@
       label="AutofillUseUnassociatedListedElements:enabled"/>
   <int value="1710630380"
       label="AutofillEnableSupportForMoreStructureInNames:enabled"/>
+  <int value="1710663404" label="CastForceEnableRemotingQuery:disabled"/>
   <int value="1711037950"
       label="AutofillVisualImprovementsForSuggestionUi:disabled"/>
   <int value="1711286384" label="ContextMenuCopyImage:disabled"/>
+  <int value="1711998812" label="LinkCapturingInfoBar:disabled"/>
   <int value="1712622545" label="Memories:disabled"/>
   <int value="1712697097" label="QuickAnswersV2:disabled"/>
   <int value="1712880335" label="FiltersInRecents:enabled"/>
@@ -59857,12 +59892,15 @@
   <int value="0" label="Unknown"/>
   <int value="1" label="CdmProxyReceivedInInvalidState"/>
   <int value="2" label="FailedToSetSourceOnMediaEngine"/>
-  <int value="3" label="kFailedToSetCurrentTime"/>
-  <int value="4" label="kFailedToPlay"/>
-  <int value="5" label="kOnPlaybackError"/>
-  <int value="6" label="kOnDCompSurfaceReceivedError"/>
-  <int value="7" label="kOnDCompSurfaceHandleSetError"/>
-  <int value="8" label="kOnConnectionError"/>
+  <int value="3" label="SetCurrentTimeError"/>
+  <int value="4" label="FailedToPlay"/>
+  <int value="5" label="OnPlaybackError"/>
+  <int value="6" label="OnDCompSurfaceReceivedError (deprecated)"/>
+  <int value="7" label="OnDCompSurfaceHandleSetError"/>
+  <int value="8" label="OnConnectionError"/>
+  <int value="9" label="FailedToSetDCompMode"/>
+  <int value="10" label="FailedToGetDCompSurface"/>
+  <int value="11" label="FailedToDuplicateHandle"/>
 </enum>
 
 <enum name="MediaGalleriesUsageType">
@@ -80851,6 +80889,7 @@
   <int value="8" label="Failed to write model result to DB"/>
   <int value="9" label="Invalid selection result found in prefs"/>
   <int value="10" label="Database initialization failed"/>
+  <int value="11" label="At least one segment is not available"/>
 </enum>
 
 <enum name="SelectedNewTabCreationOption">
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 49ba440..13e9bb1 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -93,7 +93,7 @@
 </histogram>
 
 <histogram name="Arc.Accessibility.ActiveTime.{ArcAccessibilityFeatures}"
-    units="ms" expires_after="2022-04-10">
+    units="ms" expires_after="2023-04-10">
   <owner>sahok@chromium.org</owner>
   <owner>arc-framework@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/browsing_topics/histograms.xml b/tools/metrics/histograms/metadata/browsing_topics/histograms.xml
index 1e7dfbd..4b1da00 100644
--- a/tools/metrics/histograms/metadata/browsing_topics/histograms.xml
+++ b/tools/metrics/histograms/metadata/browsing_topics/histograms.xml
@@ -32,8 +32,60 @@
   </summary>
 </histogram>
 
+<histogram name="BrowsingTopics.EpochTopicsCalculation.CalculatorResultStatus"
+    enum="BrowsingTopicsCalculatorResultStatus" expires_after="2023-03-14">
+  <owner>yaoxia@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Records the browsing topics calculation result status (i.e. success, or the
+    failure reason). Recored at the end of each (weekly) topics calculation.
+  </summary>
+</histogram>
+
+<histogram
+    name="BrowsingTopics.EpochTopicsCalculation.EligibleDistinctHistoryHostsCount"
+    units="hosts" expires_after="2023-03-14">
+  <owner>yaoxia@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Records the count of distinct history hosts that are eligible for topics
+    calculation. Recorded during each (weekly) topics calculation after the
+    observation domains are derived. In case of a calculation failure (e.g.
+    permission denied, etc.), this metric won't be recorded.
+  </summary>
+</histogram>
+
+<histogram
+    name="BrowsingTopics.EpochTopicsCalculation.ObservationContextDomainsCountPerTopTopic"
+    units="context domains" expires_after="2023-03-14">
+  <owner>yaoxia@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Records the count of context domains for each of the calculated top topics.
+    This won't exceed the cap number
+    `kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToKeepPerTopic`. Recorded
+    once for each calculated top topics, during each (weekly) topics calculation
+    after the observation domains are derived. In case of a calculation failure
+    (e.g. permission denied; candidate topic was blocked; etc.), this metric
+    won't be recorded.
+  </summary>
+</histogram>
+
+<histogram
+    name="BrowsingTopics.EpochTopicsCalculation.TopTopicsCountBeforePadding"
+    units="topics" expires_after="2023-03-14">
+  <owner>yaoxia@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Records the count of derived top topics before random ones are padded.
+    Recorded during each (weekly) topics calculation after the top topics are
+    derived. In case of a calculation failure (e.g. permission denied, etc.),
+    this metric won't be recorded.
+  </summary>
+</histogram>
+
 <histogram name="BrowsingTopics.SiteDataStorage.InitStatus"
-    enum="BooleanSuccess" expires_after="2023-02-10">
+    enum="BooleanSuccess" expires_after="2023-03-14">
   <owner>yaoxia@chromium.org</owner>
   <owner>jkarlin@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index c22226c..99fead3c 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -772,6 +772,16 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.Dlp.CaptureModeInitWarned" enum="BooleanWarned"
+    expires_after="2022-12-01">
+  <owner>aidazolic@chromium.org</owner>
+  <owner>chromeos-dlp@google.com</owner>
+  <summary>
+    Recorded when the user is warned by Data Leak Prevention before capture mode
+    initialization.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.Dlp.ClipboardReadBlocked" enum="BooleanBlocked"
     expires_after="2022-12-01">
   <owner>poromov@chromium.org</owner>
@@ -1012,6 +1022,40 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.Dlp.ScreenshotWarned" enum="BooleanWarned"
+    expires_after="2022-12-01">
+  <owner>aidazolic@chromium.org</owner>
+  <owner>chromeos-dlp@google.com</owner>
+  <summary>
+    Recorded when there is a &quot;warn&quot; level Data Leak Prevention
+    evaluation for taking a screenshot or a video capture.
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.Dlp.ScreenshotWarnProceeded" enum="Boolean"
+    expires_after="2022-12-01">
+  <owner>aidazolic@chromium.org</owner>
+  <owner>chromeos-dlp@google.com</owner>
+  <summary>
+    Recorded when the user proceeded with (True) or canceled (False) the action
+    after a Data Leak Prevention warning shown at the capture mode
+    initialization or before taking a screenshot or a video capture.
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.Dlp.ScreenshotWarnSilentProceeded" enum="Boolean"
+    expires_after="2022-12-01">
+  <owner>aidazolic@chromium.org</owner>
+  <owner>chromeos-dlp@google.com</owner>
+  <summary>
+    Recorded when there is a &quot;warn&quot; level Data Leak Prevention
+    evaluation for initiating capture mode, or taking a screenshot or a video
+    capture, but it is caused by the content that the user was already warned
+    about and decided to proceed, so no new warning is shown and the screen
+    share is silently allowed.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.Dlp.VideoCaptureInterrupted" enum="Boolean"
     expires_after="2022-12-01">
   <owner>poromov@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index c7e3afa9..2b4deff4 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -220,7 +220,7 @@
 </histogram>
 
 <histogram name="FileBrowser.Downloads.DirectorySizeMiB" units="MiB"
-    expires_after="2022-04-17">
+    expires_after="2023-04-17">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 115e6854..0620137e 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3111,6 +3111,17 @@
   </summary>
 </histogram>
 
+<histogram name="Conversions.AttributionSrcRequestStatus"
+    enum="ConversionAttributionSrcRequestStatus" expires_after="2022-09-12">
+  <owner>linnan@chromium.org</owner>
+  <owner>johnidel@chromium.org</owner>
+  <owner>measurement-api-dev+metrics@google.com</owner>
+  <summary>
+    Records the status of the attributionsrc requests. Recorded when the request
+    started and finished.
+  </summary>
+</histogram>
+
 <histogram name="Conversions.ClearDataTime" units="ms" expires_after="M105">
   <owner>johnidel@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
@@ -3271,6 +3282,28 @@
   </summary>
 </histogram>
 
+<histogram name="Conversions.RegisteredSourcesPerDataHost" units="sources"
+    expires_after="2022-09-12">
+  <owner>linnan@chromium.org</owner>
+  <owner>johnidel@chromium.org</owner>
+  <owner>measurement-api-dev+metrics@google.com</owner>
+  <summary>
+    Measures the number of sources registered per data host. Recorded when the
+    data host is disconnected or when the data host manager is destroyed.
+  </summary>
+</histogram>
+
+<histogram name="Conversions.RegisteredTriggersPerDataHost" units="triggers"
+    expires_after="2022-09-12">
+  <owner>linnan@chromium.org</owner>
+  <owner>johnidel@chromium.org</owner>
+  <owner>measurement-api-dev+metrics@google.com</owner>
+  <summary>
+    Measures the number of triggers registered per data host. Recorded when the
+    data host is disconnected or when the data host manager is destroyed.
+  </summary>
+</histogram>
+
 <histogram name="Conversions.RegisterImpressionAllowed" enum="BooleanAllowed"
     expires_after="M105">
   <owner>apaseltiner@chromium.org</owner>
@@ -6337,7 +6370,7 @@
 </histogram>
 
 <histogram name="Kiosk.LaunchType" enum="KioskLaunchType"
-    expires_after="2022-04-24">
+    expires_after="2023-04-24">
   <owner>xiyuan@chromium.org</owner>
   <owner>aghuie@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/stability/histograms.xml b/tools/metrics/histograms/metadata/stability/histograms.xml
index 9dbdd0cb..1336e7b 100644
--- a/tools/metrics/histograms/metadata/stability/histograms.xml
+++ b/tools/metrics/histograms/metadata/stability/histograms.xml
@@ -279,7 +279,7 @@
 </histogram>
 
 <histogram name="Stability.DebugScenario.Navigation" enum="DebugScenario"
-    expires_after="2022-03-19">
+    expires_after="2022-12-19">
   <owner>nasko@chromium.org</owner>
   <owner>altimin@chromium.org</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index a9a4d21..76e8ab8 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "1f86891669f4e4b3a88046dd1fa78d8e9f259c59",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/7c9c7d6662bdceb620481ce8826d305ffd24f25e/trace_processor_shell.exe"
+            "hash": "15b2482fb98e1aa361460722abf1ecb3ffab894e",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/9b8eaa21f16d10cffefdf758a8b900e014e4788d/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "5d82d23fadbbe147410266f39b9a72fbb5391cf7",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/7c9c7d6662bdceb620481ce8826d305ffd24f25e/trace_processor_shell"
+            "hash": "72ae2950a4333c3450bee06ba63cbe4578da7ddb",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/9b8eaa21f16d10cffefdf758a8b900e014e4788d/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
             "remote_path": "perfetto_binaries/trace_processor_shell/mac_arm64/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell"
         },
         "linux": {
-            "hash": "6c5897970b7fe316880c315f4b40a5a68f6a0ddd",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/6570222e0f7fd09e13b668cdc69d378a75295c29/trace_processor_shell"
+            "hash": "6b0a9130ba5c2cc1c6ff67bf6fa3064297bc7f19",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/9b8eaa21f16d10cffefdf758a8b900e014e4788d/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java b/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java
index 7032246..183d401f 100644
--- a/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java
+++ b/ui/android/java/src/org/chromium/ui/base/DragAndDropDelegateImpl.java
@@ -12,6 +12,7 @@
 import android.net.Uri;
 import android.os.Build.VERSION_CODES;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Pair;
 import android.view.DragEvent;
@@ -162,7 +163,8 @@
                 return ClipData.newUri(
                         ContextUtils.getApplicationContext().getContentResolver(), null, uri);
             case DragTargetType.LINK:
-                // TODO(https://crbug.com/1289393): Handle link dragging.
+                // TODO(https://crbug.com/1298308): Handle image link dragging.
+                return ClipData.newPlainText(null, getTextForLinkData(dropData));
             case DragTargetType.INVALID:
                 return null;
             case DragTargetType.NUM_ENTRIES:
@@ -178,6 +180,8 @@
             return View.DRAG_FLAG_GLOBAL;
         } else if (dropData.hasImage()) {
             return View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ;
+        } else if (dropData.hasLink()) {
+            return View.DRAG_FLAG_GLOBAL;
         } else {
             return 0;
         }
@@ -300,8 +304,9 @@
             return DragTargetType.TEXT;
         } else if (dropDataAndroid.hasImage()) {
             return DragTargetType.IMAGE;
+        } else if (dropDataAndroid.hasLink()) {
+            return DragTargetType.LINK;
         } else {
-            // TODO(https://crbug.com/1289393): Handle link dragging.
             return DragTargetType.INVALID;
         }
     }
@@ -310,6 +315,15 @@
         return resources.getDimensionPixelSize(R.dimen.drag_shadow_min_width);
     }
 
+    /**
+     * Return the text to be dropped when {@link DropDataAndroid} contains a link.
+     */
+    static String getTextForLinkData(DropDataAndroid dropData) {
+        assert dropData.hasLink();
+        if (TextUtils.isEmpty(dropData.text)) return dropData.gurl.getSpec();
+        return dropData.text + "\n" + dropData.gurl.getSpec();
+    }
+
     private void reset() {
         mShadowHeight = 0;
         mShadowWidth = 0;
diff --git a/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java b/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java
index f3c2aa6..9de13a5 100644
--- a/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java
+++ b/ui/android/junit/src/org/chromium/ui/base/DragAndDropDelegateImplUnitTest.java
@@ -126,24 +126,34 @@
     }
 
     /**
-     * Link dragging is not supported yet, adding this test to make sure status are handled
-     * correctly.
-     * TODO(https://crbug.com/1289393): Handle link dragging.
+     * Image link dragging is not supported yet.
+     * TODO(https://crbug.com/1298308): Handle image link dragging.
      */
     @Test
-    public void testStartDragAndDrop_Link() {
+    public void testStartDragAndDrop_TextLink() {
         final View containerView = new View(mContext);
         final Bitmap shadowImage = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
+        final DropDataAndroid dropData = DropDataAndroid.create(
+                "text", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null);
 
-        final DropDataAndroid linkDropData = DropDataAndroid.create(
-                "", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null);
-        mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, linkDropData);
-        Assert.assertEquals(
-                "Drag link is not supported.", 0, mDragAndDropDelegateImpl.getDragShadowWidth());
-        Assert.assertEquals(
-                "Drag link is not supported.", 0, mDragAndDropDelegateImpl.getDragShadowHeight());
-        Assert.assertFalse("Drag Link is not supported.", mDragAndDropDelegateImpl.isDragStarted());
-        assertDragTypeNotRecorded("Drag didn't started.");
+        mDragAndDropDelegateImpl.startDragAndDrop(containerView, shadowImage, dropData);
+
+        Assert.assertTrue("Drag should start.", mDragAndDropDelegateImpl.isDragStarted());
+        Assert.assertEquals("Drag shadow width does not match. Should not resize for text link.",
+                100, mDragAndDropDelegateImpl.getDragShadowWidth());
+        Assert.assertEquals("Drag shadow height does not match. Should not resize for text link.",
+                200, mDragAndDropDelegateImpl.getDragShadowHeight());
+        assertDragTypeNotRecorded("Drag did not end.");
+
+        mDragAndDropDelegateImpl.onDrag(containerView, mockDragEvent(DragEvent.ACTION_DRAG_ENDED));
+
+        Assert.assertFalse("Drag should end.", mDragAndDropDelegateImpl.isDragStarted());
+        Assert.assertEquals("Drag shadow width should be reset.", 0,
+                mDragAndDropDelegateImpl.getDragShadowWidth());
+        Assert.assertEquals("Drag shadow height should be reset.", 0,
+                mDragAndDropDelegateImpl.getDragShadowHeight());
+        assertDragTypeRecorded(DragTargetType.LINK);
+        assertDragOutsideWebContentHistogramsRecorded(/*dropResult=*/false);
     }
 
     @Test
@@ -292,6 +302,26 @@
                 /*expectedWidth=*/7, /*expectedHeight=*/700);
     }
 
+    @Test
+    public void testTextForLinkData_UrlWithNoTitle() {
+        final DropDataAndroid dropData = DropDataAndroid.create(
+                "", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null);
+
+        String text = DragAndDropDelegateImpl.getTextForLinkData(dropData);
+        Assert.assertEquals("Text should match.", JUnitTestGURLs.EXAMPLE_URL, text);
+    }
+
+    @Test
+    public void testTextForLinkData_UrlWithTitle() {
+        String linkTitle = "Link text";
+        final DropDataAndroid dropData = DropDataAndroid.create(
+                linkTitle, JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), null, null);
+
+        String text = DragAndDropDelegateImpl.getTextForLinkData(dropData);
+        Assert.assertEquals(
+                "Text should match.", linkTitle + "\n" + JUnitTestGURLs.EXAMPLE_URL, text);
+    }
+
     private void doTestResizeShadowImage(
             String testcase, int width, int height, int expectedWidth, int expectedHeight) {
         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 85a49141..be753e5e 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -348,6 +348,8 @@
       "pointer/touch_editing_controller.h",
       "text/bytes_formatting.cc",
       "text/bytes_formatting.h",
+      "themed_vector_icon.cc",
+      "themed_vector_icon.h",
       "ui_base_types.cc",
       "ui_base_types.h",
       "user_activity/user_activity_detector.cc",
@@ -458,6 +460,7 @@
     "//ui/base:data_exchange",
     "//ui/base/clipboard:clipboard_types",
     "//ui/base/dragdrop/mojom",
+    "//ui/color",
     "//ui/display",
     "//ui/display/util:gpu_info_util",
     "//ui/events",
@@ -932,7 +935,10 @@
     ]
   }
   if (!is_ios) {
-    sources += [ "user_activity/user_activity_detector_unittest.cc" ]
+    sources += [
+      "themed_vector_icon_unittest.cc",
+      "user_activity/user_activity_detector_unittest.cc",
+    ]
 
     if (is_mac) {
       sources += [
@@ -950,6 +956,7 @@
     "//base",
     "//base/test:test_support",
     "//build:chromeos_buildflags",
+    "//components/vector_icons",
     "//mojo/core/embedder",
     "//net",
     "//net:test_support",
diff --git a/ui/base/DEPS b/ui/base/DEPS
index 03a6e31..d4d19c59 100644
--- a/ui/base/DEPS
+++ b/ui/base/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/vector_icons",
   "+media/media_buildflags.h",
   "+net",
   "+skia/ext",
diff --git a/ui/base/models/image_model.cc b/ui/base/models/image_model.cc
index 8d7c2b1..27cc58b 100644
--- a/ui/base/models/image_model.cc
+++ b/ui/base/models/image_model.cc
@@ -10,6 +10,10 @@
 #include "ui/color/color_id.h"
 #include "ui/gfx/vector_icon_utils.h"
 
+#if !BUILDFLAG(IS_IOS)
+#include "ui/base/themed_vector_icon.h"
+#endif
+
 namespace ui {
 
 VectorIconModel::VectorIconModel() = default;
@@ -160,6 +164,24 @@
   return !(*this == other);
 }
 
+#if !BUILDFLAG(IS_IOS)
+gfx::ImageSkia ImageModel::Rasterize(
+    const ui::ColorProvider* color_provider) const {
+  if (IsImage())
+    return GetImage().AsImageSkia();
+
+  if (IsVectorIcon()) {
+    DCHECK(color_provider);
+    return ThemedVectorIcon(GetVectorIcon()).GetImageSkia(color_provider);
+  }
+
+  if (IsImageGenerator())
+    return GetImageGenerator().Run(color_provider);
+
+  return gfx::ImageSkia();
+}
+#endif
+
 ImageModel::ImageGeneratorAndSize::ImageGeneratorAndSize(
     ImageGenerator generator,
     gfx::Size size)
diff --git a/ui/base/models/image_model.h b/ui/base/models/image_model.h
index 47fee6d..926159ff 100644
--- a/ui/base/models/image_model.h
+++ b/ui/base/models/image_model.h
@@ -122,6 +122,11 @@
   bool operator==(const ImageModel& other) const;
   bool operator!=(const ImageModel& other) const;
 
+#if !BUILDFLAG(IS_IOS)
+  // Rasterizes if necessary.
+  gfx::ImageSkia Rasterize(const ui::ColorProvider* color_provider) const;
+#endif
+
  private:
   struct ImageGeneratorAndSize {
     ImageGeneratorAndSize(ImageGenerator generator, gfx::Size size);
diff --git a/ui/base/models/image_model_unittest.cc b/ui/base/models/image_model_unittest.cc
index d28ed91..53a06f4 100644
--- a/ui/base/models/image_model_unittest.cc
+++ b/ui/base/models/image_model_unittest.cc
@@ -6,8 +6,10 @@
 
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/vector_icons/vector_icons.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/color/color_id.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/vector_icon_types.h"
@@ -214,4 +216,28 @@
   EXPECT_EQ(image_model_src, image_model_dest);
 }
 
+#if !BUILDFLAG(IS_IOS)
+TEST(ImageModelTest, ShouldRasterizeEmptyModel) {
+  gfx::ImageSkia image_skia = ui::ImageModel().Rasterize(nullptr);
+  EXPECT_TRUE(image_skia.isNull());
+}
+
+TEST(ImageModelTest, ShouldRasterizeVectorIcon) {
+  ui::ColorProvider color_provider;
+  color_provider.GenerateColorMap();
+  gfx::ImageSkia image_skia =
+      ui::ImageModel::FromVectorIcon(vector_icons::kSyncIcon)
+          .Rasterize(&color_provider);
+  EXPECT_FALSE(image_skia.isNull());
+}
+
+TEST(ImageModelTest, ShouldRasterizeImage) {
+  gfx::Image image = gfx::test::CreateImage(16, 16);
+  gfx::ImageSkia image_skia =
+      ui::ImageModel::FromImage(image).Rasterize(nullptr);
+  EXPECT_FALSE(image_skia.isNull());
+  EXPECT_TRUE(image_skia.BackedBySameObjectAs(image.AsImageSkia()));
+}
+#endif  // !BUILDFLAG(IS_IOS)
+
 }  // namespace ui
diff --git a/ui/native_theme/themed_vector_icon.cc b/ui/base/themed_vector_icon.cc
similarity index 98%
rename from ui/native_theme/themed_vector_icon.cc
rename to ui/base/themed_vector_icon.cc
index 3f2f63e..0686fbda 100644
--- a/ui/native_theme/themed_vector_icon.cc
+++ b/ui/base/themed_vector_icon.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/native_theme/themed_vector_icon.h"
+#include "ui/base/themed_vector_icon.h"
 
 #include "ui/color/color_provider.h"
 #include "ui/gfx/paint_vector_icon.h"
diff --git a/ui/native_theme/themed_vector_icon.h b/ui/base/themed_vector_icon.h
similarity index 89%
rename from ui/native_theme/themed_vector_icon.h
rename to ui/base/themed_vector_icon.h
index 4bc6c7e..0e4e953 100644
--- a/ui/native_theme/themed_vector_icon.h
+++ b/ui/base/themed_vector_icon.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_NATIVE_THEME_THEMED_VECTOR_ICON_H_
-#define UI_NATIVE_THEME_THEMED_VECTOR_ICON_H_
+#ifndef UI_BASE_THEMED_VECTOR_ICON_H_
+#define UI_BASE_THEMED_VECTOR_ICON_H_
 
+#include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/models/image_model.h"
 #include "ui/color/color_id.h"
-#include "ui/native_theme/native_theme_export.h"
 
 namespace gfx {
 class ImageSkia;
@@ -21,7 +21,7 @@
 
 class ColorProvider;
 
-class NATIVE_THEME_EXPORT ThemedVectorIcon {
+class COMPONENT_EXPORT(UI_BASE) ThemedVectorIcon {
  public:
   ThemedVectorIcon();
   explicit ThemedVectorIcon(const gfx::VectorIcon* icon,
@@ -63,4 +63,4 @@
 
 }  // namespace ui
 
-#endif  // UI_NATIVE_THEME_THEMED_VECTOR_ICON_H_
+#endif  // UI_BASE_THEMED_VECTOR_ICON_H_
diff --git a/ui/native_theme/themed_vector_icon_unittest.cc b/ui/base/themed_vector_icon_unittest.cc
similarity index 95%
rename from ui/native_theme/themed_vector_icon_unittest.cc
rename to ui/base/themed_vector_icon_unittest.cc
index 544dbd6..f8fbb38c 100644
--- a/ui/native_theme/themed_vector_icon_unittest.cc
+++ b/ui/base/themed_vector_icon_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/native_theme/themed_vector_icon.h"
+#include "ui/base/themed_vector_icon.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/paint_vector_icon.h"
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 6db84ac..2d16acd 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -116,12 +116,6 @@
   E_CPONLY(kColorOverlayScrollbarStrokeHoveredDark) \
   E_CPONLY(kColorOverlayScrollbarStrokeHoveredLight) \
   E_CPONLY(kColorProgressBar) \
-  E_CPONLY(kColorPwaSecurityChipForeground) \
-  E_CPONLY(kColorPwaSecurityChipForegroundDangerous) \
-  E_CPONLY(kColorPwaSecurityChipForegroundPolicyCert) \
-  E_CPONLY(kColorPwaSecurityChipForegroundSecure) \
-  E_CPONLY(kColorPwaToolbarBackground) \
-  E_CPONLY(kColorPwaToolbarForeground) \
   E_CPONLY(kColorSeparator) \
   E_CPONLY(kColorShadowBase) \
   E_CPONLY(kColorShadowValueAmbientShadowElevationSixteen) \
diff --git a/ui/color/ui_color_mixer.cc b/ui/color/ui_color_mixer.cc
index 2c49e13..b438c8f 100644
--- a/ui/color/ui_color_mixer.cc
+++ b/ui/color/ui_color_mixer.cc
@@ -135,13 +135,6 @@
       SetAlpha(GetColorWithMaxContrast(kColorOverlayScrollbarFillHoveredLight),
                gfx::kGoogleGreyAlpha500);
   mixer[kColorProgressBar] = {kColorAccent};
-  mixer[kColorPwaSecurityChipForeground] = {kColorSecondaryForeground};
-  mixer[kColorPwaSecurityChipForegroundDangerous] = {kColorAlertHighSeverity};
-  mixer[kColorPwaSecurityChipForegroundPolicyCert] = {kColorDisabledForeground};
-  mixer[kColorPwaSecurityChipForegroundSecure] = {
-      kColorPwaSecurityChipForeground};
-  mixer[kColorPwaToolbarBackground] = {kColorEndpointBackground};
-  mixer[kColorPwaToolbarForeground] = {kColorEndpointForeground};
   mixer[kColorSeparator] = {kColorMidground};
   mixer[kColorShadowBase] = {dark_mode ? SK_ColorBLACK : gfx::kGoogleGrey800};
   mixer[kColorShadowValueAmbientShadowElevationThree] =
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 4124a5f..6805de3 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -635,7 +635,8 @@
 
 EGLConfig ChooseConfig(GLSurfaceFormat format,
                        bool surfaceless,
-                       bool offscreen) {
+                       bool offscreen,
+                       EGLint visual_id) {
   // Choose an EGL configuration.
   // On X this is only used for PBuffer surfaces.
 
@@ -722,7 +723,7 @@
     }
 
     std::unique_ptr<EGLConfig[]> matching_configs(new EGLConfig[num_configs]);
-    if (want_rgb565) {
+    if (want_rgb565 || visual_id >= 0) {
       config_size = num_configs;
       config_data = matching_configs.get();
     }
@@ -771,6 +772,16 @@
           return config;
         }
       }
+    } else if (visual_id >= 0) {
+      for (int i = 0; i < num_configs; i++) {
+        EGLint id;
+        if (eglGetConfigAttrib(g_egl_display, matching_configs[i],
+                               EGL_NATIVE_VISUAL_ID, &id) &&
+            id == visual_id) {
+          config = matching_configs[i];
+          break;
+        }
+      }
     }
     return config;
   }
@@ -990,11 +1001,16 @@
 
 EGLConfig GLSurfaceEGL::GetConfig() {
   if (!config_) {
-    config_ = ChooseConfig(format_, IsSurfaceless(), IsOffscreen());
+    config_ = ChooseConfig(format_, IsSurfaceless(), IsOffscreen(),
+                           GetNativeVisualID());
   }
   return config_;
 }
 
+EGLint GLSurfaceEGL::GetNativeVisualID() const {
+  return -1;
+}
+
 // static
 bool GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform native_display,
                                     uint64_t system_device_id) {
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index 87f34e2..ec63bbd 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -92,6 +92,7 @@
 
   GLSurfaceEGL(const GLSurfaceEGL&) = delete;
   GLSurfaceEGL& operator=(const GLSurfaceEGL&) = delete;
+  virtual EGLint GetNativeVisualID() const;
 
   // Implement GLSurface.
   EGLDisplay GetDisplay() override;
diff --git a/ui/gl/gl_surface_egl_x11.cc b/ui/gl/gl_surface_egl_x11.cc
index d85d999..e39323f7 100644
--- a/ui/gl/gl_surface_egl_x11.cc
+++ b/ui/gl/gl_surface_egl_x11.cc
@@ -66,6 +66,13 @@
   return result;
 }
 
+EGLint NativeViewGLSurfaceEGLX11::GetNativeVisualID() const {
+  x11::VisualId visual_id;
+  ui::XVisualManager::GetInstance()->ChooseVisualForWindow(
+      true, &visual_id, nullptr, nullptr, nullptr);
+  return static_cast<EGLint>(visual_id);
+}
+
 NativeViewGLSurfaceEGLX11::~NativeViewGLSurfaceEGLX11() {
   Destroy();
 }
diff --git a/ui/gl/gl_surface_egl_x11.h b/ui/gl/gl_surface_egl_x11.h
index 2c16496..efaffcd 100644
--- a/ui/gl/gl_surface_egl_x11.h
+++ b/ui/gl/gl_surface_egl_x11.h
@@ -27,6 +27,7 @@
   bool Initialize(GLSurfaceFormat format) override;
   void Destroy() override;
   gfx::SwapResult SwapBuffers(PresentationCallback callback) override;
+  EGLint GetNativeVisualID() const override;
 
  protected:
   ~NativeViewGLSurfaceEGLX11() override;
diff --git a/ui/native_theme/BUILD.gn b/ui/native_theme/BUILD.gn
index 377d54b1..33ca89b 100644
--- a/ui/native_theme/BUILD.gn
+++ b/ui/native_theme/BUILD.gn
@@ -22,8 +22,6 @@
     "native_theme_observer.h",
     "native_theme_utils.cc",
     "native_theme_utils.h",
-    "themed_vector_icon.cc",
-    "themed_vector_icon.h",
   ]
 
   if (is_android) {
@@ -126,7 +124,7 @@
 test("native_theme_unittests") {
   use_xvfb = use_xvfb_in_this_config
 
-  sources = [ "themed_vector_icon_unittest.cc" ]
+  sources = []
 
   if (use_aura) {
     sources += [ "native_theme_aura_unittest.cc" ]
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index b9a8822..01c9b4e 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -200,7 +200,6 @@
     "focus/focus_manager_factory.h",
     "focus/focus_search.h",
     "focus/widget_focus_manager.h",
-    "image_model_utils.h",
     "input_event_activation_protector.h",
     "interaction/element_tracker_views.h",
     "interaction/interaction_sequence_views.h",
@@ -413,7 +412,6 @@
     "focus/focus_manager_factory.cc",
     "focus/focus_search.cc",
     "focus/widget_focus_manager.cc",
-    "image_model_utils.cc",
     "input_event_activation_protector.cc",
     "interaction/element_tracker_views.cc",
     "interaction/interaction_sequence_views.cc",
@@ -1141,7 +1139,6 @@
     "event_monitor_unittest.cc",
     "focus/focus_manager_unittest.cc",
     "focus/focus_traversal_unittest.cc",
-    "image_model_utils_unittest.cc",
     "interaction/element_tracker_views_unittest.cc",
     "interaction/interaction_sequence_views_unittest.cc",
     "interaction/interaction_test_util_views_unittest.cc",
diff --git a/ui/views/background.h b/ui/views/background.h
index 3e090be..e6a44ed6 100644
--- a/ui/views/background.h
+++ b/ui/views/background.h
@@ -11,9 +11,9 @@
 
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/color/color_id.h"
 #include "ui/gfx/color_palette.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/views/views_export.h"
 
 namespace gfx {
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 0142629..9ead354 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -34,7 +34,6 @@
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/controls/image_view.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/paint_info.h"
@@ -294,8 +293,8 @@
   DCHECK(GetWidget());
   gfx::ImageSkia image;
   if (GetWidget()->widget_delegate()->ShouldShowWindowIcon()) {
-    image = GetImageSkiaFromImageModel(
-        GetWidget()->widget_delegate()->GetWindowIcon(), GetColorProvider());
+    image = GetWidget()->widget_delegate()->GetWindowIcon().Rasterize(
+        GetColorProvider());
   }
   title_icon_->SetImage(&image);
 }
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc
index a4b117c..326e2416 100644
--- a/ui/views/controls/button/image_button.cc
+++ b/ui/views/controls/button/image_button.cc
@@ -16,7 +16,6 @@
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/views/background.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/painter.h"
 #include "ui/views/widget/widget.h"
 
@@ -42,7 +41,7 @@
 ImageButton::~ImageButton() = default;
 
 gfx::ImageSkia ImageButton::GetImage(ButtonState state) const {
-  return GetImageSkiaFromImageModel(images_[state], GetColorProvider());
+  return images_[state].Rasterize(GetColorProvider());
 }
 
 void ImageButton::SetImage(ButtonState for_state, const gfx::ImageSkia* image) {
@@ -287,11 +286,9 @@
 // ToggleImageButton, ImageButton overrides:
 
 gfx::ImageSkia ToggleImageButton::GetImage(ButtonState image_state) const {
-  if (toggled_) {
-    return GetImageSkiaFromImageModel(alternate_images_[image_state],
-                                      GetColorProvider());
-  }
-  return GetImageSkiaFromImageModel(images_[image_state], GetColorProvider());
+  if (toggled_)
+    return alternate_images_[image_state].Rasterize(GetColorProvider());
+  return images_[image_state].Rasterize(GetColorProvider());
 }
 
 void ToggleImageButton::SetImageModel(ButtonState image_state,
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc
index 57e3a7d..2611d27 100644
--- a/ui/views/controls/button/label_button.cc
+++ b/ui/views/controls/button/label_button.cc
@@ -27,7 +27,6 @@
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/label_button_border.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/painter.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/views/view_class_properties.h"
@@ -67,8 +66,7 @@
 
 gfx::ImageSkia LabelButton::GetImage(ButtonState for_state) const {
   for_state = ImageStateForState(for_state);
-  return GetImageSkiaFromImageModel(button_state_image_models_[for_state],
-                                    GetColorProvider());
+  return button_state_image_models_[for_state].Rasterize(GetColorProvider());
 }
 
 void LabelButton::SetImage(ButtonState for_state, const gfx::ImageSkia& image) {
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 1e6140cd..288bb30 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -40,7 +40,6 @@
 #include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/controls/prefix_selector.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/mouse_constants.h"
 #include "ui/views/style/platform_style.h"
@@ -613,8 +612,7 @@
   // Draw the icon.
   ui::ImageModel icon = GetModel()->GetIconAt(selected_index_);
   if (!icon.IsEmpty()) {
-    gfx::ImageSkia icon_skia =
-        GetImageSkiaFromImageModel(icon, GetColorProvider());
+    gfx::ImageSkia icon_skia = icon.Rasterize(GetColorProvider());
     int icon_y = y + (contents_height - icon_skia.height()) / 2;
     gfx::Rect icon_bounds(x, icon_y, icon_skia.width(), icon_skia.height());
     AdjustBoundsForRTLUI(&icon_bounds);
@@ -724,7 +722,7 @@
       if (!icon.IsEmpty()) {
         gfx::ImageSkia icon_skia;
         if (GetWidget())
-          icon_skia = GetImageSkiaFromImageModel(icon, GetColorProvider());
+          icon_skia = icon.Rasterize(GetColorProvider());
         item_width +=
             icon_skia.width() + LayoutProvider::Get()->GetDistanceMetric(
                                     DISTANCE_RELATED_LABEL_HORIZONTAL);
diff --git a/ui/views/controls/image_view.cc b/ui/views/controls/image_view.cc
index a0825bd3..1cbd698 100644
--- a/ui/views/controls/image_view.cc
+++ b/ui/views/controls/image_view.cc
@@ -12,11 +12,10 @@
 #include "cc/paint/paint_flags.h"
 #include "skia/ext/image_operations.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/paint_vector_icon.h"
-#include "ui/native_theme/themed_vector_icon.h"
-#include "ui/views/image_model_utils.h"
 
 namespace views {
 
@@ -51,7 +50,7 @@
 }
 
 gfx::ImageSkia ImageView::GetImage() const {
-  return views::GetImageSkiaFromImageModel(image_model_, GetColorProvider());
+  return image_model_.Rasterize(GetColorProvider());
 }
 
 ui::ImageModel ImageView::GetImageModel() const {
@@ -131,8 +130,7 @@
     return gfx::ImageSkia();
 
   if (image_model_.IsImage() || image_model_.IsImageGenerator()) {
-    const gfx::ImageSkia image =
-        views::GetImageSkiaFromImageModel(image_model_, GetColorProvider());
+    const gfx::ImageSkia image = image_model_.Rasterize(GetColorProvider());
     if (image.isNull())
       return image;
 
@@ -154,8 +152,7 @@
             scaled_size.width(), scaled_size.height()),
         scale));
   } else if (scaled_image_.isNull()) {
-    scaled_image_ =
-        views::GetImageSkiaFromImageModel(image_model_, GetColorProvider());
+    scaled_image_ = image_model_.Rasterize(GetColorProvider());
   }
   return scaled_image_;
 }
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 9f8112be..6034e856 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -49,7 +49,6 @@
 #include "ui/views/controls/menu/new_badge.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/controls/separator.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/style/typography.h"
 #include "ui/views/vector_icons.h"
 #include "ui/views/view_class_properties.h"
@@ -1142,8 +1141,7 @@
   }
 
   if (!minor_icon.IsEmpty()) {
-    const gfx::ImageSkia image =
-        GetImageSkiaFromImageModel(minor_icon, GetColorProvider());
+    const gfx::ImageSkia image = minor_icon.Rasterize(GetColorProvider());
 
     int image_x = GetMirroredRect(minor_text_bounds).right() -
                   render_text->GetContentWidth() -
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index 8edc5a3c..4fb6b50d 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -16,9 +16,9 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/models/menu_separator_types.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/controls/menu/menu_types.h"
 #include "ui/views/view.h"
diff --git a/ui/views/controls/menu/menu_item_view_unittest.cc b/ui/views/controls/menu/menu_item_view_unittest.cc
index 2df7dac..0a1939d 100644
--- a/ui/views/controls/menu/menu_item_view_unittest.cc
+++ b/ui/views/controls/menu/menu_item_view_unittest.cc
@@ -13,10 +13,10 @@
 #include "base/test/bind.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/themed_vector_icon.h"
 #include "ui/compositor/canvas_painter.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
-#include "ui/native_theme/themed_vector_icon.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/menu/menu_runner.h"
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc
index 42fa1ad1..74e2447c2 100644
--- a/ui/views/controls/table/table_view.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -46,7 +46,6 @@
 #include "ui/views/controls/table/table_utils.h"
 #include "ui/views/controls/table/table_view_observer.h"
 #include "ui/views/focus/focus_manager.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/views/style/typography.h"
@@ -941,8 +940,8 @@
 
       // Always paint the icon in the first visible column.
       if (j == 0 && table_type_ == ICON_AND_TEXT) {
-        gfx::ImageSkia image = views::GetImageSkiaFromImageModel(
-            model_->GetIcon(model_index), GetColorProvider());
+        gfx::ImageSkia image =
+            model_->GetIcon(model_index).Rasterize(GetColorProvider());
         if (!image.isNull()) {
           int image_x =
               GetMirroredXWithWidthInView(text_x, ui::TableModel::kIconSize);
diff --git a/ui/views/image_model_utils.cc b/ui/views/image_model_utils.cc
deleted file mode 100644
index c4264bca..0000000
--- a/ui/views/image_model_utils.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/image_model_utils.h"
-
-#include "ui/native_theme/themed_vector_icon.h"
-
-namespace views {
-
-gfx::ImageSkia GetImageSkiaFromImageModel(
-    const ui::ImageModel& model,
-    const ui::ColorProvider* color_provider) {
-  if (model.IsImage())
-    return model.GetImage().AsImageSkia();
-
-  if (model.IsVectorIcon()) {
-    DCHECK(color_provider);
-    return ui::ThemedVectorIcon(model.GetVectorIcon())
-        .GetImageSkia(color_provider);
-  }
-
-  if (model.IsImageGenerator())
-    return model.GetImageGenerator().Run(color_provider);
-
-  return gfx::ImageSkia();
-}
-
-}  // namespace views
diff --git a/ui/views/image_model_utils.h b/ui/views/image_model_utils.h
deleted file mode 100644
index 243ebed..0000000
--- a/ui/views/image_model_utils.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_IMAGE_MODEL_UTILS_H_
-#define UI_VIEWS_IMAGE_MODEL_UTILS_H_
-
-#include "ui/gfx/image/image_skia.h"
-#include "ui/views/views_export.h"
-
-namespace ui {
-class ColorProvider;
-class ImageModel;
-}  // namespace ui
-
-namespace views {
-
-// Returns an ImageSkia representation from an ImageModel representation.
-// `color_provider` must be non-null if `model` represents a vector icon. If
-// `model` is empty, it returns an empty ImageSkia.
-VIEWS_EXPORT gfx::ImageSkia GetImageSkiaFromImageModel(
-    const ui::ImageModel& model,
-    const ui::ColorProvider* color_provider = nullptr);
-}  // namespace views
-
-#endif  // UI_VIEWS_IMAGE_MODEL_UTILS_H_
diff --git a/ui/views/image_model_utils_unittest.cc b/ui/views/image_model_utils_unittest.cc
deleted file mode 100644
index ebd65f9..0000000
--- a/ui/views/image_model_utils_unittest.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/image_model_utils.h"
-
-#include "components/vector_icons/vector_icons.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/models/image_model.h"
-#include "ui/color/color_provider.h"
-#include "ui/gfx/image/image_unittest_util.h"
-
-namespace views {
-
-TEST(GetImageSkiaFromImageModel, ShouldConvertEmptyModel) {
-  gfx::ImageSkia image_skia = GetImageSkiaFromImageModel(ui::ImageModel());
-  EXPECT_TRUE(image_skia.isNull());
-}
-
-TEST(GetImageSkiaFromImageModel, ShouldConvertVectorIcon) {
-  ui::ColorProvider color_provider;
-  color_provider.GenerateColorMap();
-  gfx::ImageSkia image_skia = GetImageSkiaFromImageModel(
-      ui::ImageModel::FromVectorIcon(vector_icons::kSyncIcon), &color_provider);
-  EXPECT_FALSE(image_skia.isNull());
-}
-
-TEST(GetImageSkiaFromImageModel, ShouldConvertImage) {
-  gfx::Image image = gfx::test::CreateImage(16, 16);
-  gfx::ImageSkia image_skia =
-      GetImageSkiaFromImageModel(ui::ImageModel::FromImage(image));
-  EXPECT_FALSE(image_skia.isNull());
-  EXPECT_TRUE(image_skia.BackedBySameObjectAs(image.AsImageSkia()));
-}
-
-}  // namespace views
diff --git a/ui/views/views_features.cc b/ui/views/views_features.cc
index a494d122..06e664b 100644
--- a/ui/views/views_features.cc
+++ b/ui/views/views_features.cc
@@ -29,7 +29,7 @@
 // flakes are mopped up, this feature will be removed.
 // https://crbug.com/1302857
 const base::Feature kFullscreenControllerMac{"FullscreenControllerMac",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
 }  // namespace features
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 35cb9c3..59208a1 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -35,7 +35,6 @@
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/focus/focus_manager_factory.h"
 #include "ui/views/focus/widget_focus_manager.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/any_widget_observer_singleton.h"
 #include "ui/views/widget/native_widget_private.h"
@@ -979,8 +978,8 @@
   if (non_client_view_)
     non_client_view_->UpdateWindowIcon();
 
-  gfx::ImageSkia window_icon = GetImageSkiaFromImageModel(
-      widget_delegate_->GetWindowIcon(), GetColorProvider());
+  gfx::ImageSkia window_icon =
+      widget_delegate_->GetWindowIcon().Rasterize(GetColorProvider());
 
   // In general, icon information is read from a |widget_delegate_| and then
   // passed to |native_widget_|. On ChromeOS, for lacros-chrome to support the
@@ -995,8 +994,8 @@
       window_icon = *icon;
   }
 
-  gfx::ImageSkia app_icon = GetImageSkiaFromImageModel(
-      widget_delegate_->GetWindowAppIcon(), GetColorProvider());
+  gfx::ImageSkia app_icon =
+      widget_delegate_->GetWindowAppIcon().Rasterize(GetColorProvider());
   if (app_icon.isNull()) {
     const gfx::ImageSkia* icon = native_widget_->GetWindowAppIcon();
     if (icon && !icon->isNull())
diff --git a/ui/views/widget/widget_delegate_unittest.cc b/ui/views/widget/widget_delegate_unittest.cc
index 3b9cc7b7..7c79bc7 100644
--- a/ui/views/widget/widget_delegate_unittest.cc
+++ b/ui/views/widget/widget_delegate_unittest.cc
@@ -10,7 +10,6 @@
 #include "ui/base/models/image_model.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_unittest_util.h"
-#include "ui/views/image_model_utils.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view.h"
 #include "ui/views/view_tracker.h"
@@ -110,10 +109,11 @@
   delegate->SetIcon(window_icon);
   gfx::ImageSkia app_icon = gfx::test::CreateImageSkia(48, 48);
   delegate->SetAppIcon(app_icon);
-  EXPECT_TRUE(GetImageSkiaFromImageModel(delegate->GetWindowIcon(), nullptr)
-                  .BackedBySameObjectAs(window_icon));
-  EXPECT_TRUE(GetImageSkiaFromImageModel(delegate->GetWindowAppIcon(), nullptr)
-                  .BackedBySameObjectAs(app_icon));
+  EXPECT_TRUE(delegate->GetWindowIcon().Rasterize(nullptr).BackedBySameObjectAs(
+      window_icon));
+  EXPECT_TRUE(
+      delegate->GetWindowAppIcon().Rasterize(nullptr).BackedBySameObjectAs(
+          app_icon));
 }
 
 TEST_F(WidgetDelegateTest, AppIconFallsBackToWindowIcon) {
@@ -122,8 +122,9 @@
   gfx::ImageSkia window_icon = gfx::test::CreateImageSkia(16, 16);
   delegate->SetIcon(window_icon);
   // Don't set an independent app icon.
-  EXPECT_TRUE(GetImageSkiaFromImageModel(delegate->GetWindowAppIcon(), nullptr)
-                  .BackedBySameObjectAs(window_icon));
+  EXPECT_TRUE(
+      delegate->GetWindowAppIcon().Rasterize(nullptr).BackedBySameObjectAs(
+          window_icon));
 }
 
 }  // namespace
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 069f754..ea08b87 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -3613,8 +3613,17 @@
 };
 
 // Deletes a Widget when the bounds change as part of toggling fullscreen.
-// This is a regression test for https://crbug.com/1197436 .
-TEST_F(DesktopWidgetTest, DeleteInSetFullscreen) {
+// This is a regression test for https://crbug.com/1197436.
+// Disabled on Mac: This test has historically deleted the Widget not during
+// SetFullscreen, but at the end of the test. When the Widget is deleted inside
+// SetFullscreen, the test crashes.
+// https://crbug.com/1307486
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_DeleteInSetFullscreen DISABLED_DeleteInSetFullscreen
+#else
+#define MAYBE_DeleteInSetFullscreen DeleteInSetFullscreen
+#endif
+TEST_F(DesktopWidgetTest, MAYBE_DeleteInSetFullscreen) {
   std::unique_ptr<Widget> widget = std::make_unique<Widget>();
   Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -3678,7 +3687,13 @@
   EXPECT_FALSE(frame->fullscreen_layout_called());
   widget->SetFullscreen(true);
   widget->Show();
+#if BUILDFLAG(IS_MAC)
+  // On macOS, a fullscreen layout is triggered from within SetFullscreen.
+  // https://crbug.com/1307496
+  EXPECT_TRUE(frame->fullscreen_layout_called());
+#else
   EXPECT_TRUE(ViewTestApi(frame).needs_layout());
+#endif
   widget->LayoutRootViewIfNecessary();
   RunPendingMessages();
 
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS b/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS
index 7027ab73..18377141 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS
@@ -1 +1 @@
-file://chromeos/components/multidevice/OWNERS
+file://ash/components/multidevice/OWNERS
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
index 6ed61574..6d70beb5f 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
@@ -6,7 +6,7 @@
 // #import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
 // #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 // #import 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js';
-// #import 'chrome://resources/mojo/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js';
+// #import 'chrome://resources/mojo/ash/components/multidevice/mojom/multidevice_types.mojom-lite.js';
 // #import 'chrome://resources/mojo/ash/services/device_sync/public/mojom/device_sync.mojom-lite.js';
 // #import 'chrome://resources/mojo/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-lite.js';
 // clang-format on
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java
index 3b27c24..210f5c6 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/CookieManagerTest.java
@@ -9,6 +9,7 @@
 import androidx.fragment.app.FragmentManager;
 import androidx.test.filters.SmallTest;
 
+import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -23,6 +24,7 @@
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
+import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -32,6 +34,7 @@
 public class CookieManagerTest {
     private CookieManager mCookieManager;
     private Uri mBaseUri;
+    private Uri mBaseUriWithPath;
 
     @Rule
     public InstrumentationActivityTestRule mActivityTestRule =
@@ -43,6 +46,7 @@
         mCookieManager = TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> { return activity.getBrowser().getProfile().getCookieManager(); });
         mBaseUri = Uri.parse(mActivityTestRule.getTestServer().getURL("/"));
+        mBaseUriWithPath = Uri.parse(mActivityTestRule.getTestServer().getURL("/path"));
     }
 
     @Test
@@ -106,6 +110,47 @@
 
     @Test
     @SmallTest
+    @MinWebLayerVersion(101)
+    public void testGetResponseCookiesSimple() throws Exception {
+        Assert.assertTrue(getResponseCookies().isEmpty());
+        Assert.assertTrue(setCookie("foo="));
+        Assert.assertThat(getResponseCookies(),
+                Matchers.containsInAnyOrder("foo=; path=/; domain=127.0.0.1; priority=medium"));
+        Assert.assertTrue(setCookie("foo=bar"));
+        Assert.assertThat(getResponseCookies(),
+                Matchers.containsInAnyOrder("foo=bar; path=/; domain=127.0.0.1; priority=medium"));
+
+        Assert.assertTrue(setCookie("baz=blah"));
+        Assert.assertThat(getResponseCookies(),
+                Matchers.containsInAnyOrder("foo=bar; path=/; domain=127.0.0.1; priority=medium",
+                        "baz=blah; path=/; domain=127.0.0.1; priority=medium"));
+    }
+
+    @Test
+    @SmallTest
+    @MinWebLayerVersion(101)
+    public void testGetResponseCookiesAllAttributes() throws Exception {
+        Assert.assertTrue(getResponseCookies().isEmpty());
+
+        // Setting a cookie with all attributes should return the same cookie.
+        String cookie = "foo=bar; path=/; domain=127.0.0.1; expires=Thu, 15 Jul 2032 00:00:01 GMT; "
+                + "secure; httponly; samesite=lax; priority=high; sameparty";
+        Assert.assertTrue(setCookie(cookie));
+        Assert.assertThat(getResponseCookies(), Matchers.containsInAnyOrder(cookie));
+    }
+
+    @Test
+    @SmallTest
+    @MinWebLayerVersion(101)
+    public void testGetResponseCookiesWithPath() throws Exception {
+        Assert.assertTrue(setCookie(mBaseUriWithPath, "foo=bar; path=/path"));
+        Assert.assertThat(getResponseCookies(mBaseUriWithPath),
+                Matchers.containsInAnyOrder(
+                        "foo=bar; path=/path; domain=127.0.0.1; priority=medium"));
+    }
+
+    @Test
+    @SmallTest
     public void testCookieChanged() throws Exception {
         CookieChangedCallbackHelper helper = new CookieChangedCallbackHelper();
         TestThreadUtils.runOnUiThreadBlocking(
@@ -163,14 +208,26 @@
         });
     }
 
+    private boolean setCookie(Uri uri, String value) throws Exception {
+        return mActivityTestRule.setCookie(mCookieManager, uri, value);
+    }
+
     private boolean setCookie(String value) throws Exception {
-        return mActivityTestRule.setCookie(mCookieManager, mBaseUri, value);
+        return setCookie(mBaseUri, value);
     }
 
     private String getCookie() throws Exception {
         return mActivityTestRule.getCookie(mCookieManager, mBaseUri);
     }
 
+    private List<String> getResponseCookies(Uri uri) throws Exception {
+        return mActivityTestRule.getResponseCookies(mCookieManager, uri);
+    }
+
+    private List<String> getResponseCookies() throws Exception {
+        return getResponseCookies(mBaseUri);
+    }
+
     private static class CookieChangedCallbackHelper extends CookieChangedCallback {
         private CallbackHelper mCallbackHelper = new CallbackHelper();
         private int mCallCount;
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java
index c2705ba..91ccdb3f 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java
@@ -33,6 +33,8 @@
 import org.chromium.weblayer.WebLayer;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -289,4 +291,17 @@
         callbackHelper.waitForFirst();
         return resultHolder[0];
     }
+
+    public List<String> getResponseCookies(CookieManager cookieManager, Uri uri) throws Exception {
+        List<String> finalResult = new ArrayList<>();
+        CallbackHelper callbackHelper = new CallbackHelper();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            cookieManager.getResponseCookies(uri, (List<String> result) -> {
+                finalResult.addAll(result);
+                callbackHelper.notifyCalled();
+            });
+        });
+        callbackHelper.waitForFirst();
+        return finalResult;
+    }
 }
diff --git a/weblayer/browser/cookie_manager_impl.cc b/weblayer/browser/cookie_manager_impl.cc
index 7743b1e..9a2510f7 100644
--- a/weblayer/browser/cookie_manager_impl.cc
+++ b/weblayer/browser/cookie_manager_impl.cc
@@ -7,10 +7,13 @@
 #include "build/build_config.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
+#include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/parsed_cookie.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/callback_android.h"
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "weblayer/browser/java/jni/CookieManagerImpl_jni.h"
@@ -27,6 +30,32 @@
   std::move(callback).Run(net::CanonicalCookie::BuildCookieLine(cookie_list));
 }
 
+void GetResponseCookiesComplete(
+    CookieManager::GetResponseCookiesCallback callback,
+    const net::CookieAccessResultList& cookies,
+    const net::CookieAccessResultList& excluded_cookies) {
+  net::CookieList cookie_list = net::cookie_util::StripAccessResults(cookies);
+  std::vector<std::string> response_cookies;
+  for (const net::CanonicalCookie& cookie : cookie_list) {
+    net::ParsedCookie parsed("");
+    parsed.SetName(cookie.Name());
+    parsed.SetValue(cookie.Value());
+    parsed.SetPath(cookie.Path());
+    parsed.SetDomain(cookie.Domain());
+    if (!cookie.ExpiryDate().is_null())
+      parsed.SetExpires(base::TimeFormatHTTP(cookie.ExpiryDate()));
+    parsed.SetIsSecure(cookie.IsSecure());
+    parsed.SetIsHttpOnly(cookie.IsHttpOnly());
+    if (cookie.SameSite() != net::CookieSameSite::UNSPECIFIED)
+      parsed.SetSameSite(net::CookieSameSiteToString(cookie.SameSite()));
+    parsed.SetPriority(net::CookiePriorityToString(cookie.Priority()));
+    parsed.SetIsSameParty(cookie.IsSameParty());
+    parsed.SetIsPartitioned(cookie.IsPartitioned());
+    response_cookies.push_back(parsed.ToCookieLine());
+  }
+  std::move(callback).Run(response_cookies);
+}
+
 #if BUILDFLAG(IS_ANDROID)
 void OnCookieChangedAndroid(
     base::android::ScopedJavaGlobalRef<jobject> callback,
@@ -38,6 +67,14 @@
           env, net::CanonicalCookie::BuildCookieLine({change.cookie})),
       static_cast<int>(change.cause));
 }
+
+void RunGetResponseCookiesCallback(
+    const base::android::JavaRef<jobject>& callback,
+    const std::vector<std::string>& cookies) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::RunObjectCallbackAndroid(
+      callback, base::android::ToJavaArrayOfStrings(env, cookies));
+}
 #endif
 
 void OnCookieChanged(CookieManager::CookieChangedCallbackList* callback_list,
@@ -81,6 +118,17 @@
                       base::BindOnce(&GetCookieComplete, std::move(callback)));
 }
 
+void CookieManagerImpl::GetResponseCookies(
+    const GURL& url,
+    GetResponseCookiesCallback callback) {
+  browser_context_->GetDefaultStoragePartition()
+      ->GetCookieManagerForBrowserProcess()
+      ->GetCookieList(
+          url, net::CookieOptions::MakeAllInclusive(),
+          net::CookiePartitionKeyCollection::Todo(),
+          base::BindOnce(&GetResponseCookiesComplete, std::move(callback)));
+}
+
 base::CallbackListSubscription CookieManagerImpl::AddCookieChangedCallback(
     const GURL& url,
     const std::string* name,
@@ -119,6 +167,16 @@
                      base::android::ScopedJavaGlobalRef<jobject>(callback)));
 }
 
+void CookieManagerImpl::GetResponseCookies(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& url,
+    const base::android::JavaParamRef<jobject>& callback) {
+  GetResponseCookies(
+      GURL(ConvertJavaStringToUTF8(url)),
+      base::BindOnce(&RunGetResponseCookiesCallback,
+                     base::android::ScopedJavaGlobalRef<jobject>(callback)));
+}
+
 int CookieManagerImpl::AddCookieChangedCallback(
     JNIEnv* env,
     const base::android::JavaParamRef<jstring>& url,
diff --git a/weblayer/browser/cookie_manager_impl.h b/weblayer/browser/cookie_manager_impl.h
index 677b96d5..23c56e903 100644
--- a/weblayer/browser/cookie_manager_impl.h
+++ b/weblayer/browser/cookie_manager_impl.h
@@ -37,6 +37,8 @@
                  const std::string& value,
                  SetCookieCallback callback) override;
   void GetCookie(const GURL& url, GetCookieCallback callback) override;
+  void GetResponseCookies(const GURL& url,
+                          GetResponseCookiesCallback callback) override;
   base::CallbackListSubscription AddCookieChangedCallback(
       const GURL& url,
       const std::string* name,
@@ -50,6 +52,9 @@
   void GetCookie(JNIEnv* env,
                  const base::android::JavaParamRef<jstring>& url,
                  const base::android::JavaParamRef<jobject>& callback);
+  void GetResponseCookies(JNIEnv* env,
+                          const base::android::JavaParamRef<jstring>& url,
+                          const base::android::JavaParamRef<jobject>& callback);
   int AddCookieChangedCallback(
       JNIEnv* env,
       const base::android::JavaParamRef<jstring>& url,
diff --git a/weblayer/browser/download_manager_delegate_impl.cc b/weblayer/browser/download_manager_delegate_impl.cc
index 0dc1351..e74bfa9 100644
--- a/weblayer/browser/download_manager_delegate_impl.cc
+++ b/weblayer/browser/download_manager_delegate_impl.cc
@@ -90,7 +90,8 @@
         download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
         download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
         download::DownloadItem::MixedContentStatus::UNKNOWN,
-        item->GetForcedFilePath(), absl::nullopt /*download_schedule*/,
+        item->GetForcedFilePath(), base::FilePath(),
+        absl::nullopt /*download_schedule*/,
         download::DOWNLOAD_INTERRUPT_REASON_NONE);
     return true;
   }
@@ -262,7 +263,7 @@
       download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
       download::DownloadItem::MixedContentStatus::UNKNOWN,
       suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")),
-      absl::nullopt /*download_schedule*/,
+      base::FilePath(), absl::nullopt /*download_schedule*/,
       download::DOWNLOAD_INTERRUPT_REASON_NONE);
 }
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java
index ba3e80a..d820868 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java
@@ -19,6 +19,8 @@
 import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
 
 import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Implementation of ICookieManager.
@@ -54,6 +56,16 @@
     }
 
     @Override
+    public void getResponseCookies(String url, IObjectWrapper callback) {
+        StrictModeWorkaround.apply();
+        ValueCallback<List<String>> valueCallback =
+                (ValueCallback<List<String>>) ObjectWrapper.unwrap(callback, ValueCallback.class);
+        Callback<String[]> baseCallback =
+                (String[] result) -> valueCallback.onReceiveValue(Arrays.asList(result));
+        CookieManagerImplJni.get().getResponseCookies(mNativeCookieManager, url, baseCallback);
+    }
+
+    @Override
     public IObjectWrapper addCookieChangedCallback(
             String url, String name, ICookieChangedCallbackClient callback) {
         StrictModeWorkaround.apply();
@@ -105,6 +117,8 @@
         boolean setCookie(
                 long nativeCookieManagerImpl, String url, String value, Callback<Boolean> callback);
         void getCookie(long nativeCookieManagerImpl, String url, Callback<String> callback);
+        void getResponseCookies(
+                long nativeCookieManagerImpl, String url, Callback<String[]> callback);
         int addCookieChangedCallback(long nativeCookieManagerImpl, String url, String name,
                 ICookieChangedCallbackClient callback);
         void removeCookieChangedCallback(long nativeCookieManagerImpl, int id);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl
index 397eab9..5a648cb9 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ICookieManager.aidl
@@ -13,4 +13,8 @@
   void getCookie(in String url, in IObjectWrapper callback) = 1;
 
   IObjectWrapper addCookieChangedCallback(in String url, in String name, ICookieChangedCallbackClient callback) = 2;
+
+  // Added in 101.
+  void getResponseCookies(in String url, in IObjectWrapper callback) = 3;
+
 }
diff --git a/weblayer/public/cookie_manager.h b/weblayer/public/cookie_manager.h
index 0d5027b..07b14cfe 100644
--- a/weblayer/public/cookie_manager.h
+++ b/weblayer/public/cookie_manager.h
@@ -31,6 +31,13 @@
   using GetCookieCallback = base::OnceCallback<void(const std::string&)>;
   virtual void GetCookie(const GURL& url, GetCookieCallback callback) = 0;
 
+  // Gets the cookies for the given URL in the form of the 'Set-Cookie' HTTP
+  // response header.
+  using GetResponseCookiesCallback =
+      base::OnceCallback<void(const std::vector<std::string>&)>;
+  virtual void GetResponseCookies(const GURL& url,
+                                  GetResponseCookiesCallback callback) = 0;
+
   // Adds a callback to listen for changes to cookies for the given URL.
   using CookieChangedCallbackList =
       base::RepeatingCallbackList<void(const net::CookieChangeInfo&)>;
diff --git a/weblayer/public/java/org/chromium/weblayer/CookieManager.java b/weblayer/public/java/org/chromium/weblayer/CookieManager.java
index 9c71938..1b3f41c 100644
--- a/weblayer/public/java/org/chromium/weblayer/CookieManager.java
+++ b/weblayer/public/java/org/chromium/weblayer/CookieManager.java
@@ -18,6 +18,8 @@
 import org.chromium.weblayer_private.interfaces.ObjectWrapper;
 import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
 
+import java.util.List;
+
 /**
  * Manages cookies for a WebLayer profile.
  */
@@ -89,6 +91,29 @@
     }
 
     /**
+     * Gets the cookies for the given URL in the form of the 'Set-Cookie' HTTP response header.
+     *
+     * @param uri the URI to get cookies for.
+     * @param callback a callback to be executed with a list of cookie strings in the format of the
+     *     'Set-Cookie' HTTP response header.
+     * @since 101
+     */
+    public void getResponseCookies(@NonNull Uri uri, @NonNull Callback<List<String>> callback) {
+        ThreadCheck.ensureOnUiThread();
+        if (WebLayer.getSupportedMajorVersionInternal() < 101) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            ValueCallback<List<String>> valueCallback = (List<String> result) -> {
+                callback.onResult(result);
+            };
+            mImpl.getResponseCookies(uri.toString(), ObjectWrapper.wrap(valueCallback));
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    /**
      * Adds a callback to listen for changes to cookies for the given URI.
      *
      * @param uri the URI to listen to cookie changes on.
diff --git a/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
index 2d82ee1..3156402 100644
--- a/weblayer/public/java/org/chromium/weblayer/WebLayer.java
+++ b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
@@ -586,6 +586,9 @@
      */
     public String getXClientDataHeader() {
         ThreadCheck.ensureOnUiThread();
+        if (getSupportedMajorVersionInternal() < 101) {
+            throw new UnsupportedOperationException();
+        }
         try {
             return mImpl.getXClientDataHeader();
         } catch (RemoteException e) {