diff --git a/DEPS b/DEPS
index 57d6e0b..1dbaa8c 100644
--- a/DEPS
+++ b/DEPS
@@ -269,19 +269,19 @@
   # 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': 'c43555b2f910f3e8cbc2d2597fec260fdff4e168',
+  'skia_revision': '0d8b218f8a386d24d3d201a9ae777b3fc084db38',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '8bd4d4ff99cb81c308a98e527a24dff2a0514e38',
+  'v8_revision': '781bcedfd4ee8c4a4bd4833cb75978d95ed94111',
   # 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': 'ee1894e5fcb85db29a900354b2c7f58a698bd01a',
+  'angle_revision': '7c83a3635d623f090b94b6bdaaad9152fede12f3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'e07da52caba554450749a084549608ee347ac3ef',
+  'swiftshader_revision': 'b5cb037f029213553f194ca4203d6d3c03639ebc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -296,7 +296,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:8.20220520.2.1',
+  'fuchsia_version': 'version:8.20220520.3.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -340,7 +340,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': '811104909bad1a58c2f05332a9057a3dba347237',
+  'catapult_revision': 'a1cf7a299ecea4633a1ea4c2c40dcd345220820a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -432,7 +432,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.
-  'libunwind_revision':    'c9b2288d0c3c312bb7ecdb52659db88679de309d',
+  'libunwind_revision':    'd03f56b8fa9c457d192b3eeddbe16a8187f553d1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -745,7 +745,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'd6f38aa83c84f31185b16b8f138cace09c8343f5',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'ea17ef1fd0fd487a870a135c7724ea7b6c2e8e2f',
       'condition': 'checkout_ios',
   },
 
@@ -914,7 +914,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '6UlL8Kfql-lL2RalyoIAmkibNjfUbOy9MP3nUIVQVPYC',
+          'version': 'RB0mb2ryaZJKy878dbB61Agwpxw3Qbz87u7kfIPB1oQC',
       },
     ],
     'condition': 'checkout_android',
@@ -1133,7 +1133,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'cf9a77671386713037eeed5c62a8f0a10b25a07e',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0e9a7d264340ded17db0c991d27da74d78a0719f',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1287,7 +1287,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '41cdffd71c9948f63c7ad36e1fb0ff519aa7a37e',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '1c67b4e7d31b800b4d02349fe42744417b34eed8',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '4c1a868725d1226c13da64e3a5bfff2b8ae1a63b',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1666,7 +1666,7 @@
       'condition': 'checkout_android',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@0198743a9a6410443a3968a594726ec44cc4b44a',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@aef219516acf143e0c3c91f1bd660c0ce19962be',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1702,10 +1702,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd4d598789af1e73a1bdd6c9eec246e75ff43551a',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'b5cc682434bf84956f9f2bc270e053e6711b9efa',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9618103c4aa25ea342ddad65848ff8bb0c1cee9a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '213d389d567315274c71402526045e2a2a47035e',
+    Var('webrtc_git') + '/src.git' + '@' + 'fc412ed8535079b65168dc9f1dbc3dbf9fccd161',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1778,7 +1778,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@757a52ccc9e8426cff38944dcb811e73730aa5d1',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@dba172f98a44c007e565ccb1f719a639090448a1',
     'condition': 'checkout_src_internal',
   },
 
@@ -1786,7 +1786,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/assistant/ambient',
-        'version': 'version:float_on_by_background_color_fix',
+        'version': 'version:feel_the_breeze_swinging_fix',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 4b9bd9f..707fd01 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -2786,10 +2786,16 @@
 
 
 @dataclass
+class _SecurityProblemWithItems:
+    problem: str
+    items: Sequence[str]
+
+
+@dataclass
 class _MissingSecurityOwnersResult:
-    owners_file_errors: Sequence[str]
+    owners_file_problems: Sequence[_SecurityProblemWithItems]
     has_security_sensitive_files: bool
-    missing_reviewer_errors: Sequence[str]
+    missing_reviewer_problem: Optional[_SecurityProblemWithItems]
 
 
 def _FindMissingSecurityOwners(input_api,
@@ -2888,52 +2894,66 @@
                 break
 
     has_security_sensitive_files = bool(to_check)
-    missing_reviewer_errors = []
-    if files_to_review and not _ChangeHasSecurityReviewer(
+
+    # Check if any newly added lines in OWNERS files intersect with required
+    # per-file OWNERS lines. If so, ensure that a security reviewer is included.
+    # This is a hack, but is needed because the OWNERS check (by design) ignores
+    # new OWNERS entries; otherwise, a non-owner could add someone as a new
+    # OWNER and have that newly-added OWNER self-approve their own addition.
+    newly_covered_files = []
+    for file in input_api.AffectedFiles(include_deletes=False):
+        if not file.LocalPath() in to_check:
+            continue
+        for _, line in file.ChangedContents():
+            for _, entry in to_check[file.LocalPath()].items():
+                if line in entry['rules']:
+                    newly_covered_files.extend(entry['files'])
+
+    missing_reviewer_problems = None
+    if newly_covered_files and not _ChangeHasSecurityReviewer(
             input_api, required_owners_file):
-        joined_files_to_review = '\n'.join(f'  {file}'
-                                           for file in files_to_review)
-        missing_reviewer_errors.append(
-            f'Code review from an owner in //{required_owners_file} is required '
-            'for this change for the following files:\n'
-            f'{joined_files_to_review}')
+        missing_reviewer_problems = _SecurityProblemWithItems(
+            f'Review from an owner in {required_owners_file} is required for '
+            'the following newly-added files:',
+            [f'{file}' for file in sorted(set(newly_covered_files))])
 
     # Go through the OWNERS files to check, filtering out rules that are already
     # present in that OWNERS file.
     for owners_file, patterns in to_check.items():
         try:
-            with open(owners_file) as f:
-                lines = set(f.read().splitlines())
-                for entry in patterns.values():
-                    entry['rules'] = [
-                        rule for rule in entry['rules'] if rule not in lines
-                    ]
+            lines = set(
+                input_api.ReadFile(
+                    input_api.os_path.join(input_api.change.RepositoryRoot(),
+                                           owners_file)).splitlines())
+            for entry in patterns.values():
+                entry['rules'] = [
+                    rule for rule in entry['rules'] if rule not in lines
+                ]
         except IOError:
             # No OWNERS file, so all the rules are definitely missing.
             continue
 
     # All the remaining lines weren't found in OWNERS files, so emit an error.
-    owners_file_errors = []
+    owners_file_problems = []
 
     for owners_file, patterns in to_check.items():
         missing_lines = []
         files = []
         for _, entry in patterns.items():
+            files.extend(f.LocalPath() for f in entry['files'])
             missing_lines.extend(entry['rules'])
-            files.extend(
-                ['  %s' % file.LocalPath() for file in entry['files']])
         if missing_lines:
-            joined_files = '\n'.join(files)
-            joined_missing_lines = '\n'.join(missing_lines)
-            owners_file_errors.append(
-                f'Because of the presence of files:\n{joined_files}\n\n'
-                f'{owners_file} needs the following {len(missing_lines)} '
-                'line(s) added:\n\n'
-                f'{joined_missing_lines}')
+            joined_missing_lines = '\n'.join(line for line in missing_lines)
+            owners_file_problems.append(
+                _SecurityProblemWithItems(
+                    'Found missing OWNERS lines for security-sensitive files. '
+                    f'Please add the following lines to {owners_file}:\n'
+                    f'{joined_missing_lines}\n\nTo ensure security review for:',
+                    files))
 
-    return _MissingSecurityOwnersResult(owners_file_errors,
+    return _MissingSecurityOwnersResult(owners_file_problems,
                                         has_security_sensitive_files,
-                                        missing_reviewer_errors)
+                                        missing_reviewer_problems)
 
 
 def _CheckChangeForIpcSecurityOwners(input_api, output_api):
@@ -3021,46 +3041,39 @@
 
     results = []
 
-    # Ensure that a security reviewer is included as a CL reviewer. This is a
-    # hack, but is needed because the OWNERS check (by design) ignores new
-    # OWNERS entries; otherwise, a non-owner could add someone as a new OWNER
-    # and have that newly-added OWNER self-approve their own addition.
-    missing_reviewer_errors = []
-    missing_reviewer_errors.extend(ipc_results.missing_reviewer_errors)
-    missing_reviewer_errors.extend(fuchsia_results.missing_reviewer_errors)
+    missing_reviewer_problems = []
+    if ipc_results.missing_reviewer_problem:
+        missing_reviewer_problems.append(ipc_results.missing_reviewer_problem)
+    if fuchsia_results.missing_reviewer_problem:
+        missing_reviewer_problems.append(
+            fuchsia_results.missing_reviewer_problem)
 
-    if missing_reviewer_errors:
-        # Missing reviewers are an error unless there's no issue number
-        # associated with this branch; in that case, the presubmit is being run
-        # with --all or --files.
-        #
-        # Note that upload should never be an error; otherwise, it would be
-        # impossible to upload changes at all.
-        if input_api.is_committing and input_api.change.issue:
-            make_presubmit_message = output_api.PresubmitError
-        else:
-            make_presubmit_message = output_api.PresubmitNotifyResult
+    # Missing reviewers are an error unless there's no issue number
+    # associated with this branch; in that case, the presubmit is being run
+    # with --all or --files.
+    #
+    # Note that upload should never be an error; otherwise, it would be
+    # impossible to upload changes at all.
+    if input_api.is_committing and input_api.change.issue:
+        make_presubmit_message = output_api.PresubmitError
+    else:
+        make_presubmit_message = output_api.PresubmitNotifyResult
+    for problem in missing_reviewer_problems:
         results.append(
-            make_presubmit_message(
-                'Found missing security reviewers:',
-                long_text='\n\n'.join(missing_reviewer_errors)))
+            make_presubmit_message(problem.problem, items=problem.items))
 
-    owners_file_errors = []
-    owners_file_errors.extend(ipc_results.owners_file_errors)
-    owners_file_errors.extend(fuchsia_results.owners_file_errors)
+    owners_file_problems = []
+    owners_file_problems.extend(ipc_results.owners_file_problems)
+    owners_file_problems.extend(fuchsia_results.owners_file_problems)
 
-    if owners_file_errors:
+    for problem in owners_file_problems:
         # Missing per-file rules are always an error. While swarming and caching
         # means that uploading a patchset with updated OWNERS files and sending
         # it to the CQ again should not have a large incremental cost, it is
         # still frustrating to discover the error only after the change has
         # already been uploaded.
         results.append(
-            output_api.PresubmitError(
-                'Found OWNERS files with missing per-file rules for '
-                'security-sensitive files.\nPlease update the OWNERS files '
-                'below to add the missing rules:',
-                long_text='\n\n'.join(owners_file_errors)))
+            output_api.PresubmitError(problem.problem, items=problem.items))
 
     return results
 
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 36ab26e..fa2cb41f 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -2292,6 +2292,16 @@
 
 
 class _SecurityOwnersTestCase(unittest.TestCase):
+  def _createMockInputApi(self):
+    mock_input_api = MockInputApi()
+    def FakeRepositoryRoot():
+      return mock_input_api.os_path.join('chromium', 'src')
+    mock_input_api.change.RepositoryRoot = FakeRepositoryRoot
+    self._injectFakeOwnersClient(
+        mock_input_api,
+        ['apple@chromium.org', 'orange@chromium.org'])
+    return mock_input_api
+
   def _setupFakeChange(self, input_api):
     class FakeGerrit(object):
       def IsOwnersOverrideApproved(self, issue):
@@ -2329,17 +2339,59 @@
       ('*.aidl', 'scary.aidl'),
   ]
 
-  # TODO(dcheng): add tests for when there are no missing per-file rules. These
-  # are currently missing because `open()` is not injected.
-  def testMissingSecurityReviewerAtUpload(self):
-    mock_input_api = MockInputApi()
+  def testHasCorrectPerFileRulesAndSecurityReviewer(self):
+    mock_input_api = self._createMockInputApi()
+    new_owners_file_path = mock_input_api.os_path.join(
+        'services', 'goat', 'public', 'OWNERS')
+    new_owners_file = [
+        'per-file *.mojom=set noparent',
+        'per-file *.mojom=file://ipc/SECURITY_OWNERS'
+    ]
+    def FakeReadFile(filename):
+      self.assertEqual(
+          mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
+          filename)
+      return '\n'.join(new_owners_file)
+    mock_input_api.ReadFile = FakeReadFile
     mock_input_api.files = [
-      MockAffectedFile(f'services/goat/public/goat.mojom',
-                       ['// Scary contents.'])]
+      MockAffectedFile(
+          new_owners_file_path, new_owners_file),
+      MockAffectedFile(
+          mock_input_api.os_path.join(
+              'services', 'goat', 'public', 'goat.mojom'),
+          ['// Scary contents.'])]
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(
-        mock_input_api,
-        ['apple@chromium.org', 'orange@chromium.org'])
+    self._injectFakeChangeOwnerAndReviewers(
+        mock_input_api, 'owner@chromium.org', ['orange@chromium.org'])
+    mock_input_api.is_committing = True
+    mock_input_api.dry_run = False
+    mock_output_api = MockOutputApi()
+    results = PRESUBMIT.CheckSecurityOwners(
+        mock_input_api, mock_output_api)
+    self.assertEqual(0, len(results))
+
+  def testMissingSecurityReviewerAtUpload(self):
+    mock_input_api = self._createMockInputApi()
+    new_owners_file_path = mock_input_api.os_path.join(
+        'services', 'goat', 'public', 'OWNERS')
+    new_owners_file = [
+        'per-file *.mojom=set noparent',
+        'per-file *.mojom=file://ipc/SECURITY_OWNERS'
+    ]
+    def FakeReadFile(filename):
+      self.assertEqual(
+          mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
+          filename)
+      return '\n'.join(new_owners_file)
+    mock_input_api.ReadFile = FakeReadFile
+    mock_input_api.files = [
+      MockAffectedFile(
+          new_owners_file_path, new_owners_file),
+      MockAffectedFile(
+          mock_input_api.os_path.join(
+              'services', 'goat', 'public', 'goat.mojom'),
+          ['// Scary contents.'])]
+    self._setupFakeChange(mock_input_api)
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.is_committing = False
@@ -2347,22 +2399,34 @@
     mock_output_api = MockOutputApi()
     results = PRESUBMIT.CheckSecurityOwners(
         mock_input_api, mock_output_api)
-    # TODO(dcheng): This should be 1, but the PRESUBMIT currently opens the
-    # OWNERS file in an unmockable way.
-    self.assertEqual(2, len(results))
+    self.assertEqual(1, len(results))
     self.assertEqual('notify', results[0].type)
     self.assertEqual(
-        'Found missing security reviewers:', results[0].message)
+        'Review from an owner in ipc/SECURITY_OWNERS is required for the '
+        'following newly-added files:', results[0].message)
 
   def testMissingSecurityReviewerAtDryRunCommit(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
+    new_owners_file_path = mock_input_api.os_path.join(
+        'services', 'goat', 'public', 'OWNERS')
+    new_owners_file = [
+        'per-file *.mojom=set noparent',
+        'per-file *.mojom=file://ipc/SECURITY_OWNERS'
+    ]
+    def FakeReadFile(filename):
+      self.assertEqual(
+          mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
+          filename)
+      return '\n'.join(new_owners_file)
+    mock_input_api.ReadFile = FakeReadFile
     mock_input_api.files = [
-      MockAffectedFile(f'services/goat/public/goat.mojom',
-                       ['// Scary contents.'])]
+      MockAffectedFile(
+          new_owners_file_path, new_owners_file),
+      MockAffectedFile(
+          mock_input_api.os_path.join(
+              'services', 'goat', 'public', 'goat.mojom'),
+          ['// Scary contents.'])]
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(
-        mock_input_api,
-        ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.is_committing = True
@@ -2370,22 +2434,34 @@
     mock_output_api = MockOutputApi()
     results = PRESUBMIT.CheckSecurityOwners(
         mock_input_api, mock_output_api)
-    # TODO(dcheng): This should be 1, but the PRESUBMIT currently opens the
-    # OWNERS file in an unmockable way.
-    self.assertEqual(2, len(results))
+    self.assertEqual(1, len(results))
     self.assertEqual('error', results[0].type)
     self.assertEqual(
-        'Found missing security reviewers:', results[0].message)
+        'Review from an owner in ipc/SECURITY_OWNERS is required for the '
+        'following newly-added files:', results[0].message)
 
   def testmissingSecurityApprovalAtRealCommit(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
+    new_owners_file_path = mock_input_api.os_path.join(
+        'services', 'goat', 'public', 'OWNERS')
+    new_owners_file = [
+        'per-file *.mojom=set noparent',
+        'per-file *.mojom=file://ipc/SECURITY_OWNERS'
+    ]
+    def FakeReadFile(filename):
+      self.assertEqual(
+          mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
+          filename)
+      return '\n'.join(new_owners_file)
+    mock_input_api.ReadFile = FakeReadFile
     mock_input_api.files = [
-      MockAffectedFile(f'services/goat/public/goat.mojom',
-                       ['// Scary contents.'])]
+      MockAffectedFile(
+          new_owners_file_path, new_owners_file),
+      MockAffectedFile(
+          mock_input_api.os_path.join(
+              'services', 'goat', 'public', 'goat.mojom'),
+          ['// Scary contents.'])]
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(
-        mock_input_api,
-        ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.is_committing = True
@@ -2393,50 +2469,23 @@
     mock_output_api = MockOutputApi()
     results = PRESUBMIT.CheckSecurityOwners(
         mock_input_api, mock_output_api)
-    # TODO(dcheng): This should be 1, but the PRESUBMIT currently opens the
-    # OWNERS file in an unmockable way.
-    self.assertEqual(2, len(results))
     self.assertEqual('error', results[0].type)
     self.assertEqual(
-        'Found missing security reviewers:', results[0].message)
-
-  def testMissingPerFileRulesButNotSecurityReviewer(self):
-    for pattern, filename in self._test_cases:
-      with self.subTest(line=filename):
-        mock_input_api = MockInputApi()
-        mock_input_api.files = [
-          MockAffectedFile(f'services/goat/public/{filename}',
-                           ['// Scary contents.'])]
-        self._setupFakeChange(mock_input_api)
-        self._injectFakeOwnersClient(
-            mock_input_api,
-            ['apple@chromium.org', 'orange@chromium.org'])
-        self._injectFakeChangeOwnerAndReviewers(
-            mock_input_api, 'owner@chromium.org', ['orange@chromium.org'])
-        mock_output_api = MockOutputApi()
-        errors = PRESUBMIT.CheckSecurityOwners(
-            mock_input_api, mock_output_api)
-        self.assertEqual(1, len(errors))
-        self.assertEqual(
-            'Found OWNERS files with missing per-file rules for '
-            'security-sensitive files.\nPlease update the OWNERS files below '
-            'to add the missing rules:', errors[0].message)
-        self.assertEqual(['ipc-security-reviews@chromium.org'],
-                         mock_output_api.more_cc)
+        'Review from an owner in ipc/SECURITY_OWNERS is required for the '
+        'following newly-added files:', results[0].message)
 
   def testIpcChangeNeedsSecurityOwner(self):
     for is_committing in [True, False]:
       for pattern, filename in self._test_cases:
         with self.subTest(
             line=f'is_committing={is_committing}, filename={filename}'):
-          mock_input_api = MockInputApi()
+          mock_input_api = self._createMockInputApi()
           mock_input_api.files = [
-            MockAffectedFile(f'services/goat/public/{filename}',
-                             ['// Scary contents.'])]
+            MockAffectedFile(
+                mock_input_api.os_path.join(
+                    'services', 'goat', 'public', filename),
+                ['// Scary contents.'])]
           self._setupFakeChange(mock_input_api)
-          self._injectFakeOwnersClient(
-              mock_input_api,
-              ['apple@chromium.org', 'orange@chromium.org'])
           self._injectFakeChangeOwnerAndReviewers(
               mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
           mock_input_api.is_committing = is_committing
@@ -2444,51 +2493,39 @@
           mock_output_api = MockOutputApi()
           results = PRESUBMIT.CheckSecurityOwners(
               mock_input_api, mock_output_api)
-          self.assertEqual(2, len(results))
-          if is_committing:
-            self.assertEqual('error', results[0].type)
-          else:
-            self.assertEqual('notify', results[0].type)
-          self.assertEqual(
-              'Found missing security reviewers:', results[0].message)
-          self.assertEqual('error', results[1].type)
-          self.assertEqual(
-              'Found OWNERS files with missing per-file rules for '
-              'security-sensitive files.\nPlease update the OWNERS files below '
-              'to add the missing rules:', results[1].message)
+          self.assertEqual(1, len(results))
+          self.assertEqual('error', results[0].type)
+          self.assertTrue(results[0].message.replace('\\', '/').startswith(
+              'Found missing OWNERS lines for security-sensitive files. '
+              'Please add the following lines to services/goat/public/OWNERS:'))
           self.assertEqual(['ipc-security-reviews@chromium.org'],
                            mock_output_api.more_cc)
 
 
   def testServiceManifestChangeNeedsSecurityOwner(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
     mock_input_api.files = [
-      MockAffectedFile('services/goat/public/cpp/manifest.cc',
-                       [
-                         '#include "services/goat/public/cpp/manifest.h"',
-                         'const service_manager::Manifest& GetManifest() {}',
-                       ])]
+      MockAffectedFile(
+          mock_input_api.os_path.join(
+              'services', 'goat', 'public', 'cpp', 'manifest.cc'),
+              [
+                '#include "services/goat/public/cpp/manifest.h"',
+                'const service_manager::Manifest& GetManifest() {}',
+              ])]
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_output_api = MockOutputApi()
     errors = PRESUBMIT.CheckSecurityOwners(
         mock_input_api, mock_output_api)
-    self.assertEqual(2, len(errors))
-    self.assertEqual(
-        'Found missing security reviewers:', errors[0].message)
-    self.assertEqual(
-        'Found OWNERS files with missing per-file rules for security-sensitive '
-        'files.\nPlease update the OWNERS files below to '
-        'add the missing rules:', errors[1].message)
+    self.assertEqual(1, len(errors))
+    self.assertTrue(errors[0].message.replace('\\', '/').startswith(
+        'Found missing OWNERS lines for security-sensitive files. '
+        'Please add the following lines to services/goat/public/cpp/OWNERS:'))
     self.assertEqual(['ipc-security-reviews@chromium.org'], mock_output_api.more_cc)
 
   def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
-    mock_input_api = MockInputApi()
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
+    mock_input_api = self._createMockInputApi()
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.files = [
@@ -2505,78 +2542,61 @@
 
 class FuchsiaSecurityOwnerTest(_SecurityOwnersTestCase):
   def testFidlChangeNeedsSecurityOwner(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
     mock_input_api.files = [
       MockAffectedFile('potentially/scary/ipc.fidl',
                        [
                          'library test.fidl'
                        ])]
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_output_api = MockOutputApi()
     errors = PRESUBMIT.CheckSecurityOwners(
         mock_input_api, mock_output_api)
-    self.assertEqual(2, len(errors))
-    self.assertEqual(
-        'Found missing security reviewers:', errors[0].message)
-    self.assertEqual(
-        'Found OWNERS files with missing per-file rules for security-sensitive '
-        'files.\nPlease update the OWNERS files below to '
-        'add the missing rules:', errors[1].message)
+    self.assertEqual(1, len(errors))
+    self.assertTrue(errors[0].message.replace('\\', '/').startswith(
+        'Found missing OWNERS lines for security-sensitive files. '
+        'Please add the following lines to potentially/scary/OWNERS:'))
 
   def testComponentManifestV1ChangeNeedsSecurityOwner(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
     mock_input_api.files = [
       MockAffectedFile('potentially/scary/v2_manifest.cmx',
                        [
                          '{ "that is no": "manifest!" }'
                        ])]
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_output_api = MockOutputApi()
     errors = PRESUBMIT.CheckSecurityOwners(
         mock_input_api, mock_output_api)
-    self.assertEqual(2, len(errors))
-    self.assertEqual(
-        'Found missing security reviewers:', errors[0].message)
-    self.assertEqual(
-        'Found OWNERS files with missing per-file rules for security-sensitive '
-        'files.\nPlease update the OWNERS files below to '
-        'add the missing rules:', errors[1].message)
+    self.assertEqual(1, len(errors))
+    self.assertTrue(errors[0].message.replace('\\', '/').startswith(
+        'Found missing OWNERS lines for security-sensitive files. '
+        'Please add the following lines to potentially/scary/OWNERS:'))
 
   def testComponentManifestV2NeedsSecurityOwner(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
     mock_input_api.files = [
       MockAffectedFile('potentially/scary/v2_manifest.cml',
                        [
                          '{ "that is no": "manifest!" }'
                        ])]
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_output_api = MockOutputApi()
     errors = PRESUBMIT.CheckSecurityOwners(
         mock_input_api, mock_output_api)
-    self.assertEqual(2, len(errors))
-    self.assertEqual(
-        'Found missing security reviewers:', errors[0].message)
-    self.assertEqual(
-        'Found OWNERS files with missing per-file rules for security-sensitive '
-        'files.\nPlease update the OWNERS files below to '
-        'add the missing rules:', errors[1].message)
+    self.assertEqual(1, len(errors))
+    self.assertTrue(errors[0].message.replace('\\', '/').startswith(
+        'Found missing OWNERS lines for security-sensitive files. '
+        'Please add the following lines to potentially/scary/OWNERS:'))
 
   def testThirdPartyTestsDoNotRequireSecurityOwner(self):
     mock_input_api = MockInputApi()
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.files = [
@@ -2591,8 +2611,6 @@
 
   def testOtherFuchsiaChangesDoNotRequireSecurityOwner(self):
     mock_input_api = MockInputApi()
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.files = [
@@ -2655,10 +2673,8 @@
         files_to_functions)
 
   def testChangeOwnersMissing(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.is_committing = False
@@ -2676,10 +2692,8 @@
         '    content::GetServiceSandboxType<>()\n\n')
 
   def testChangeOwnersMissingAtCommit(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
     mock_input_api.is_committing = True
@@ -2698,9 +2712,7 @@
         '    content::GetServiceSandboxType<>()\n\n')
 
   def testChangeOwnersPresent(self):
-    mock_input_api = MockInputApi()
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
+    mock_input_api = self._createMockInputApi()
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'owner@chromium.org',
         ['apple@chromium.org', 'banana@chromium.org'])
@@ -2712,10 +2724,8 @@
     self.assertEqual(0, len(result))
 
   def testChangeOwnerIsSecurityOwner(self):
-    mock_input_api = MockInputApi()
+    mock_input_api = self._createMockInputApi()
     self._setupFakeChange(mock_input_api)
-    self._injectFakeOwnersClient(mock_input_api,
-                                 ['apple@chromium.org', 'orange@chromium.org'])
     self._injectFakeChangeOwnerAndReviewers(
         mock_input_api, 'orange@chromium.org', ['pear@chromium.org'])
     mock_input_api.files = [
diff --git a/android_webview/docs/webview-shell.md b/android_webview/docs/webview-shell.md
index db0a218..cd07e540 100644
--- a/android_webview/docs/webview-shell.md
+++ b/android_webview/docs/webview-shell.md
@@ -83,13 +83,11 @@
 We maintain a **public** archive of prebuilt WebView shell APKs. This saves you
 the effort of setting up a chromium checkout just for the sake of compiling this
 test app. You can download a prebuilt APK from this cloud storage bucket:
-https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Android/
+https://storage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Android/
 
-1. The builds are sorted from oldest to newest. You should scroll to the
-   **bottom of the page** to get the latest revision. Note that the page can
-   take some time to load all the revisions, so you may have to scroll multiple
-   times. Keep scrolling until you see a file named `LAST_CHANGE`, then pick the
-   numbered folder immediately above it.
+1. Click on the "name" column header **twice**. This will force the page to sort
+   the newest builds toward the top. Click the folder with the largest number,
+   ignoring the `LAST_CHANGE` and `refs_heads_main-*/` folders.
      * Your WebView shell version **does not** need to match your device's
        WebView version. We recommend using the latest WebView shell build
        regardless of your WebView version to make sure you have the latest
@@ -106,6 +104,14 @@
      $ adb install -d -r ~/Downloads/chrome-android/SystemWebViewShell.apk
      ```
 
+*** note
+**Note:** on the Android emulator, this may fail to install and print the
+`INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package ... signatures do not match
+previously installed version` error message. This may require switching to a
+physical device or compiling the WebView shell from source (see the
+troubleshooting steps below).
+***
+
 ## Troubleshooting
 
 ### INSTALL\_FAILED\_UPDATE\_INCOMPATIBLE: Package ... signatures do not match previously installed version
diff --git a/ash/capture_mode/capture_mode_camera_controller.cc b/ash/capture_mode/capture_mode_camera_controller.cc
index 1efd991..73465f9 100644
--- a/ash/capture_mode/capture_mode_camera_controller.cc
+++ b/ash/capture_mode/capture_mode_camera_controller.cc
@@ -632,6 +632,10 @@
   MaybeUpdatePreviewWidget(/*animate=*/true);
 }
 
+void CaptureModeCameraController::OnCaptureSessionStarted() {
+  GetCameraDevices();
+}
+
 void CaptureModeCameraController::OnRecordingStarted(
     bool is_in_projector_mode) {
   // Check if there's a camera disconnection that happened before recording
diff --git a/ash/capture_mode/capture_mode_camera_controller.h b/ash/capture_mode/capture_mode_camera_controller.h
index a6687508..a6eab77 100644
--- a/ash/capture_mode/capture_mode_camera_controller.h
+++ b/ash/capture_mode/capture_mode_camera_controller.h
@@ -210,6 +210,12 @@
   // `is_camera_preview_collapsed_` when the resize button is pressed.
   void ToggleCameraPreviewSize();
 
+  // Called when a capture session gets started so we can refresh the cameras
+  // list, since the cros-camera service might have not been running when we
+  // tried to refresh the cameras at the beginning. (See
+  // http://b/230917107#comment12 for more details).
+  void OnCaptureSessionStarted();
+
   void OnRecordingStarted(bool is_in_projector_mode);
   void OnRecordingEnded();
 
diff --git a/ash/capture_mode/capture_mode_camera_unittests.cc b/ash/capture_mode/capture_mode_camera_unittests.cc
index 810a310..2c526f50 100644
--- a/ash/capture_mode/capture_mode_camera_unittests.cc
+++ b/ash/capture_mode/capture_mode_camera_unittests.cc
@@ -389,7 +389,6 @@
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
-  base::SystemMonitor system_monitor_;
   std::unique_ptr<aura::Window> window_;
 };
 
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 3f1fdbb..5014521 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -1632,6 +1632,9 @@
   capture_mode_session_ =
       std::make_unique<CaptureModeSession>(this, for_projector);
   capture_mode_session_->Initialize();
+
+  if (camera_controller_)
+    camera_controller_->OnCaptureSessionStarted();
 }
 
 void CaptureModeController::OnDlpRestrictionCheckedAtVideoEnd(
diff --git a/ash/capture_mode/capture_mode_menu_group.cc b/ash/capture_mode/capture_mode_menu_group.cc
index a0da31ce..fc685e8 100644
--- a/ash/capture_mode/capture_mode_menu_group.cc
+++ b/ash/capture_mode/capture_mode_menu_group.cc
@@ -372,6 +372,11 @@
 void CaptureModeMenuGroup::AppendHighlightableItems(
     std::vector<CaptureModeSessionFocusCycler::HighlightableView*>&
         highlightable_items) {
+  // The camera menu group can be hidden if there are no cameras connected. In
+  // this case no items in this group should be highlightable.
+  if (!GetVisible())
+    return;
+
   highlightable_items.push_back(menu_header_);
   for (auto* option : options_) {
     if (option->GetEnabled())
diff --git a/ash/components/hid_detection/hid_detection_manager.h b/ash/components/hid_detection/hid_detection_manager.h
index 9276186..6d413c7f 100644
--- a/ash/components/hid_detection/hid_detection_manager.h
+++ b/ash/components/hid_detection/hid_detection_manager.h
@@ -7,13 +7,43 @@
 
 #include "base/callback.h"
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace ash::hid_detection {
 
 // Manages detecting and automatically connecting to human interface devices.
 class HidDetectionManager {
  public:
-  // Represents the status of HIDs on the device.
+  // The connection state of an input.
+  enum class InputState {
+    // No device is connected.
+    kSearching,
+
+    // A device is connected via USB.
+    kConnectedViaUsb,
+
+    // A device is connected, but is not known to be USB (Bluetooth vs USB vs
+    // serial).
+    kConnected
+  };
+
+  // Info of an input on the device.
+  struct InputMetadata {
+    InputState state = InputState::kSearching;
+
+    // The name of the HID currently being interfaced with. Empty if |state| is
+    // kSearching (no HID is being interfaced with).
+    std::string detected_hid_name;
+  };
+
+  // Represents the status of inputs on the device.
   struct HidDetectionStatus {
+    // Pointer input info of the device.
+    InputMetadata pointer_metadata;
+
+    // Keyboard input info of the device.
+    InputMetadata keyboard_metadata;
+
     // Indicates the device has a touchscreen connected.
     bool touchscreen_detected;
   };
diff --git a/ash/components/hid_detection/hid_detection_manager_impl.cc b/ash/components/hid_detection/hid_detection_manager_impl.cc
index 9afefb5..6eb30ef 100644
--- a/ash/components/hid_detection/hid_detection_manager_impl.cc
+++ b/ash/components/hid_detection/hid_detection_manager_impl.cc
@@ -10,6 +10,8 @@
 
 namespace ash::hid_detection {
 namespace {
+using InputState = HidDetectionManager::InputState;
+
 // Global InputDeviceManagerBinder instance that can be overridden in tests.
 base::NoDestructor<HidDetectionManagerImpl::InputDeviceManagerBinder>
     g_input_device_manager_binder;
@@ -55,7 +57,9 @@
 HidDetectionManager::HidDetectionStatus
 HidDetectionManagerImpl::ComputeHidDetectionStatus() const {
   return HidDetectionManager::HidDetectionStatus{
-      /*touchscreen_detected=*/connected_touchscreen_id_.has_value()};
+      GetInputMetadata(connected_pointer_id_),
+      GetInputMetadata(connected_keyboard_id_),
+      connected_touchscreen_id_.has_value()};
 }
 
 void HidDetectionManagerImpl::InputDeviceAdded(
@@ -79,10 +83,20 @@
   bool was_connected_hid_disconnected_ = false;
 
   if (id == connected_touchscreen_id_) {
-    HID_LOG(EVENT) << "Removing touchscreen: " << id;
+    HID_LOG(EVENT) << "Removing connected touchscreen: " << id;
     connected_touchscreen_id_.reset();
     was_connected_hid_disconnected_ = true;
   }
+  if (id == connected_pointer_id_) {
+    HID_LOG(EVENT) << "Removing connected pointer: " << id;
+    connected_pointer_id_.reset();
+    was_connected_hid_disconnected_ = true;
+  }
+  if (id == connected_keyboard_id_) {
+    HID_LOG(EVENT) << "Removing connected keyboard: " << id;
+    connected_keyboard_id_.reset();
+    was_connected_hid_disconnected_ = true;
+  }
 
   if (was_connected_hid_disconnected_) {
     SetConnectedHids();
@@ -123,7 +137,7 @@
 
   HID_LOG(EVENT)
       << "Fetched " << devices.size()
-      << " input devices for GetIsHIdDetectionRequired(). Pointer detected: "
+      << " input devices for GetIsHidDetectionRequired(). Pointer detected: "
       << has_pointer << ", keyboard detected: " << has_keyboard;
 
   // HID detection is not required if both devices are present.
@@ -142,6 +156,7 @@
 }
 
 bool HidDetectionManagerImpl::SetConnectedHids() {
+  HID_LOG(EVENT) << "Setting connected HIDs";
   bool is_any_device_newly_connected_hid = false;
   for (const auto& [device_id, device] : device_id_to_device_map_) {
     is_any_device_newly_connected_hid |=
@@ -159,9 +174,46 @@
     connected_touchscreen_id_ = device.id;
     is_device_newly_connected_hid = true;
   }
+  if (!connected_pointer_id_.has_value() &&
+      hid_detection::IsDevicePointer(device)) {
+    HID_LOG(EVENT) << "Pointer detected: " << device.id;
+    connected_pointer_id_ = device.id;
+    is_device_newly_connected_hid = true;
+  }
+  if (!connected_keyboard_id_.has_value() && device.is_keyboard) {
+    HID_LOG(EVENT) << "Keyboard detected: " << device.id;
+    connected_keyboard_id_ = device.id;
+    is_device_newly_connected_hid = true;
+  }
 
-  // TODO(gordonseto): Handle keyboards/pointers.
   return is_device_newly_connected_hid;
 }
 
+HidDetectionManager::InputMetadata HidDetectionManagerImpl::GetInputMetadata(
+    const absl::optional<std::string>& device_id) const {
+  if (!device_id.has_value()) {
+    return InputMetadata();
+  }
+
+  const device::mojom::InputDeviceInfoPtr& device =
+      device_id_to_device_map_.find(device_id.value())->second;
+  DCHECK(device) << " |device_id| not found in |device_id_to_device_map_|";
+  InputState state;
+  switch (device->type) {
+    case device::mojom::InputDeviceType::TYPE_BLUETOOTH:
+      // TODO(gordonseto): Handle Bluetooth type.
+      state = InputState::kConnected;
+      break;
+    case device::mojom::InputDeviceType::TYPE_USB:
+      state = InputState::kConnectedViaUsb;
+      break;
+    case device::mojom::InputDeviceType::TYPE_SERIO:
+      [[fallthrough]];
+    case device::mojom::InputDeviceType::TYPE_UNKNOWN:
+      state = InputState::kConnected;
+      break;
+  }
+  return InputMetadata{state, device->name};
+}
+
 }  // namespace ash::hid_detection
diff --git a/ash/components/hid_detection/hid_detection_manager_impl.h b/ash/components/hid_detection/hid_detection_manager_impl.h
index 36bf362..d0a136e3 100644
--- a/ash/components/hid_detection/hid_detection_manager_impl.h
+++ b/ash/components/hid_detection/hid_detection_manager_impl.h
@@ -71,9 +71,17 @@
   bool AttemptSetDeviceAsConnectedHid(
       const device::mojom::InputDeviceInfo& device);
 
+  // Returns InputMetadata based on |device_id|. A null |device_id| means no HID
+  // for that input is connected. If |device_id| is not null, it must be a
+  // key for an entry in |device_id_to_device_map_|.
+  InputMetadata GetInputMetadata(
+      const absl::optional<std::string>& device_id) const;
+
   std::map<std::string, device::mojom::InputDeviceInfoPtr>
       device_id_to_device_map_;
   absl::optional<std::string> connected_touchscreen_id_;
+  absl::optional<std::string> connected_pointer_id_;
+  absl::optional<std::string> connected_keyboard_id_;
 
   device::mojom::DeviceService* device_service_ = nullptr;
   mojo::Remote<device::mojom::InputDeviceManager> input_device_manager_;
diff --git a/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc b/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc
index 5d45bfcd..600c596 100644
--- a/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc
+++ b/ash/components/hid_detection/hid_detection_manager_impl_unittest.cc
@@ -14,6 +14,8 @@
 namespace ash::hid_detection {
 namespace {
 
+using InputMetadata = HidDetectionManager::InputMetadata;
+using InputState = HidDetectionManager::InputState;
 using InputDeviceType = device::mojom::InputDeviceType;
 
 enum HidType {
@@ -21,6 +23,7 @@
   kTouchpad,
   kKeyboard,
   kTouchscreen,
+  kTablet,
 };
 
 class FakeHidDetectionManagerDelegate : public HidDetectionManager::Delegate {
@@ -107,26 +110,39 @@
   void AddDevice(HidType hid_type,
                  InputDeviceType device_type,
                  std::string* id_out = nullptr) {
+    AddDevice(std::vector{hid_type}, device_type, id_out);
+  }
+
+  void AddDevice(std::vector<HidType> hid_types,
+                 InputDeviceType device_type,
+                 std::string* id_out = nullptr) {
     auto device = device::mojom::InputDeviceInfo::New();
     device->id = num_devices_created_++;
     if (id_out)
       *id_out = device->id;
 
+    device->name = device->id;
     device->subsystem = device::mojom::InputDeviceSubsystem::SUBSYSTEM_INPUT;
     device->type = device_type;
-    switch (hid_type) {
-      case kMouse:
-        device->is_mouse = true;
-        break;
-      case kTouchpad:
-        device->is_touchpad = true;
-        break;
-      case kKeyboard:
-        device->is_keyboard = true;
-        break;
-      case kTouchscreen:
-        device->is_touchscreen = true;
-        break;
+
+    for (const auto& hid_type : hid_types) {
+      switch (hid_type) {
+        case kMouse:
+          device->is_mouse = true;
+          break;
+        case kTouchpad:
+          device->is_touchpad = true;
+          break;
+        case kKeyboard:
+          device->is_keyboard = true;
+          break;
+        case kTouchscreen:
+          device->is_touchscreen = true;
+          break;
+        case kTablet:
+          device->is_tablet = true;
+          break;
+      }
     }
     fake_input_service_.AddDevice(std::move(device));
     base::RunLoop().RunUntilIdle();
@@ -137,6 +153,21 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  void AssertHidDetectionStatus(InputMetadata pointer_metadata,
+                                InputMetadata keyboard_metadata,
+                                bool touchscreen_detected) {
+    EXPECT_EQ(pointer_metadata.state,
+              GetLastHidDetectionStatus()->pointer_metadata.state);
+    EXPECT_EQ(pointer_metadata.detected_hid_name,
+              GetLastHidDetectionStatus()->pointer_metadata.detected_hid_name);
+    EXPECT_EQ(keyboard_metadata.state,
+              GetLastHidDetectionStatus()->keyboard_metadata.state);
+    EXPECT_EQ(keyboard_metadata.detected_hid_name,
+              GetLastHidDetectionStatus()->keyboard_metadata.detected_hid_name);
+    EXPECT_EQ(touchscreen_detected,
+              GetLastHidDetectionStatus()->touchscreen_detected);
+  }
+
  private:
   base::test::TaskEnvironment task_environment_;
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -185,61 +216,236 @@
   StartHidDetection();
   EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
   ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
-  EXPECT_TRUE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/true);
 }
 
-TEST_F(HidDetectionManagerImplTest,
-       StartDetection_TouchscreenConnectedDisconnected) {
-  AddDevice(HidType::kTouchpad, InputDeviceType::TYPE_USB);
+TEST_F(HidDetectionManagerImplTest, StartDetection_PointerPreConnected) {
+  std::string device_id;
+  AddDevice(HidType::kMouse, InputDeviceType::TYPE_SERIO, &device_id);
   EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
 
   StartHidDetection();
   EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
   ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
-  EXPECT_FALSE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kConnected, device_id},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+}
 
-  // Add a non-touchscreen device. Touchscreen should not be detected.
-  std::string device_id1;
-  AddDevice(HidType::kKeyboard, InputDeviceType::TYPE_USB, &device_id1);
+TEST_F(HidDetectionManagerImplTest, StartDetection_KeyboardPreConnected) {
+  std::string device_id;
+  AddDevice(HidType::kKeyboard, InputDeviceType::TYPE_SERIO, &device_id);
+  EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
+
+  StartHidDetection();
   EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_FALSE(GetLastHidDetectionStatus()->touchscreen_detected);
+  ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/InputMetadata{InputState::kConnected, device_id},
+      /*touchscreen_detected=*/false);
+}
 
-  // Add touchscreen device. Touchscreen should be detected.
-  std::string device_id2;
-  AddDevice(HidType::kTouchscreen, InputDeviceType::TYPE_SERIO, &device_id2);
+TEST_F(HidDetectionManagerImplTest,
+       StartDetection_TouchscreenConnectedDisconnected) {
+  StartHidDetection();
+  EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
+  ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  std::string touchscreen_id1;
+  AddDevice(HidType::kTouchscreen, InputDeviceType::TYPE_SERIO,
+            &touchscreen_id1);
   EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_TRUE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/true);
 
-  // Remove the non-touchscreen device. Touchscreen should still be detected.
-  RemoveDevice(device_id1);
-  EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_TRUE(GetLastHidDetectionStatus()->touchscreen_detected);
-
-  // Remove the touchscreen device. Touchscreen should no longer be detected.
-  RemoveDevice(device_id2);
+  RemoveDevice(touchscreen_id1);
   EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_FALSE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
 
   StopHidDetection();
   EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_FALSE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
 
-  // Add another touchscreen device. Delegate should not be notified.
-  std::string device_id3;
-  AddDevice(HidType::kTouchscreen, InputDeviceType::TYPE_SERIO, &device_id3);
+  // Add another touchscreen device. This should not inform the delegate.
+  std::string touchscreen_id2;
+  AddDevice(HidType::kTouchscreen, InputDeviceType::TYPE_SERIO,
+            &touchscreen_id2);
   EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_FALSE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
 
-  // Remove the touchscreen device. Delegate should not be notified.
-  RemoveDevice(device_id3);
+  // Remove the touchscreen device. This should not inform the delegate.
+  RemoveDevice(touchscreen_id2);
   EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_FALSE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+}
+
+TEST_F(HidDetectionManagerImplTest,
+       StartDetection_PointerConnectedDisconnected) {
+  StartHidDetection();
+  EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
+  ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  std::string pointer_id1;
+  AddDevice(HidType::kMouse, InputDeviceType::TYPE_USB, &pointer_id1);
+  EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kConnectedViaUsb,
+                                         pointer_id1},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  RemoveDevice(pointer_id1);
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  StopHidDetection();
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  // Add another pointer device. This should not inform the delegate.
+  std::string pointer_id2;
+  AddDevice(HidType::kMouse, InputDeviceType::TYPE_USB, &pointer_id2);
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  // Remove the pointer device. This should not inform the delegate.
+  RemoveDevice(pointer_id2);
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+}
+
+TEST_F(HidDetectionManagerImplTest,
+       StartDetection_KeyboardConnectedDisconnected) {
+  StartHidDetection();
+  EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
+  ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  std::string keyboard_id1;
+  AddDevice(HidType::kKeyboard, InputDeviceType::TYPE_USB, &keyboard_id1);
+  EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kConnectedViaUsb, keyboard_id1},
+      /*touchscreen_detected=*/false);
+
+  RemoveDevice(keyboard_id1);
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  StopHidDetection();
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  // Add another keyboard device. This should not inform the delegate.
+  std::string keyboard_id2;
+  AddDevice(HidType::kMouse, InputDeviceType::TYPE_USB, &keyboard_id2);
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  // Remove the keyboard device. This should not inform the delegate.
+  RemoveDevice(keyboard_id2);
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
 }
 
 TEST_F(HidDetectionManagerImplTest,
        StartDetection_MultipleTouchscreensDisconnected) {
   std::string device_id1;
-  AddDevice(HidType::kTouchscreen, InputDeviceType::TYPE_SERIO, &device_id1);
+  AddDevice(HidType::kTablet, InputDeviceType::TYPE_SERIO, &device_id1);
   std::string device_id2;
   AddDevice(HidType::kTouchscreen, InputDeviceType::TYPE_SERIO, &device_id2);
   EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
@@ -247,13 +453,115 @@
   StartHidDetection();
   EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
   ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
-  EXPECT_TRUE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/true);
 
   // Remove the first touchscreen device. The second touchscreen should be
   // detected and delegate notified.
   RemoveDevice(device_id1);
   EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
-  EXPECT_TRUE(GetLastHidDetectionStatus()->touchscreen_detected);
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/true);
+}
+
+TEST_F(HidDetectionManagerImplTest,
+       StartDetection_MultiplePointersDisconnected) {
+  std::string device_id1;
+  AddDevice(HidType::kTouchpad, InputDeviceType::TYPE_UNKNOWN, &device_id1);
+  std::string device_id2;
+  AddDevice(HidType::kMouse, InputDeviceType::TYPE_SERIO, &device_id2);
+  EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
+
+  StartHidDetection();
+  EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
+  ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kConnected, device_id1},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+
+  // Remove the first pointer. The second pointer should be detected and
+  // delegate notified.
+  RemoveDevice(device_id1);
+  EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kConnected, device_id2},
+      /*keyboard_metadata=*/
+      InputMetadata{InputState::kSearching, /*detected_hid_name=*/""},
+      /*touchscreen_detected=*/false);
+}
+
+TEST_F(HidDetectionManagerImplTest,
+       StartDetection_MultipleKeyboardsDisconnected) {
+  std::string device_id1;
+  AddDevice(HidType::kKeyboard, InputDeviceType::TYPE_UNKNOWN, &device_id1);
+  std::string device_id2;
+  AddDevice(HidType::kKeyboard, InputDeviceType::TYPE_SERIO, &device_id2);
+  EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
+
+  StartHidDetection();
+  EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
+  ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/InputMetadata{InputState::kConnected, device_id1},
+      /*touchscreen_detected=*/false);
+
+  // Remove the first keyboard. The second keyboard should be detected and
+  // delegate notified.
+  RemoveDevice(device_id1);
+  EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/InputMetadata{InputState::kConnected, device_id2},
+      /*touchscreen_detected=*/false);
+}
+
+TEST_F(HidDetectionManagerImplTest,
+       StartDetection_DeviceMultipleHidTypesDisconnected) {
+  std::string device_id1;
+  AddDevice(HidType::kTouchpad, InputDeviceType::TYPE_USB, &device_id1);
+  std::string device_id2;
+  std::vector<HidType> hid_types{HidType::kKeyboard, HidType::kTouchpad};
+  AddDevice(hid_types, InputDeviceType::TYPE_SERIO, &device_id2);
+  std::string device_id3;
+  AddDevice(HidType::kKeyboard, InputDeviceType::TYPE_UNKNOWN, &device_id3);
+  EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
+
+  StartHidDetection();
+  EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
+  ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kConnectedViaUsb,
+                                         device_id1},
+      /*keyboard_metadata=*/InputMetadata{InputState::kConnected, device_id2},
+      /*touchscreen_detected=*/false);
+
+  RemoveDevice(device_id1);
+  EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kConnected, device_id2},
+      /*keyboard_metadata=*/InputMetadata{InputState::kConnected, device_id2},
+      /*touchscreen_detected=*/false);
+
+  RemoveDevice(device_id2);
+  EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
+  AssertHidDetectionStatus(
+      /*pointer_metadata=*/InputMetadata{InputState::kSearching,
+                                         /*detected_hid_name=*/""},
+      /*keyboard_metadata=*/InputMetadata{InputState::kConnected, device_id3},
+      /*touchscreen_detected=*/false);
 }
 
 }  // namespace ash::hid_detection
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index cdc00d80..56a747a16 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -267,7 +267,7 @@
 
 // Controls whether the selfie camera feature is enabled for Capture Mode.
 const base::Feature kCaptureModeSelfieCamera{"CaptureModeSelfieCamera",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 // If enabled, allow eSIM installation bypass the non-cellular internet
 // connectivity check.
diff --git a/ash/style/dark_light_mode_nudge_controller.cc b/ash/style/dark_light_mode_nudge_controller.cc
index 93ef71f..24cb4c8 100644
--- a/ash/style/dark_light_mode_nudge_controller.cc
+++ b/ash/style/dark_light_mode_nudge_controller.cc
@@ -60,6 +60,10 @@
   if (!chromeos::features::IsDarkLightModeEnabled())
     return false;
 
+  // Do not show the nudge if it is set to be hidden in the tests.
+  if (hide_nudge_for_testing_)
+    return false;
+
   auto* session_controller = Shell::Get()->session_controller();
   if (!session_controller->IsActiveUserSessionStarted())
     return false;
diff --git a/ash/style/dark_light_mode_nudge_controller.h b/ash/style/dark_light_mode_nudge_controller.h
index de559b0..58691ba 100644
--- a/ash/style/dark_light_mode_nudge_controller.h
+++ b/ash/style/dark_light_mode_nudge_controller.h
@@ -33,6 +33,10 @@
   // Called when the feature's state is toggled manually by the user.
   void ToggledByUser();
 
+  void set_show_nudge_for_testing(bool value) {
+    hide_nudge_for_testing_ = !value;
+  }
+
  protected:
   // SystemNudgeController:
   std::unique_ptr<SystemNudge> CreateSystemNudge() override;
@@ -40,6 +44,11 @@
  private:
   // Returns true if the educational nudge should be shown.
   bool ShouldShowNudge() const;
+
+  // Used to indicate whether to hide the nudge in tests. Will be initialized to
+  // hide for ash tests through `set_show_nudge_for_testing` above. See
+  // AshTestHelper::SetUp for more details.
+  bool hide_nudge_for_testing_ = false;
 };
 
 }  // namespace ash
diff --git a/ash/style/dark_light_mode_nudge_controller_unittests.cc b/ash/style/dark_light_mode_nudge_controller_unittests.cc
index c8fafd4..8ed5fa3 100644
--- a/ash/style/dark_light_mode_nudge_controller_unittests.cc
+++ b/ash/style/dark_light_mode_nudge_controller_unittests.cc
@@ -5,6 +5,8 @@
 #include "ash/style/dark_light_mode_nudge_controller.h"
 
 #include "ash/constants/ash_constants.h"
+#include "ash/shell.h"
+#include "ash/style/dark_mode_controller.h"
 #include "ash/system/dark_mode/dark_mode_feature_pod_controller.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
@@ -33,6 +35,12 @@
       const DarkLightModeNudgeControllerTest&) = delete;
   ~DarkLightModeNudgeControllerTest() override = default;
 
+  // NoSessionAshTestBase:
+  void SetUp() override {
+    NoSessionAshTestBase::SetUp();
+    Shell::Get()->dark_mode_controller()->SetShowNudgeForTesting(true);
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/ash/style/dark_mode_controller.cc b/ash/style/dark_mode_controller.cc
index cb8450a..bce79ec 100644
--- a/ash/style/dark_mode_controller.cc
+++ b/ash/style/dark_mode_controller.cc
@@ -66,6 +66,10 @@
   nudge_controller_->ToggledByUser();
 }
 
+void DarkModeController::SetShowNudgeForTesting(bool value) {
+  nudge_controller_->set_show_nudge_for_testing(value);  // IN-TEST
+}
+
 void DarkModeController::RefreshFeatureState() {}
 
 void DarkModeController::OnSessionStateChanged(
diff --git a/ash/style/dark_mode_controller.h b/ash/style/dark_mode_controller.h
index e516794..0da4fe09 100644
--- a/ash/style/dark_mode_controller.h
+++ b/ash/style/dark_mode_controller.h
@@ -43,6 +43,8 @@
   // color mode. Educational nudge will not be shown any more when this happens.
   void ToggledByUser();
 
+  void SetShowNudgeForTesting(bool value);
+
  protected:
   // ScheduledFeature:
   void RefreshFeatureState() override;
diff --git a/ash/system/message_center/notification_grouping_controller.cc b/ash/system/message_center/notification_grouping_controller.cc
index de61dca..e1a64c3 100644
--- a/ash/system/message_center/notification_grouping_controller.cc
+++ b/ash/system/message_center/notification_grouping_controller.cc
@@ -357,13 +357,16 @@
 void NotificationGroupingController::OnNotificationRemoved(
     const std::string& notification_id,
     bool by_user) {
+  auto* message_center = MessageCenter::Get();
   if (grouped_notification_list_->GroupedChildNotificationExists(
           notification_id)) {
     const std::string parent_id =
         grouped_notification_list_->GetParentForChild(notification_id);
 
     RemoveGroupedChild(notification_id);
-    MessageCenter::Get()->ResetPopupTimer(parent_id);
+
+    if (message_center->FindPopupNotificationById(parent_id))
+      message_center->ResetPopupTimer(parent_id);
 
     metrics_utils::LogCountOfNotificationsInOneGroup(
         grouped_notification_list_->GetGroupedNotificationsForParent(parent_id)
@@ -380,7 +383,7 @@
     grouped_notification_list_->ClearGroupedNotification(notification_id);
 
     for (const auto& id : to_be_deleted)
-      MessageCenter::Get()->RemoveNotification(id, by_user);
+      message_center->RemoveNotification(id, by_user);
   }
 }
 
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index 4287de51..69272766 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -24,6 +24,7 @@
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
 #include "ash/shell_init_params.h"
+#include "ash/style/dark_mode_controller.h"
 #include "ash/system/message_center/session_state_notification_blocker.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/screen_layout_observer.h"
@@ -36,6 +37,7 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/run_loop.h"
 #include "base/system/sys_info.h"
+#include "base/system/system_monitor.h"
 #include "chromeos/ash/components/dbus/rgbkbd/rgbkbd_client.h"
 #include "chromeos/dbus/audio/cras_audio_client.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
@@ -91,7 +93,8 @@
 };
 
 AshTestHelper::AshTestHelper(ui::ContextFactory* context_factory)
-    : AuraTestHelper(context_factory) {
+    : AuraTestHelper(context_factory),
+      system_monitor_(std::make_unique<base::SystemMonitor>()) {
   views::ViewsTestHelperAura::SetFallbackTestViewsDelegateFactory(
       &MakeTestViewsDelegate);
 
@@ -278,6 +281,15 @@
   Shell::CreateInstance(std::move(shell_init_params));
   Shell* shell = Shell::Get();
 
+  // The dark/light mode educational nudge is expected to be shown when session
+  // state changed to ACTIVE. This means it might be shown above the shelf in
+  // all the tests with an active user session. This setting here make it will
+  // not be shown by default in tests. As keep it shown will change the
+  // operations needed in many of the tests, e.g, when productive launcher is
+  // shown as well, we need one more click outside of the launcher to dismiss
+  // the nudge first before dismissing the launcher.
+  shell->dark_mode_controller()->SetShowNudgeForTesting(false);
+
   // Set up a test wallpaper controller client before signing in any users. At
   // the time a user logs in, Wallpaper controller relies on
   // WallpaperControllerClient to check if user data should be synced.
@@ -339,7 +351,7 @@
   // Move the mouse cursor to far away so that native events don't interfere
   // with test expectations.
   Shell::GetPrimaryRootWindow()->MoveCursorTo(gfx::Point(-1000, -1000));
-  Shell::Get()->cursor_manager()->EnableMouseEvents();
+  shell->cursor_manager()->EnableMouseEvents();
 
   // Changing GestureConfiguration shouldn't make tests fail. These values
   // prevent unexpected events from being generated during tests. Such as
diff --git a/ash/test/ash_test_helper.h b/ash/test/ash_test_helper.h
index 9579189..081cecd 100644
--- a/ash/test/ash_test_helper.h
+++ b/ash/test/ash_test_helper.h
@@ -25,19 +25,23 @@
 
 namespace aura {
 class Window;
-}
+}  // namespace aura
+
+namespace base {
+class SystemMonitor;
+}  // namespace base
 
 namespace display {
 class Display;
-}
+}  // namespace display
 
 namespace ui {
 class ContextFactory;
-}
+}  // namespace ui
 
 namespace views {
 class TestViewsDelegate;
-}
+}  // namespace views
 
 namespace ash {
 
@@ -49,7 +53,7 @@
 
 namespace input_method {
 class MockInputMethodManager;
-}
+}  // namespace input_method
 
 // A helper class that does common initialization required for Ash. Creates a
 // root window and an ash::Shell instance with a test delegate.
@@ -141,6 +145,10 @@
   class BluezDBusManagerInitializer;
   class PowerPolicyControllerInitializer;
 
+  // Must be constructed so that `base::SystemMonitor::Get()` returns a valid
+  // instance.
+  std::unique_ptr<base::SystemMonitor> system_monitor_;
+
   std::unique_ptr<base::test::ScopedCommandLine> command_line_ =
       std::make_unique<base::test::ScopedCommandLine>();
   std::unique_ptr<chromeos::system::ScopedFakeStatisticsProvider>
diff --git a/ash/webui/os_feedback_ui/resources/BUILD.gn b/ash/webui/os_feedback_ui/resources/BUILD.gn
index 028628e1..01ab727 100644
--- a/ash/webui/os_feedback_ui/resources/BUILD.gn
+++ b/ash/webui/os_feedback_ui/resources/BUILD.gn
@@ -18,6 +18,7 @@
 polymer_element_files = [
   "confirmation_page.js",
   "feedback_flow.js",
+  "file_attachment.js",
   "help_content.js",
   "help_resources_icons.js",
   "os_feedback_shared_css.js",
@@ -115,6 +116,12 @@
   ]
 }
 
+js_library("file_attachment") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
 js_library("help_resources_icons") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
@@ -145,6 +152,7 @@
   deps = [
     ":feedback_flow",
     ":feedback_types",
+    ":file_attachment",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
diff --git a/ash/webui/os_feedback_ui/resources/file_attachment.html b/ash/webui/os_feedback_ui/resources/file_attachment.html
new file mode 100644
index 0000000..ba0b37b
--- /dev/null
+++ b/ash/webui/os_feedback_ui/resources/file_attachment.html
@@ -0,0 +1,21 @@
+<style>
+  .button-content {
+    align-items: center;
+    background-color: white;
+    border: none;
+    box-sizing: border-box;
+    cursor: pointer;
+    display: flex;
+    height: 48px;
+    padding-inline-end: 10px;
+    padding-inline-start: 12px;
+  }
+</style>
+<div>
+  <button class="button-content" title="Add file">
+    <span>
+      <iron-icon icon="attachment:add-file"></iron-icon>
+    </span>
+    <span id="AddFileLabel">Add file</span>
+  </button>
+</div>
diff --git a/ash/webui/os_feedback_ui/resources/file_attachment.js b/ash/webui/os_feedback_ui/resources/file_attachment.js
new file mode 100644
index 0000000..56c428f
--- /dev/null
+++ b/ash/webui/os_feedback_ui/resources/file_attachment.js
@@ -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.
+
+import './help_resources_icons.js';
+import './os_feedback_shared_css.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+/**
+ * @fileoverview
+ * 'file-attachment' allows users to select a file as an attachment to the
+ *  report.
+ */
+export class FileAttachmentElement extends PolymerElement {
+  static get is() {
+    return 'file-attachment';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+}
+
+customElements.define(FileAttachmentElement.is, FileAttachmentElement);
diff --git a/ash/webui/os_feedback_ui/resources/help_resources_icons.html b/ash/webui/os_feedback_ui/resources/help_resources_icons.html
index 48b3cbb..d5eef59 100644
--- a/ash/webui/os_feedback_ui/resources/help_resources_icons.html
+++ b/ash/webui/os_feedback_ui/resources/help_resources_icons.html
@@ -313,3 +313,16 @@
     </defs>
   </svg>
 </iron-iconset-svg>
+
+<iron-iconset-svg name="attachment" size="21">
+  <svg width="21" height="21" viewBox="0 0 21 21" fill="none"
+      xmlns="http://www.w3.org/2000/svg">
+    <defs>
+      <g id="add-file">
+        <path fill-rule="evenodd" clip-rule="evenodd"
+            d="M14.6172 9.1377V11.1377H11.6172V14.1377H9.61719V11.1377H6.61719V9.1377H9.61719V6.1377H11.6172V9.1377H14.6172ZM10.6172 2.1377C6.19319 2.1377 2.61719 5.7137 2.61719 10.1377C2.61719 14.5617 6.19319 18.1377 10.6172 18.1377C15.0412 18.1377 18.6172 14.5617 18.6172 10.1377C18.6172 5.7137 15.0412 2.1377 10.6172 2.1377ZM10.6172 16.1377C7.30969 16.1377 4.61719 13.4452 4.61719 10.1377C4.61719 6.8302 7.30969 4.1377 10.6172 4.1377C13.9247 4.1377 16.6172 6.8302 16.6172 10.1377C16.6172 13.4452 13.9247 16.1377 10.6172 16.1377Z"
+            fill="#1A73E8">
+      </g>
+    </defs>
+  </svg>
+</iron-iconset-svg>
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.html b/ash/webui/os_feedback_ui/resources/share_data_page.html
index 918c156b..6e94ed2 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.html
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -50,7 +50,7 @@
     z-index: 1;
   }
 
-  #addFile {
+  #addFileContainer {
     align-items: center;
     height: 48px;
     margin-inline-start: 12px;
@@ -87,8 +87,9 @@
               src="[[screenshotUrl]]">
         </div>
         <!-- Attach a file -->
-        <!-- TODO(xiangdongkong): Create a polymer element.-->
-        <div id="addFile" class="card-frame">Add file</div>
+        <div id="addFileContainer" class="card-frame">
+          <file-attachment></file-attachment>
+        </div>
       </div>
     </div>
     <!-- User e-mail -->
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.js b/ash/webui/os_feedback_ui/resources/share_data_page.js
index 45b735b..4d50e16 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import './os_feedback_shared_css.js';
+import './file_attachment.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/base/BUILD.gn b/base/BUILD.gn
index be46e9c6..e5a179d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -649,8 +649,8 @@
     "strings/utf_string_conversion_utils.h",
     "strings/utf_string_conversions.cc",
     "strings/utf_string_conversions.h",
-    "substring_set_matcher/string_pattern.cc",
-    "substring_set_matcher/string_pattern.h",
+    "substring_set_matcher/matcher_string_pattern.cc",
+    "substring_set_matcher/matcher_string_pattern.h",
     "substring_set_matcher/substring_set_matcher.cc",
     "substring_set_matcher/substring_set_matcher.h",
     "supports_user_data.cc",
@@ -1019,11 +1019,11 @@
 
   if (is_win) {
     sources += [
-      "debug/close_handle_hook_win.cc",
-      "debug/close_handle_hook_win.h",
       "debug/debugger_win.cc",
       "debug/gdi_debug_util_win.cc",
       "debug/gdi_debug_util_win.h",
+      "debug/handle_hooks_win.cc",
+      "debug/handle_hooks_win.h",
       "debug/invalid_access_win.cc",
       "debug/invalid_access_win.h",
       "debug/stack_trace_win.cc",
diff --git a/base/allocator/partition_allocator/BUILD.gn b/base/allocator/partition_allocator/BUILD.gn
index 0428a71..10a140d 100644
--- a/base/allocator/partition_allocator/BUILD.gn
+++ b/base/allocator/partition_allocator/BUILD.gn
@@ -82,6 +82,7 @@
     "partition_alloc_base/debug/alias.cc",
     "partition_alloc_base/debug/alias.h",
     "partition_alloc_base/gtest_prod_util.h",
+    "partition_alloc_base/immediate_crash.h",
     "partition_alloc_base/logging.cc",
     "partition_alloc_base/logging.h",
     "partition_alloc_base/memory/ref_counted.cc",
diff --git a/base/allocator/partition_allocator/DEPS b/base/allocator/partition_allocator/DEPS
index 7468b7c..eeb8b8d 100644
--- a/base/allocator/partition_allocator/DEPS
+++ b/base/allocator/partition_allocator/DEPS
@@ -10,7 +10,6 @@
     "+base/check_op.h",
     "+base/compiler_specific.h",
     "+base/dcheck_is_on.h",
-    "+base/immediate_crash.h",
     "+base/logging_buildflags.h",
     "+base/mac/foundation_util.h",
     "+base/mac/mac_util.h",
diff --git a/base/allocator/partition_allocator/address_space_randomization.h b/base/allocator/partition_allocator/address_space_randomization.h
index a16506e..59161ae0 100644
--- a/base/allocator/partition_allocator/address_space_randomization.h
+++ b/base/allocator/partition_allocator/address_space_randomization.h
@@ -8,8 +8,8 @@
 #include <cstdint>
 
 #include "base/allocator/partition_allocator/page_allocator_constants.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/base_export.h"
-#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 namespace partition_alloc {
@@ -20,11 +20,11 @@
 
 namespace internal {
 
-PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
 AslrAddress(uintptr_t mask) {
   return mask & PageAllocationGranularityBaseMask();
 }
-PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
 AslrMask(uintptr_t bits) {
   return AslrAddress((1ULL << bits) - 1ULL);
 }
@@ -45,11 +45,11 @@
     // hard-coded in those tools, bad things happen. This address range is
     // copied from TSAN source but works with all tools. See
     // https://crbug.com/539863.
-    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
     ASLRMask() {
       return AslrAddress(0x007fffffffffULL);
     }
-    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
     ASLROffset() {
       return AslrAddress(0x7e8000000000ULL);
     }
@@ -60,14 +60,14 @@
     // versions of Windows only support 44 bits. Since ASLROffset() is non-zero
     // and may cause a carry, use 47 and 43 bit masks. See
     // http://www.alex-ionescu.com/?p=246
-    constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+    constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
       return AslrMask(47);
     }
-    constexpr ALWAYS_INLINE uintptr_t ASLRMaskBefore8_10() {
+    constexpr PA_ALWAYS_INLINE uintptr_t ASLRMaskBefore8_10() {
       return AslrMask(43);
     }
     // Try not to map pages into the range where Windows loads DLLs by default.
-    constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+    constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
       return 0x80000000ULL;
     }
 
@@ -86,11 +86,11 @@
     //
     // TODO(crbug.com/738925): Remove this limitation if/when the macOS behavior
     // changes.
-    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
     ASLRMask() {
       return AslrMask(38);
     }
-    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+    PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
     ASLROffset() {
       // Be careful, there is a zone where macOS will not map memory, at least
       // on ARM64. From an ARM64 machine running 12.3, the range seems to be
@@ -108,10 +108,10 @@
 
       // Linux (and macOS) support the full 47-bit user space of x64 processors.
       // Use only 46 to allow the kernel a chance to fulfill the request.
-      constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
         return AslrMask(46);
       }
-      constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
         return AslrAddress(0);
       }
 
@@ -121,10 +121,10 @@
 
       // Restrict the address range on Android to avoid a large performance
       // regression in single-process WebViews. See https://crbug.com/837640.
-      constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
         return AslrMask(30);
       }
-      constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
         return AslrAddress(0x20000000ULL);
       }
 
@@ -134,11 +134,11 @@
       // page size and number of levels of translation pages used. We use
       // 39-bit as base as all setups should support this, lowered to 38-bit
       // as ASLROffset() could cause a carry.
-      PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+      PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
       ASLRMask() {
         return AslrMask(38);
       }
-      PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+      PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR PA_ALWAYS_INLINE uintptr_t
       ASLROffset() {
         return AslrAddress(0x1000000000ULL);
       }
@@ -147,10 +147,10 @@
 
       // ARM64 on Linux has 39-bit user space. Use 38 bits since ASLROffset()
       // could cause a carry.
-      constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
         return AslrMask(38);
       }
-      constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
         return AslrAddress(0x1000000000ULL);
       }
 
@@ -163,30 +163,30 @@
         // AIX has 64 bits of virtual addressing, but we limit the address range
         // to (a) minimize segment lookaside buffer (SLB) misses; and (b) use
         // extra address space to isolate the mmap regions.
-        constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
           return AslrMask(30);
         }
-        constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
           return AslrAddress(0x400000000000ULL);
         }
 
       #elif defined(ARCH_CPU_BIG_ENDIAN)
 
         // Big-endian Linux PPC has 44 bits of virtual addressing. Use 42.
-        constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
           return AslrMask(42);
         }
-        constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
           return AslrAddress(0);
         }
 
       #else  // !BUILDFLAG(IS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN)
 
         // Little-endian Linux PPC has 48 bits of virtual addressing. Use 46.
-        constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
           return AslrMask(46);
         }
-        constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
           return AslrAddress(0);
         }
 
@@ -197,10 +197,10 @@
       // Linux on Z uses bits 22 - 32 for Region Indexing, which translates to
       // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel a
       // chance to fulfill the request.
-      constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
         return AslrMask(40);
       }
-      constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
         return AslrAddress(0);
       }
 
@@ -208,10 +208,10 @@
 
       // 31 bits of virtual addressing. Truncate to 29 bits to allow the kernel
       // a chance to fulfill the request.
-      constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
         return AslrMask(29);
       }
-      constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
         return AslrAddress(0);
       }
 
@@ -219,7 +219,7 @@
            // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390)
 
       // For all other POSIX variants, use 30 bits.
-      constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+      constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
         return AslrMask(30);
       }
 
@@ -235,7 +235,7 @@
         // fails allocate as if there were no hint at all. The high hint
         // prevents the break from getting hemmed in at low values, ceding half
         // of the address space to the system heap.
-        constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
           return AslrAddress(0x80000000ULL);
         }
 
@@ -243,7 +243,7 @@
 
         // The range 0x30000000 - 0xD0000000 is available on AIX; choose the
         // upper range.
-        constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
           return AslrAddress(0x90000000ULL);
         }
 
@@ -252,7 +252,7 @@
         // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
         // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macOS
         // 10.6 and 10.7.
-        constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+        constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
           return AslrAddress(0x20000000ULL);
         }
 
@@ -268,10 +268,10 @@
   // This is a good range on 32-bit Windows and Android (the only platforms on
   // which we support 32-bitness). Allocates in the 0.5 - 1.5 GiB region. There
   // is no issue with carries here.
-  constexpr ALWAYS_INLINE uintptr_t ASLRMask() {
+  constexpr PA_ALWAYS_INLINE uintptr_t ASLRMask() {
     return AslrMask(30);
   }
-  constexpr ALWAYS_INLINE uintptr_t ASLROffset() {
+  constexpr PA_ALWAYS_INLINE uintptr_t ASLROffset() {
     return AslrAddress(0x20000000ULL);
   }
 
diff --git a/base/allocator/partition_allocator/allocation_guard.cc b/base/allocator/partition_allocator/allocation_guard.cc
index 0d56a20..1d5638a 100644
--- a/base/allocator/partition_allocator/allocation_guard.cc
+++ b/base/allocator/partition_allocator/allocation_guard.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "base/allocator/partition_allocator/allocation_guard.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
-#include "base/immediate_crash.h"
 
 #if defined(PA_HAS_ALLOCATION_GUARD)
 
@@ -16,7 +16,7 @@
 
 ScopedDisallowAllocations::ScopedDisallowAllocations() {
   if (g_disallow_allocations)
-    IMMEDIATE_CRASH();
+    PA_IMMEDIATE_CRASH();
 
   g_disallow_allocations = true;
 }
diff --git a/base/allocator/partition_allocator/oom.cc b/base/allocator/partition_allocator/oom.cc
index e83e225b..d9e585f 100644
--- a/base/allocator/partition_allocator/oom.cc
+++ b/base/allocator/partition_allocator/oom.cc
@@ -5,8 +5,8 @@
 #include "base/allocator/partition_allocator/oom.h"
 
 #include "base/allocator/partition_allocator/oom_callback.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/compiler_specific.h"
-#include "base/immediate_crash.h"
 #include "base/process/memory.h"
 
 namespace partition_alloc::internal {
@@ -17,7 +17,7 @@
 [[noreturn]] NOINLINE void NOT_TAIL_CALLED OnNoMemory(size_t size) {
   RunPartitionAllocOomCallback();
   base::TerminateBecauseOutOfMemory(size);
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 }
 
 }  // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h b/base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h
new file mode 100644
index 0000000..e569414
--- /dev/null
+++ b/base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h
@@ -0,0 +1,168 @@
+// 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.
+
+#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_
+#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_
+
+#include "build/build_config.h"
+
+// Crashes in the fastest possible way with no attempt at logging.
+// There are several constraints; see http://crbug.com/664209 for more context.
+//
+// - PA_TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
+//   resulting exception or simply hit 'continue' to skip over it in a debugger.
+// - Different instances of PA_TRAP_SEQUENCE_() must not be folded together, to
+//   ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
+//   blocks will not be folded together.
+//   Note: PA_TRAP_SEQUENCE_() previously required an instruction with a unique
+//   nonce since unlike clang, GCC folds together identical asm volatile
+//   blocks.
+// - PA_TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
+//   memory access.
+// - PA_TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
+//   __builtin_unreachable() is used to provide that hint here. clang also uses
+//   this as a heuristic to pack the instructions in the function epilogue to
+//   improve code density.
+//
+// Additional properties that are nice to have:
+// - PA_TRAP_SEQUENCE_() should be as compact as possible.
+// - The first instruction of PA_TRAP_SEQUENCE_() should not change, to avoid
+//   shifting crash reporting clusters. As a consequence of this, explicit
+//   assembly is preferred over intrinsics.
+//   Note: this last bullet point may no longer be true, and may be removed in
+//   the future.
+
+// Note: PA_TRAP_SEQUENCE Is currently split into two macro helpers due to the
+// fact that clang emits an actual instruction for __builtin_unreachable() on
+// certain platforms (see https://crbug.com/958675). In addition, the
+// int3/bkpt/brk will be removed in followups, so splitting it up like this now
+// makes it easy to land the followups.
+
+#if defined(COMPILER_GCC)
+
+#if BUILDFLAG(IS_NACL)
+
+// Crash report accuracy is not guaranteed on NaCl.
+#define PA_TRAP_SEQUENCE1_() __builtin_trap()
+#define PA_TRAP_SEQUENCE2_() asm volatile("")
+
+#elif defined(ARCH_CPU_X86_FAMILY)
+
+// TODO(https://crbug.com/958675): In theory, it should be possible to use just
+// int3. However, there are a number of crashes with SIGILL as the exception
+// code, so it seems likely that there's a signal handler that allows execution
+// to continue after SIGTRAP.
+#define PA_TRAP_SEQUENCE1_() asm volatile("int3")
+
+#if BUILDFLAG(IS_APPLE)
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see PA_IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
+#define PA_TRAP_SEQUENCE2_() asm volatile("")
+#else
+#define PA_TRAP_SEQUENCE2_() asm volatile("ud2")
+#endif  // BUILDFLAG(IS_APPLE)
+
+#elif defined(ARCH_CPU_ARMEL)
+
+// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
+// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
+// cause a SIGTRAP from userspace without using a syscall (which would be a
+// problem for sandboxing).
+// TODO(https://crbug.com/958675): Remove bkpt from this sequence.
+#define PA_TRAP_SEQUENCE1_() asm volatile("bkpt #0")
+#define PA_TRAP_SEQUENCE2_() asm volatile("udf #0")
+
+#elif defined(ARCH_CPU_ARM64)
+
+// This will always generate a SIGTRAP on arm64.
+// TODO(https://crbug.com/958675): Remove brk from this sequence.
+#define PA_TRAP_SEQUENCE1_() asm volatile("brk #0")
+#define PA_TRAP_SEQUENCE2_() asm volatile("hlt #0")
+
+#else
+
+// Crash report accuracy will not be guaranteed on other architectures, but at
+// least this will crash as expected.
+#define PA_TRAP_SEQUENCE1_() __builtin_trap()
+#define PA_TRAP_SEQUENCE2_() asm volatile("")
+
+#endif  // ARCH_CPU_*
+
+#elif defined(COMPILER_MSVC)
+
+#if !defined(__clang__)
+
+// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
+#define PA_TRAP_SEQUENCE1_() __debugbreak()
+#define PA_TRAP_SEQUENCE2_()
+
+#elif defined(ARCH_CPU_ARM64)
+
+// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
+// __debugbreak() generates that in both VC++ and clang.
+#define PA_TRAP_SEQUENCE1_() __debugbreak()
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see PA_IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
+// https://crbug.com/958373
+#define PA_TRAP_SEQUENCE2_() __asm volatile("")
+
+#else
+
+#define PA_TRAP_SEQUENCE1_() asm volatile("int3")
+#define PA_TRAP_SEQUENCE2_() asm volatile("ud2")
+
+#endif  // __clang__
+
+#else
+
+#error No supported trap sequence!
+
+#endif  // COMPILER_GCC
+
+#define PA_TRAP_SEQUENCE_() \
+  do {                      \
+    PA_TRAP_SEQUENCE1_();   \
+    PA_TRAP_SEQUENCE2_();   \
+  } while (false)
+
+// CHECK() and the trap sequence can be invoked from a constexpr function.
+// This could make compilation fail on GCC, as it forbids directly using inline
+// asm inside a constexpr function. However, it allows calling a lambda
+// expression including the same asm.
+// The side effect is that the top of the stacktrace will not point to the
+// calling function, but to this anonymous lambda. This is still useful as the
+// full name of the lambda will typically include the name of the function that
+// calls CHECK() and the debugger will still break at the right line of code.
+#if !defined(COMPILER_GCC) || defined(__clang__)
+
+#define PA_WRAPPED_TRAP_SEQUENCE_() PA_TRAP_SEQUENCE_()
+
+#else
+
+#define PA_WRAPPED_TRAP_SEQUENCE_() \
+  do {                              \
+    [] { PA_TRAP_SEQUENCE_(); }();  \
+  } while (false)
+
+#endif  // !defined(COMPILER_GCC) || defined(__clang__)
+
+#if defined(__clang__) || defined(COMPILER_GCC)
+
+// __builtin_unreachable() hints to the compiler that this is noreturn and can
+// be packed in the function epilogue.
+#define PA_IMMEDIATE_CRASH()     \
+  ({                             \
+    PA_WRAPPED_TRAP_SEQUENCE_(); \
+    __builtin_unreachable();     \
+  })
+
+#else
+
+// This is supporting non-chromium user of logging.h to build with MSVC, like
+// pdfium. On MSVC there is no __builtin_unreachable().
+#define PA_IMMEDIATE_CRASH() PA_WRAPPED_TRAP_SEQUENCE_()
+
+#endif  // defined(__clang__) || defined(COMPILER_GCC)
+
+#endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/logging.cc b/base/allocator/partition_allocator/partition_alloc_base/logging.cc
index 11ba1e69..b4e73086 100644
--- a/base/allocator/partition_allocator/partition_alloc_base/logging.cc
+++ b/base/allocator/partition_allocator/partition_alloc_base/logging.cc
@@ -9,8 +9,8 @@
 #endif
 
 #include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/base_export.h"
-#include "base/immediate_crash.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -253,7 +253,7 @@
   }
 
   if (level == LOGGING_FATAL)
-    IMMEDIATE_CRASH();
+    PA_IMMEDIATE_CRASH();
 }
 
 // This was defined at the beginning of this file.
diff --git a/base/allocator/partition_allocator/partition_alloc_check.h b/base/allocator/partition_allocator/partition_alloc_check.h
index b8bf636..58ad5c39 100644
--- a/base/allocator/partition_allocator/partition_alloc_check.h
+++ b/base/allocator/partition_allocator/partition_alloc_check.h
@@ -10,9 +10,9 @@
 #include "base/allocator/buildflags.h"
 #include "base/allocator/partition_allocator/page_allocator_constants.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/check.h"
 #include "base/dcheck_is_on.h"
-#include "base/immediate_crash.h"
 #include "build/build_config.h"
 
 #define PA_STRINGIFY_IMPL(s) #s
@@ -32,7 +32,7 @@
 #if defined(OFFICIAL_BUILD) && defined(NDEBUG)
 // See base/check.h for implementation details.
 #define PA_CHECK(condition) \
-  UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_CHECK_STREAM_PARAMS()
+  UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() : EAT_CHECK_STREAM_PARAMS()
 #else
 // PartitionAlloc uses async-signal-safe RawCheck() for error reporting.
 // Async-signal-safe functions are guaranteed to not allocate as otherwise they
@@ -54,7 +54,7 @@
   if (!(condition)) {                                        \
     int error = errno;                                       \
     ::partition_alloc::internal::base::debug::Alias(&error); \
-    IMMEDIATE_CRASH();                                       \
+    PA_IMMEDIATE_CRASH();                                    \
   }
 
 #else
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index 809771b..6f43d030 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -17,6 +17,7 @@
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
@@ -42,7 +43,7 @@
     size_t size) LOCKS_EXCLUDED(root->lock_) {
   PA_NO_CODE_FOLDING();
   root->OutOfMemory(size);
-  IMMEDIATE_CRASH();  // Not required, kept as documentation.
+  PA_IMMEDIATE_CRASH();  // Not required, kept as documentation.
 }
 
 template <bool thread_safe>
@@ -51,7 +52,7 @@
     size_t size) LOCKS_EXCLUDED(root->lock_) {
   PA_NO_CODE_FOLDING();
   root->OutOfMemory(size);
-  IMMEDIATE_CRASH();  // Not required, kept as documentation.
+  PA_IMMEDIATE_CRASH();  // Not required, kept as documentation.
 }
 
 #if !defined(PA_HAS_64_BITS_POINTERS) && BUILDFLAG(USE_BACKUP_REF_PTR)
@@ -1322,7 +1323,7 @@
     // See comment in PartitionDirectMap() for unlocking.
     ::partition_alloc::internal::ScopedUnlockGuard unlock{root->lock_};
     root->OutOfMemory(raw_size);
-    IMMEDIATE_CRASH();  // Not required, kept as documentation.
+    PA_IMMEDIATE_CRASH();  // Not required, kept as documentation.
   }
 
   PA_DCHECK(new_bucket != &root->sentinel_bucket);
diff --git a/base/allocator/partition_allocator/partition_freelist_entry.h b/base/allocator/partition_allocator/partition_freelist_entry.h
index 8529569..9dc07c9 100644
--- a/base/allocator/partition_allocator/partition_freelist_entry.h
+++ b/base/allocator/partition_allocator/partition_freelist_entry.h
@@ -11,6 +11,7 @@
 #include "base/allocator/buildflags.h"
 #include "base/allocator/partition_allocator/partition_alloc-inl.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/sys_byteorder.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
@@ -18,7 +19,6 @@
 #include "base/allocator/partition_allocator/partition_ref_count.h"
 #include "base/compiler_specific.h"
 #include "base/dcheck_is_on.h"
-#include "base/immediate_crash.h"
 #include "build/build_config.h"
 
 namespace partition_alloc::internal {
@@ -28,7 +28,7 @@
 [[noreturn]] NOINLINE void FreelistCorruptionDetected(size_t extra) {
   // Make it visible in minidumps.
   PA_DEBUG_DATA_ON_STACK("extra", extra);
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 }
 
 }  // namespace
diff --git a/base/allocator/partition_allocator/partition_lock.h b/base/allocator/partition_allocator/partition_lock.h
index 2dfea13..679a0ef 100644
--- a/base/allocator/partition_allocator/partition_lock.h
+++ b/base/allocator/partition_allocator/partition_lock.h
@@ -9,6 +9,7 @@
 #include <type_traits>
 
 #include "base/allocator/buildflags.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/spinning_mutex.h"
@@ -48,7 +49,7 @@
                    current_thread)) {
         // Trying to acquire lock while it's held by this thread: reentrancy
         // issue.
-        IMMEDIATE_CRASH();
+        PA_IMMEDIATE_CRASH();
       }
       lock_.Acquire();
     }
diff --git a/base/allocator/partition_allocator/partition_ref_count.h b/base/allocator/partition_allocator/partition_ref_count.h
index 65b208a..c882217 100644
--- a/base/allocator/partition_allocator/partition_ref_count.h
+++ b/base/allocator/partition_allocator/partition_ref_count.h
@@ -9,6 +9,7 @@
 #include <cstdint>
 
 #include "base/allocator/buildflags.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
@@ -31,7 +32,7 @@
 
 [[noreturn]] NOINLINE NOT_TAIL_CALLED void DoubleFreeOrCorruptionDetected() {
   PA_NO_CODE_FOLDING();
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 }
 
 }  // namespace
diff --git a/base/allocator/partition_allocator/partition_tls.h b/base/allocator/partition_allocator/partition_tls.h
index aaa0568..8bc72cf9 100644
--- a/base/allocator/partition_allocator/partition_tls.h
+++ b/base/allocator/partition_allocator/partition_tls.h
@@ -5,6 +5,7 @@
 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_
 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_
 
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
@@ -124,15 +125,15 @@
 ALWAYS_INLINE bool PartitionTlsCreate(PartitionTlsKey* key,
                                       void (*destructor)(void*)) {
   // NOTIMPLEMENTED() may allocate, crash instead.
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 }
 
 ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) {
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 }
 
 ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) {
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 }
 
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/base/allocator/partition_allocator/starscan/pcscan_internal.cc b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
index f711c714..70ba0479 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_internal.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
@@ -27,6 +27,7 @@
 #include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/cpu.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h"
@@ -48,7 +49,6 @@
 #include "base/allocator/partition_allocator/tagging.h"
 #include "base/allocator/partition_allocator/thread_cache.h"
 #include "base/compiler_specific.h"
-#include "base/immediate_crash.h"
 #include "build/build_config.h"
 
 // TODO(bikineev): Temporarily disable inlining in *Scan to get clearer
@@ -65,7 +65,7 @@
 
 [[noreturn]] NOINLINE NOT_TAIL_CALLED void DoubleFreeAttempt() {
   PA_NO_CODE_FOLDING();
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 }
 
 namespace {
diff --git a/base/allocator/partition_allocator/thread_cache.cc b/base/allocator/partition_allocator/thread_cache.cc
index 24f5ee7..570a4072 100644
--- a/base/allocator/partition_allocator/thread_cache.cc
+++ b/base/allocator/partition_allocator/thread_cache.cc
@@ -11,6 +11,7 @@
 #include <cstdint>
 
 #include "base/allocator/partition_allocator/partition_alloc_base/cxx17_backports.h"
+#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
@@ -353,7 +354,7 @@
 // static
 void ThreadCache::Init(PartitionRoot<>* root) {
 #if BUILDFLAG(IS_NACL)
-  IMMEDIATE_CRASH();
+  PA_IMMEDIATE_CRASH();
 #endif
   PA_CHECK(root->buckets[kBucketCount - 1].slot_size ==
            ThreadCache::kLargeSizeThreshold);
diff --git a/base/debug/close_handle_hook_win.h b/base/debug/close_handle_hook_win.h
deleted file mode 100644
index c775d75..0000000
--- a/base/debug/close_handle_hook_win.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2015 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 BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
-#define BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
-
-#include "base/base_export.h"
-
-namespace base {
-namespace debug {
-
-// Installs the hooks required to debug use of improper handles.
-BASE_EXPORT void InstallHandleHooks();
-
-}  // namespace debug
-}  // namespace base
-
-#endif  // BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
diff --git a/base/debug/close_handle_hook_win.cc b/base/debug/handle_hooks_win.cc
similarity index 61%
rename from base/debug/close_handle_hook_win.cc
rename to base/debug/handle_hooks_win.cc
index 68b0198..bc3eed4b 100644
--- a/base/debug/close_handle_hook_win.cc
+++ b/base/debug/handle_hooks_win.cc
@@ -2,17 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/debug/close_handle_hook_win.h"
+#include "base/debug/handle_hooks_win.h"
 
-#include <Windows.h>
+#include <windows.h>
+
 #include <psapi.h>
 #include <stddef.h>
 
-#include <algorithm>
-#include <memory>
-#include <vector>
-
+#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/win/iat_patch_function.h"
 #include "base/win/pe_image.h"
 #include "base/win/scoped_handle.h"
@@ -20,24 +19,18 @@
 
 namespace {
 
-typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle);
+using CloseHandleType = decltype(&::CloseHandle);
+using DuplicateHandleType = decltype(&::DuplicateHandle);
 
-typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process,
-                                           HANDLE source_handle,
-                                           HANDLE target_process,
-                                           HANDLE* target_handle,
-                                           DWORD desired_access,
-                                           BOOL inherit_handle,
-                                           DWORD options);
-
-CloseHandleType g_close_function = NULL;
-DuplicateHandleType g_duplicate_function = NULL;
+CloseHandleType g_close_function = nullptr;
+DuplicateHandleType g_duplicate_function = nullptr;
 
 // The entry point for CloseHandle interception. This function notifies the
 // verifier about the handle that is being closed, and calls the original
 // function.
 BOOL WINAPI CloseHandleHook(HANDLE handle) {
-  base::win::OnHandleBeingClosed(handle);
+  base::win::OnHandleBeingClosed(handle,
+                                 base::win::HandleOperation::kCloseHandleHook);
   return g_close_function(handle);
 }
 
@@ -50,7 +43,8 @@
                                 DWORD options) {
   if ((options & DUPLICATE_CLOSE_SOURCE) &&
       (GetProcessId(source_process) == ::GetCurrentProcessId())) {
-    base::win::OnHandleBeingClosed(source_handle);
+    base::win::OnHandleBeingClosed(
+        source_handle, base::win::HandleOperation::kDuplicateHandleHook);
   }
 
   return g_duplicate_function(source_process, source_handle, target_process,
@@ -74,9 +68,7 @@
   AutoProtectMemory(const AutoProtectMemory&) = delete;
   AutoProtectMemory& operator=(const AutoProtectMemory&) = delete;
 
-  ~AutoProtectMemory() {
-    RevertProtection();
-  }
+  ~AutoProtectMemory() { RevertProtection(); }
 
   // Grants write access to a given memory range.
   bool ChangeProtection(void* address, size_t bytes);
@@ -101,7 +93,7 @@
     return false;
 
   DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
-                        PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
+                         PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
                         memory_info.Protect;
 
   DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
@@ -128,9 +120,12 @@
   old_protect_ = 0;
 }
 
-// Performs an EAT interception.
-void EATPatch(HMODULE module, const char* function_name,
-              void* new_function, void** old_function) {
+#if defined(ARCH_CPU_32_BITS)
+// Performs an EAT interception. Only supported on 32-bit.
+void EATPatch(HMODULE module,
+              const char* function_name,
+              void* new_function,
+              void** old_function) {
   if (!module)
     return;
 
@@ -150,30 +145,35 @@
     return;
 
   // Perform the patch.
-  *eat_entry = static_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
-                                  reinterpret_cast<uintptr_t>(module));
+  *eat_entry =
+      base::checked_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
+                                reinterpret_cast<uintptr_t>(module));
 }
+#endif  // defined(ARCH_CPU_32_BITS)
 
 // Performs an IAT interception.
-base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name,
-                                      void* new_function, void** old_function) {
+std::unique_ptr<base::win::IATPatchFunction> IATPatch(HMODULE module,
+                                                      const char* function_name,
+                                                      void* new_function,
+                                                      void** old_function) {
   if (!module)
-    return NULL;
+    return nullptr;
 
-  base::win::IATPatchFunction* patch = new base::win::IATPatchFunction;
+  auto patch = std::make_unique<base::win::IATPatchFunction>();
   __try {
     // There is no guarantee that |module| is still loaded at this point.
     if (patch->PatchFromModule(module, "kernel32.dll", function_name,
                                new_function)) {
-      delete patch;
-      return NULL;
+      return nullptr;
     }
-  } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
-              GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
-              GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ?
-             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+  } __except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
+               GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
+               GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR)
+                  ? EXCEPTION_EXECUTE_HANDLER
+                  : EXCEPTION_CONTINUE_SEARCH) {
     // Leak the patch.
-    return NULL;
+    std::ignore = patch.release();
+    return nullptr;
   }
 
   if (!(*old_function)) {
@@ -184,44 +184,32 @@
   return patch;
 }
 
-// Keeps track of all the hooks needed to intercept functions which could
-// possibly close handles.
-class HandleHooks {
- public:
-  HandleHooks() {}
+}  // namespace
 
-  HandleHooks(const HandleHooks&) = delete;
-  HandleHooks& operator=(const HandleHooks&) = delete;
-
-  ~HandleHooks() {}
-
-  void AddIATPatch(HMODULE module);
-  void AddEATPatch();
-
- private:
-  std::vector<base::win::IATPatchFunction*> hooks_;
-};
-
+// static
 void HandleHooks::AddIATPatch(HMODULE module) {
   if (!module)
     return;
 
-  base::win::IATPatchFunction* patch = NULL;
-  patch =
+  auto close_handle_patch =
       IATPatch(module, "CloseHandle", reinterpret_cast<void*>(&CloseHandleHook),
                reinterpret_cast<void**>(&g_close_function));
-  if (!patch)
+  if (!close_handle_patch)
     return;
-  hooks_.push_back(patch);
+  // This is intentionally leaked.
+  std::ignore = close_handle_patch.release();
 
-  patch = IATPatch(module, "DuplicateHandle",
-                   reinterpret_cast<void*>(&DuplicateHandleHook),
-                   reinterpret_cast<void**>(&g_duplicate_function));
-  if (!patch)
+  auto duplicate_handle_patch = IATPatch(
+      module, "DuplicateHandle", reinterpret_cast<void*>(&DuplicateHandleHook),
+      reinterpret_cast<void**>(&g_duplicate_function));
+  if (!duplicate_handle_patch)
     return;
-  hooks_.push_back(patch);
+  // This is intentionally leaked.
+  std::ignore = duplicate_handle_patch.release();
 }
 
+#if defined(ARCH_CPU_32_BITS)
+// static
 void HandleHooks::AddEATPatch() {
   // An attempt to restore the entry on the table at destruction is not safe.
   EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
@@ -231,33 +219,24 @@
            reinterpret_cast<void*>(&DuplicateHandleHook),
            reinterpret_cast<void**>(&g_duplicate_function));
 }
+#endif  // defined(ARCH_CPU_32_BITS)
 
-void PatchLoadedModules(HandleHooks* hooks) {
+// static
+void HandleHooks::PatchLoadedModules() {
   const DWORD kSize = 256;
   DWORD returned;
-  std::unique_ptr<HMODULE[]> modules(new HMODULE[kSize]);
-  if (!EnumProcessModules(GetCurrentProcess(), modules.get(),
-                          kSize * sizeof(HMODULE), &returned)) {
+  auto modules = std::make_unique<HMODULE[]>(kSize);
+  if (!::EnumProcessModules(GetCurrentProcess(), modules.get(),
+                            kSize * sizeof(HMODULE), &returned)) {
     return;
   }
   returned /= sizeof(HMODULE);
   returned = std::min(kSize, returned);
 
   for (DWORD current = 0; current < returned; current++) {
-    hooks->AddIATPatch(modules[current]);
+    AddIATPatch(modules[current]);
   }
 }
 
-}  // namespace
-
-void InstallHandleHooks() {
-  static HandleHooks* hooks = new HandleHooks();
-
-  // Performing EAT interception first is safer in the presence of other
-  // threads attempting to call CloseHandle.
-  hooks->AddEATPatch();
-  PatchLoadedModules(hooks);
-}
-
 }  // namespace debug
 }  // namespace base
diff --git a/base/debug/handle_hooks_win.h b/base/debug/handle_hooks_win.h
new file mode 100644
index 0000000..5459393
--- /dev/null
+++ b/base/debug/handle_hooks_win.h
@@ -0,0 +1,41 @@
+// Copyright 2015 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 BASE_DEBUG_HANDLE_HOOKS_WIN_H_
+#define BASE_DEBUG_HANDLE_HOOKS_WIN_H_
+
+#include "base/base_export.h"
+#include "base/win/windows_types.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace debug {
+
+// Provides the ability to intercept functions which could possibly close
+// handles in support of the handle tracker.
+// This is a currently a container class for static functions because there is
+// ongoing work to make the patches unhook, currently blocked by test failures.
+// See https://crbug.com/1327397.
+class BASE_EXPORT HandleHooks {
+ public:
+  HandleHooks() = delete;
+
+  HandleHooks(const HandleHooks&) = delete;
+  HandleHooks& operator=(const HandleHooks&) = delete;
+
+  // Patch IAT for a specified module.
+  static void AddIATPatch(HMODULE module);
+  // Add an EAT patch on kernel32.dll. This patch does not get removed. This is
+  // only supported on 32-bit because the EAT only supports 32-bit RVAs.
+#if defined(ARCH_CPU_32_BITS)
+  static void AddEATPatch();
+#endif
+  // Patch IAT for all currently loaded modules.
+  static void PatchLoadedModules();
+};
+
+}  // namespace debug
+}  // namespace base
+
+#endif  // BASE_DEBUG_HANDLE_HOOKS_WIN_H_
diff --git a/base/substring_set_matcher/matcher_string_pattern.cc b/base/substring_set_matcher/matcher_string_pattern.cc
new file mode 100644
index 0000000..dc6273b
--- /dev/null
+++ b/base/substring_set_matcher/matcher_string_pattern.cc
@@ -0,0 +1,30 @@
+// Copyright 2013 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/substring_set_matcher/matcher_string_pattern.h"
+
+#include <tuple>
+#include <utility>
+
+#include "base/check_op.h"
+
+namespace base {
+
+MatcherStringPattern::MatcherStringPattern(std::string pattern,
+                                           MatcherStringPattern::ID id)
+    : pattern_(std::move(pattern)), id_(id) {
+  DCHECK_NE(kInvalidId, id_);
+}
+
+MatcherStringPattern::~MatcherStringPattern() = default;
+
+MatcherStringPattern::MatcherStringPattern(MatcherStringPattern&&) = default;
+MatcherStringPattern& MatcherStringPattern::operator=(MatcherStringPattern&&) =
+    default;
+
+bool MatcherStringPattern::operator<(const MatcherStringPattern& rhs) const {
+  return std::tie(id_, pattern_) < std::tie(rhs.id_, rhs.pattern_);
+}
+
+}  // namespace base
diff --git a/base/substring_set_matcher/string_pattern.h b/base/substring_set_matcher/matcher_string_pattern.h
similarity index 60%
rename from base/substring_set_matcher/string_pattern.h
rename to base/substring_set_matcher/matcher_string_pattern.h
index f03553f..935ac601 100644
--- a/base/substring_set_matcher/string_pattern.h
+++ b/base/substring_set_matcher/matcher_string_pattern.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 BASE_SUBSTRING_SET_MATCHER_STRING_PATTERN_H_
-#define BASE_SUBSTRING_SET_MATCHER_STRING_PATTERN_H_
+#ifndef BASE_SUBSTRING_SET_MATCHER_MATCHER_STRING_PATTERN_H_
+#define BASE_SUBSTRING_SET_MATCHER_MATCHER_STRING_PATTERN_H_
 
 #include <string>
 
@@ -18,25 +18,25 @@
 // RegexMatcher::MatchURL() to help the caller to figure out what
 // patterns matched a string. All patterns registered to a matcher
 // need to contain unique IDs.
-class BASE_EXPORT StringPattern {
+class BASE_EXPORT MatcherStringPattern {
  public:
   typedef int ID;
 
   // An invalid ID value. Clients must not use this as the id.
   static constexpr ID kInvalidId = -1;
 
-  StringPattern(std::string pattern, ID id);
+  MatcherStringPattern(std::string pattern, ID id);
 
-  StringPattern(const StringPattern&) = delete;
-  StringPattern& operator=(const StringPattern&) = delete;
+  MatcherStringPattern(const MatcherStringPattern&) = delete;
+  MatcherStringPattern& operator=(const MatcherStringPattern&) = delete;
 
-  ~StringPattern();
-  StringPattern(StringPattern&&);
-  StringPattern& operator=(StringPattern&&);
+  ~MatcherStringPattern();
+  MatcherStringPattern(MatcherStringPattern&&);
+  MatcherStringPattern& operator=(MatcherStringPattern&&);
   const std::string& pattern() const { return pattern_; }
   ID id() const { return id_; }
 
-  bool operator<(const StringPattern& rhs) const;
+  bool operator<(const MatcherStringPattern& rhs) const;
 
  private:
   std::string pattern_;
@@ -45,4 +45,4 @@
 
 }  // namespace base
 
-#endif  // BASE_SUBSTRING_SET_MATCHER_STRING_PATTERN_H_
+#endif  // BASE_SUBSTRING_SET_MATCHER_MATCHER_STRING_PATTERN_H_
diff --git a/base/substring_set_matcher/string_pattern.cc b/base/substring_set_matcher/string_pattern.cc
deleted file mode 100644
index 6940f2c..0000000
--- a/base/substring_set_matcher/string_pattern.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 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/substring_set_matcher/string_pattern.h"
-
-#include <tuple>
-#include <utility>
-
-#include "base/check_op.h"
-
-namespace base {
-
-StringPattern::StringPattern(std::string pattern, StringPattern::ID id)
-    : pattern_(std::move(pattern)), id_(id) {
-  DCHECK_NE(kInvalidId, id_);
-}
-
-StringPattern::~StringPattern() {}
-
-StringPattern::StringPattern(StringPattern&&) = default;
-StringPattern& StringPattern::operator=(StringPattern&&) = default;
-
-bool StringPattern::operator<(const StringPattern& rhs) const {
-  return std::tie(id_, pattern_) < std::tie(rhs.id_, rhs.pattern_);
-}
-
-}  // namespace base
diff --git a/base/substring_set_matcher/string_pattern_unittest.cc b/base/substring_set_matcher/string_pattern_unittest.cc
index a931365..e597a04 100644
--- a/base/substring_set_matcher/string_pattern_unittest.cc
+++ b/base/substring_set_matcher/string_pattern_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 "base/substring_set_matcher/string_pattern.h"
+#include "base/substring_set_matcher/matcher_string_pattern.h"
 
 #include <string>
 
@@ -10,15 +10,15 @@
 
 namespace base {
 
-TEST(StringPatternTest, StringPattern) {
-  StringPattern r1("Test", 2);
+TEST(MatcherStringPatternTest, MatcherStringPattern) {
+  MatcherStringPattern r1("Test", 2);
   EXPECT_EQ("Test", r1.pattern());
   EXPECT_EQ(2, r1.id());
 
   EXPECT_FALSE(r1 < r1);
-  StringPattern r2("Test", 3);
+  MatcherStringPattern r2("Test", 3);
   EXPECT_TRUE(r1 < r2);
-  StringPattern r3("ZZZZ", 2);
+  MatcherStringPattern r3("ZZZZ", 2);
   EXPECT_TRUE(r1 < r3);
 }
 
diff --git a/base/substring_set_matcher/substring_set_matcher.cc b/base/substring_set_matcher/substring_set_matcher.cc
index d2c66ea..b3496da 100644
--- a/base/substring_set_matcher/substring_set_matcher.cc
+++ b/base/substring_set_matcher/substring_set_matcher.cc
@@ -24,17 +24,18 @@
 
 namespace {
 
-// Compare StringPattern instances based on their string patterns.
-bool ComparePatterns(const StringPattern* a, const StringPattern* b) {
+// Compare MatcherStringPattern instances based on their string patterns.
+bool ComparePatterns(const MatcherStringPattern* a,
+                     const MatcherStringPattern* b) {
   return a->pattern() < b->pattern();
 }
 
-std::vector<const StringPattern*> GetVectorOfPointers(
-    const std::vector<StringPattern>& patterns) {
-  std::vector<const StringPattern*> pattern_pointers;
+std::vector<const MatcherStringPattern*> GetVectorOfPointers(
+    const std::vector<MatcherStringPattern>& patterns) {
+  std::vector<const MatcherStringPattern*> pattern_pointers;
   pattern_pointers.reserve(patterns.size());
 
-  for (const StringPattern& pattern : patterns)
+  for (const MatcherStringPattern& pattern : patterns)
     pattern_pointers.push_back(&pattern);
 
   return pattern_pointers;
@@ -42,17 +43,19 @@
 
 }  // namespace
 
-bool SubstringSetMatcher::Build(const std::vector<StringPattern>& patterns) {
+bool SubstringSetMatcher::Build(
+    const std::vector<MatcherStringPattern>& patterns) {
   return Build(GetVectorOfPointers(patterns));
 }
 
-bool SubstringSetMatcher::Build(std::vector<const StringPattern*> patterns) {
+bool SubstringSetMatcher::Build(
+    std::vector<const MatcherStringPattern*> patterns) {
   // Ensure there are no duplicate IDs and all pattern strings are distinct.
 #if DCHECK_IS_ON()
   {
-    std::set<StringPattern::ID> ids;
+    std::set<MatcherStringPattern::ID> ids;
     std::set<std::string> pattern_strings;
-    for (const StringPattern* pattern : patterns) {
+    for (const MatcherStringPattern* pattern : patterns) {
       CHECK(!base::Contains(ids, pattern->id()));
       CHECK(!base::Contains(pattern_strings, pattern->pattern()));
       ids.insert(pattern->id());
@@ -62,7 +65,7 @@
 #endif
 
   // Check that all the match labels fit into an edge.
-  for (const StringPattern* pattern : patterns) {
+  for (const MatcherStringPattern* pattern : patterns) {
     if (pattern->id() < 0 ||
         base::checked_cast<NodeID>(pattern->id()) >= kInvalidNodeID) {
       return false;
@@ -88,8 +91,9 @@
 
 SubstringSetMatcher::~SubstringSetMatcher() = default;
 
-bool SubstringSetMatcher::Match(const std::string& text,
-                                std::set<StringPattern::ID>* matches) const {
+bool SubstringSetMatcher::Match(
+    const std::string& text,
+    std::set<MatcherStringPattern::ID>* matches) const {
   const size_t old_number_of_matches = matches->size();
 
   // Handle patterns matching the empty string.
@@ -168,7 +172,7 @@
 constexpr SubstringSetMatcher::NodeID SubstringSetMatcher::kRootID;
 
 SubstringSetMatcher::NodeID SubstringSetMatcher::GetTreeSize(
-    const std::vector<const StringPattern*>& patterns) const {
+    const std::vector<const MatcherStringPattern*>& patterns) const {
   DCHECK(std::is_sorted(patterns.begin(), patterns.end(), ComparePatterns));
 
   base::CheckedNumeric<NodeID> result = 1u;  // 1 for the root node.
@@ -209,14 +213,14 @@
   tree_.emplace_back();
 
   // Build the initial trie for all the patterns.
-  for (const StringPattern* pattern : patterns)
+  for (const MatcherStringPattern* pattern : patterns)
     InsertPatternIntoAhoCorasickTree(pattern);
 
   CreateFailureAndOutputEdges();
 }
 
 void SubstringSetMatcher::InsertPatternIntoAhoCorasickTree(
-    const StringPattern* pattern) {
+    const MatcherStringPattern* pattern) {
   const std::string& text = pattern->pattern();
   const std::string::const_iterator text_end = text.end();
 
@@ -319,7 +323,7 @@
 
 void SubstringSetMatcher::AccumulateMatchesForNode(
     const AhoCorasickNode* node,
-    std::set<StringPattern::ID>* matches) const {
+    std::set<MatcherStringPattern::ID>* matches) const {
   DCHECK(matches);
 
   if (!node->has_outputs()) {
diff --git a/base/substring_set_matcher/substring_set_matcher.h b/base/substring_set_matcher/substring_set_matcher.h
index 47f913f..520a5e6f 100644
--- a/base/substring_set_matcher/substring_set_matcher.h
+++ b/base/substring_set_matcher/substring_set_matcher.h
@@ -14,7 +14,7 @@
 
 #include "base/base_export.h"
 #include "base/check_op.h"
-#include "base/substring_set_matcher/string_pattern.h"
+#include "base/substring_set_matcher/matcher_string_pattern.h"
 
 namespace base {
 
@@ -41,10 +41,10 @@
   // log(k) comes from our usage of std::map to store edges.
   //
   // Returns true on success (may fail if e.g. if the tree gets too many nodes).
-  bool Build(const std::vector<StringPattern>& patterns);
-  bool Build(std::vector<const StringPattern*> patterns);
+  bool Build(const std::vector<MatcherStringPattern>& patterns);
+  bool Build(std::vector<const MatcherStringPattern*> patterns);
 
-  // Matches |text| against all registered StringPatterns. Stores the IDs
+  // Matches |text| against all registered MatcherStringPatterns. Stores the IDs
   // of matching patterns in |matches|. |matches| is not cleared before adding
   // to it.
   // Complexity:
@@ -53,7 +53,7 @@
   //    Let z = number of matches returned.
   // Complexity = O(t * logk + zlogz)
   bool Match(const std::string& text,
-             std::set<StringPattern::ID>* matches) const;
+             std::set<MatcherStringPattern::ID>* matches) const;
 
   // As Match(), except it returns immediately on the first match.
   // This allows true/false matching to be done without any dynamic
@@ -145,7 +145,7 @@
 
   // If present, this node represents the end of a pattern. It stores the ID of
   // the corresponding pattern (ie., it is not really a NodeID, but a
-  // StringPattern::ID).
+  // MatcherStringPattern::ID).
   static constexpr uint32_t kMatchIDLabel = 0x102;
 
   // Used for uninitialized label slots; used so that we do not have to test for
@@ -199,7 +199,7 @@
     }
     void SetFailure(NodeID failure);
 
-    void SetMatchID(StringPattern::ID id) {
+    void SetMatchID(MatcherStringPattern::ID id) {
       DCHECK(!IsEndOfPattern());
       SetEdge(kMatchIDLabel, id);
       has_outputs_ = true;
@@ -215,7 +215,7 @@
     }
 
     // Must only be called if |IsEndOfPattern| returns true for this node.
-    StringPattern::ID GetMatchID() const {
+    MatcherStringPattern::ID GetMatchID() const {
       DCHECK(IsEndOfPattern());
       return GetEdge(kMatchIDLabel);
     }
@@ -303,24 +303,26 @@
     uint16_t edges_capacity_ = 0;
   } __attribute__((packed));
 
-  using SubstringPatternVector = std::vector<const StringPattern*>;
+  using SubstringPatternVector = std::vector<const MatcherStringPattern*>;
 
   // Given the set of patterns, compute how many nodes will the corresponding
   // Aho-Corasick tree have. Note that |patterns| need to be sorted.
-  NodeID GetTreeSize(const std::vector<const StringPattern*>& patterns) const;
+  NodeID GetTreeSize(
+      const std::vector<const MatcherStringPattern*>& patterns) const;
 
   void BuildAhoCorasickTree(const SubstringPatternVector& patterns);
 
   // Inserts a path for |pattern->pattern()| into the tree and adds
   // |pattern->id()| to the set of matches.
-  void InsertPatternIntoAhoCorasickTree(const StringPattern* pattern);
+  void InsertPatternIntoAhoCorasickTree(const MatcherStringPattern* pattern);
 
   void CreateFailureAndOutputEdges();
 
   // Adds all pattern IDs to |matches| which are a suffix of the string
   // represented by |node|.
-  void AccumulateMatchesForNode(const AhoCorasickNode* node,
-                                std::set<StringPattern::ID>* matches) const;
+  void AccumulateMatchesForNode(
+      const AhoCorasickNode* node,
+      std::set<MatcherStringPattern::ID>* matches) const;
 
   // The nodes of a Aho-Corasick tree.
   std::vector<AhoCorasickNode> tree_;
diff --git a/base/substring_set_matcher/substring_set_matcher_perftest.cc b/base/substring_set_matcher/substring_set_matcher_perftest.cc
index 682c0a073..d6fc237 100644
--- a/base/substring_set_matcher/substring_set_matcher_perftest.cc
+++ b/base/substring_set_matcher/substring_set_matcher_perftest.cc
@@ -33,7 +33,7 @@
 // Tests performance of SubstringSetMatcher for 20000 random patterns of length
 // 30.
 TEST(SubstringSetMatcherPerfTest, RandomKeys) {
-  std::vector<StringPattern> patterns;
+  std::vector<MatcherStringPattern> patterns;
   std::set<std::string> pattern_strings;
 
   // Create patterns.
@@ -61,7 +61,7 @@
   // Match patterns against a random string of 500 characters.
   const size_t kTextLen = 500;
   base::ElapsedTimer match_timer;
-  std::set<StringPattern::ID> matches;
+  std::set<MatcherStringPattern::ID> matches;
   matcher->Match(GetRandomString(kTextLen), &matches);
   base::TimeDelta match_time = match_timer.Elapsed();
 
diff --git a/base/substring_set_matcher/substring_set_matcher_unittest.cc b/base/substring_set_matcher/substring_set_matcher_unittest.cc
index d0f611b5..a9cbb65 100644
--- a/base/substring_set_matcher/substring_set_matcher_unittest.cc
+++ b/base/substring_set_matcher/substring_set_matcher_unittest.cc
@@ -22,7 +22,7 @@
                     bool is_match) {
   std::string test = "TestOnePattern(" + test_string + ", " + pattern + ", " +
                      (is_match ? "1" : "0") + ")";
-  std::vector<StringPattern> patterns;
+  std::vector<MatcherStringPattern> patterns;
   patterns.emplace_back(pattern, 1);
   SubstringSetMatcher matcher;
   ASSERT_TRUE(matcher.Build(patterns));
@@ -43,12 +43,12 @@
                      ", " + pattern_2 + ", " + (is_match_1 ? "1" : "0") + ", " +
                      (is_match_2 ? "1" : "0") + ")";
   ASSERT_NE(pattern_1, pattern_2);
-  StringPattern substring_pattern_1(pattern_1, 1);
-  StringPattern substring_pattern_2(pattern_2, 2);
+  MatcherStringPattern substring_pattern_1(pattern_1, 1);
+  MatcherStringPattern substring_pattern_2(pattern_2, 2);
   // In order to make sure that the order in which patterns are registered
   // does not make any difference we try both permutations.
   for (int permutation = 0; permutation < 2; ++permutation) {
-    std::vector<const StringPattern*> patterns;
+    std::vector<const MatcherStringPattern*> patterns;
     if (permutation == 0) {
       patterns.push_back(&substring_pattern_1);
       patterns.push_back(&substring_pattern_2);
@@ -134,12 +134,12 @@
 }
 
 TEST(SubstringSetMatcherTest, TestMatcher2) {
-  StringPattern pattern_1("a", 1);
-  StringPattern pattern_2("b", 2);
-  StringPattern pattern_3("c", 3);
+  MatcherStringPattern pattern_1("a", 1);
+  MatcherStringPattern pattern_2("b", 2);
+  MatcherStringPattern pattern_3("c", 3);
 
-  std::vector<const StringPattern*> patterns = {&pattern_1, &pattern_2,
-                                                &pattern_3};
+  std::vector<const MatcherStringPattern*> patterns = {&pattern_1, &pattern_2,
+                                                       &pattern_3};
   auto matcher = std::make_unique<SubstringSetMatcher>();
   ASSERT_TRUE(matcher->Build(patterns));
 
@@ -160,14 +160,14 @@
   EXPECT_TRUE(matches.end() == matches.find(2));
 
   matcher = std::make_unique<SubstringSetMatcher>();
-  ASSERT_TRUE(matcher->Build(std::vector<const StringPattern*>()));
+  ASSERT_TRUE(matcher->Build(std::vector<const MatcherStringPattern*>()));
   EXPECT_TRUE(matcher->IsEmpty());
 }
 
 TEST(SubstringSetMatcherTest, TestMatcher3) {
   std::string text = "abcde";
 
-  std::vector<StringPattern> patterns;
+  std::vector<MatcherStringPattern> patterns;
   int id = 0;
   // Add all substrings of this string, including empty string.
   patterns.emplace_back("", id++);
@@ -182,14 +182,14 @@
   std::set<int> matches;
   matcher.Match(text, &matches);
   EXPECT_EQ(patterns.size(), matches.size());
-  for (const StringPattern& pattern : patterns) {
+  for (const MatcherStringPattern& pattern : patterns) {
     EXPECT_TRUE(matches.find(pattern.id()) != matches.end())
         << pattern.pattern();
   }
 }
 
 TEST(SubstringSetMatcherTest, TestEmptyMatcher) {
-  std::vector<StringPattern> patterns;
+  std::vector<MatcherStringPattern> patterns;
   SubstringSetMatcher matcher;
   matcher.Build(patterns);
   std::set<int> matches;
diff --git a/base/win/scoped_handle.cc b/base/win/scoped_handle.cc
index de68545..1c1cdce 100644
--- a/base/win/scoped_handle.cc
+++ b/base/win/scoped_handle.cc
@@ -11,6 +11,21 @@
 
 using base::win::internal::ScopedHandleVerifier;
 
+std::ostream& operator<<(std::ostream& os, HandleOperation operation) {
+  switch (operation) {
+    case HandleOperation::kHandleAlreadyTracked:
+      return os << "Handle Already Tracked";
+    case HandleOperation::kCloseHandleNotTracked:
+      return os << "Closing an untracked handle";
+    case HandleOperation::kCloseHandleNotOwner:
+      return os << "Closing a handle owned by something else";
+    case HandleOperation::kCloseHandleHook:
+      return os << "CloseHandleHook validation failure";
+    case HandleOperation::kDuplicateHandleHook:
+      return os << "DuplicateHandleHook validation failure";
+  }
+}
+
 // Static.
 bool HandleTraits::CloseHandle(HANDLE handle) {
   return ScopedHandleVerifier::Get()->CloseHandle(handle);
@@ -36,8 +51,8 @@
   return ScopedHandleVerifier::Get()->Disable();
 }
 
-void OnHandleBeingClosed(HANDLE handle) {
-  return ScopedHandleVerifier::Get()->OnHandleBeingClosed(handle);
+void OnHandleBeingClosed(HANDLE handle, HandleOperation operation) {
+  return ScopedHandleVerifier::Get()->OnHandleBeingClosed(handle, operation);
 }
 
 }  // namespace win
diff --git a/base/win/scoped_handle.h b/base/win/scoped_handle.h
index bfa8188..3a7d47e 100644
--- a/base/win/scoped_handle.h
+++ b/base/win/scoped_handle.h
@@ -7,6 +7,8 @@
 
 #include "base/win/windows_types.h"
 
+#include <ostream>
+
 #include "base/base_export.h"
 #include "base/check_op.h"
 #include "base/dcheck_is_on.h"
@@ -26,6 +28,16 @@
 namespace base {
 namespace win {
 
+enum class HandleOperation {
+  kHandleAlreadyTracked,
+  kCloseHandleNotTracked,
+  kCloseHandleNotOwner,
+  kCloseHandleHook,
+  kDuplicateHandleHook
+};
+
+std::ostream& operator<<(std::ostream& os, HandleOperation operation);
+
 // Generic wrapper for raw handles that takes care of closing handles
 // automatically. The class interface follows the style of
 // the ScopedFILE class with two additions:
@@ -113,8 +125,9 @@
   }
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, HandleVerifierWrongOwner);
-  FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, HandleVerifierUntrackedHandle);
+  FRIEND_TEST_ALL_PREFIXES(ScopedHandleDeathTest, HandleVerifierWrongOwner);
+  FRIEND_TEST_ALL_PREFIXES(ScopedHandleDeathTest,
+                           HandleVerifierUntrackedHandle);
   Handle handle_;
 };
 
@@ -183,7 +196,7 @@
     GenericScopedHandle<HandleTraits, DummyVerifierTraits>;
 using CheckedScopedHandle = GenericScopedHandle<HandleTraits, VerifierTraits>;
 
-#if DCHECK_IS_ON() && !defined(ARCH_CPU_64_BITS)
+#if DCHECK_IS_ON()
 using ScopedHandle = CheckedScopedHandle;
 #else
 using ScopedHandle = UncheckedScopedHandle;
@@ -198,7 +211,8 @@
 // verification of improper handle closing is desired. If |handle| is being
 // tracked by the handle verifier and ScopedHandle is not the one closing it,
 // a CHECK is generated.
-BASE_EXPORT void OnHandleBeingClosed(HANDLE handle);
+BASE_EXPORT void OnHandleBeingClosed(HANDLE handle, HandleOperation operation);
+
 }  // namespace win
 }  // namespace base
 
diff --git a/base/win/scoped_handle_unittest.cc b/base/win/scoped_handle_unittest.cc
index 0df21ce..1b4ccbc 100644
--- a/base/win/scoped_handle_unittest.cc
+++ b/base/win/scoped_handle_unittest.cc
@@ -8,11 +8,13 @@
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
+#include "base/debug/handle_hooks_win.h"
 #include "base/files/file_path.h"
 #include "base/scoped_native_library.h"
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_timeouts.h"
 #include "base/win/scoped_handle.h"
+#include "build/build_config.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
@@ -20,13 +22,49 @@
 namespace base {
 namespace win {
 
+namespace {
+
+std::string FailureMessage(const std::string& msg) {
+#if !defined(DEBUG) && defined(OFFICIAL_BUILD)
+  // Official release builds strip all fatal messages for saving binary size,
+  // see base/check.h.
+  return "";
+#else
+  return msg;
+#endif
+}
+
+}  // namespace
+
 namespace testing {
 extern "C" bool __declspec(dllexport) RunTest();
 }  // namespace testing
 
-TEST(ScopedHandleTest, ScopedHandle) {
+class ScopedHandleTest : public ::testing::Test,
+                         public ::testing::WithParamInterface<bool> {
+ public:
+  ScopedHandleTest(const ScopedHandleTest&) = delete;
+  ScopedHandleTest& operator=(const ScopedHandleTest&) = delete;
+
+ protected:
+  ScopedHandleTest() {
+    if (HooksEnabled()) {
+#if defined(ARCH_CPU_32_BITS)
+      // EAT patch is only supported on 32-bit.
+      base::debug::HandleHooks::AddEATPatch();
+#endif
+      base::debug::HandleHooks::PatchLoadedModules();
+    }
+  }
+
+  static bool HooksEnabled() { return GetParam(); }
+};
+
+using ScopedHandleDeathTest = ScopedHandleTest;
+
+TEST_P(ScopedHandleTest, ScopedHandle) {
   // Any illegal error code will do. We just need to test that it is preserved
-  // by ScopedHandle to avoid bug 528394.
+  // by ScopedHandle to avoid https://crbug.com/528394.
   const DWORD magic_error = 0x12345678;
 
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
@@ -49,7 +87,7 @@
   EXPECT_EQ(magic_error, ::GetLastError());
 }
 
-TEST(ScopedHandleTest, HandleVerifierTrackedHasBeenClosed) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierTrackedHasBeenClosed) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
   using NtCloseFunc = decltype(&::NtClose);
@@ -64,10 +102,35 @@
         // Destructing a ScopedHandle with an illegally closed handle should
         // fail.
       },
-      "");
+      FailureMessage("CloseHandle failed"));
 }
 
-TEST(ScopedHandleTest, HandleVerifierDoubleTracking) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierCloseTrackedHandle) {
+  // This test is only valid if hooks are enabled.
+  if (!HooksEnabled())
+    return;
+  ASSERT_DEATH(
+      {
+        HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
+        ASSERT_NE(HANDLE(nullptr), handle);
+
+        // Start tracking the handle so that closes outside of the checker are
+        // caught.
+        base::win::CheckedScopedHandle handle_holder(handle);
+
+        // Closing a tracked handle using ::CloseHandle should crash due to hook
+        // noticing the illegal close.
+        ::CloseHandle(handle);
+      },
+      // This test must match the CloseHandleHook causing this failure, because
+      // if the hook doesn't crash and instead the handle is double closed by
+      // the `handle_holder` going out of scope, then there is still a crash,
+      // but a different crash and one we are not explicitly testing here. This
+      // other crash is tested in HandleVerifierTrackedHasBeenClosed above.
+      FailureMessage("CloseHandleHook validation failure"));
+}
+
+TEST_P(ScopedHandleDeathTest, HandleVerifierDoubleTracking) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
 
@@ -76,7 +139,7 @@
   ASSERT_DEATH({ base::win::CheckedScopedHandle handle_holder2(handle); }, "");
 }
 
-TEST(ScopedHandleTest, HandleVerifierWrongOwner) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierWrongOwner) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
 
@@ -86,12 +149,12 @@
         base::win::CheckedScopedHandle handle_holder2;
         handle_holder2.handle_ = handle;
       },
-      "");
+      FailureMessage("Closing a handle owned by something else"));
   ASSERT_TRUE(handle_holder.is_valid());
   handle_holder.Close();
 }
 
-TEST(ScopedHandleTest, HandleVerifierUntrackedHandle) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierUntrackedHandle) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
 
@@ -100,7 +163,7 @@
         base::win::CheckedScopedHandle handle_holder;
         handle_holder.handle_ = handle;
       },
-      "");
+      FailureMessage("Closing an untracked handle"));
 
   ASSERT_TRUE(::CloseHandle(handle));
 }
@@ -113,7 +176,7 @@
 #define MAYBE_MultiProcess MultiProcess
 #endif
 
-TEST(ScopedHandleTest, MAYBE_MultiProcess) {
+TEST_P(ScopedHandleTest, MAYBE_MultiProcess) {
   // Initializing ICU in the child process causes a scoped handle to be created
   // before the test gets a chance to test the race condition, so disable ICU
   // for the child process here.
@@ -145,5 +208,18 @@
   return 0;
 }
 
+INSTANTIATE_TEST_SUITE_P(HooksEnabled,
+                         ScopedHandleTest,
+                         ::testing::Values(true));
+INSTANTIATE_TEST_SUITE_P(HooksDisabled,
+                         ScopedHandleTest,
+                         ::testing::Values(false));
+INSTANTIATE_TEST_SUITE_P(HooksEnabled,
+                         ScopedHandleDeathTest,
+                         ::testing::Values(true));
+INSTANTIATE_TEST_SUITE_P(HooksDisabled,
+                         ScopedHandleDeathTest,
+                         ::testing::Values(false));
+
 }  // namespace win
 }  // namespace base
diff --git a/base/win/scoped_handle_verifier.cc b/base/win/scoped_handle_verifier.cc
index e64bd2c..e7ab4b9 100644
--- a/base/win/scoped_handle_verifier.cc
+++ b/base/win/scoped_handle_verifier.cc
@@ -18,6 +18,7 @@
 #include "base/trace_event/base_tracing.h"
 #include "base/win/base_win_buildflags.h"
 #include "base/win/current_module.h"
+#include "base/win/scoped_handle.h"
 
 extern "C" {
 __declspec(dllexport) void* GetHandleVerifier();
@@ -40,21 +41,25 @@
 using NativeLock = base::internal::LockImpl;
 
 NOINLINE void ReportErrorOnScopedHandleOperation(
-    const base::debug::StackTrace& creation_stack) {
+    const base::debug::StackTrace& creation_stack,
+    HandleOperation operation) {
   auto creation_stack_copy = creation_stack;
   base::debug::Alias(&creation_stack_copy);
-  CHECK(false);
+  base::debug::Alias(&operation);
+  CHECK(false) << operation;
   __builtin_unreachable();
 }
 
 NOINLINE void ReportErrorOnScopedHandleOperation(
     const base::debug::StackTrace& creation_stack,
-    const ScopedHandleVerifierInfo& other) {
+    const ScopedHandleVerifierInfo& other,
+    HandleOperation operation) {
   auto other_stack_copy = *other.stack;
   base::debug::Alias(&other_stack_copy);
   auto creation_stack_copy = creation_stack;
   base::debug::Alias(&creation_stack_copy);
-  CHECK(false);
+  base::debug::Alias(&operation);
+  CHECK(false) << operation;
   __builtin_unreachable();
 }
 
@@ -107,7 +112,7 @@
 
 bool CloseHandleWrapper(HANDLE handle) {
   if (!::CloseHandle(handle))
-    CHECK(false);  // CloseHandle failed.
+    CHECK(false) << "CloseHandle failed";
   return true;
 }
 
@@ -202,9 +207,10 @@
   enabled_ = false;
 }
 
-void ScopedHandleVerifier::OnHandleBeingClosed(HANDLE handle) {
+void ScopedHandleVerifier::OnHandleBeingClosed(HANDLE handle,
+                                               HandleOperation operation) {
   if (enabled_)
-    OnHandleBeingClosedImpl(handle);
+    OnHandleBeingClosedImpl(handle, operation);
 }
 
 HMODULE ScopedHandleVerifier::GetModule() const {
@@ -227,7 +233,8 @@
                                        thread_id});
   if (!result.second) {
     // Attempt to start tracking already tracked handle.
-    ReportErrorOnScopedHandleOperation(creation_stack_, result.first->second);
+    ReportErrorOnScopedHandleOperation(creation_stack_, result.first->second,
+                                       HandleOperation::kHandleAlreadyTracked);
   }
 }
 
@@ -239,18 +246,22 @@
   HandleMap::iterator i = map_.find(handle);
   if (i == map_.end()) {
     // Attempting to close an untracked handle.
-    ReportErrorOnScopedHandleOperation(creation_stack_);
+    ReportErrorOnScopedHandleOperation(creation_stack_,
+                                       HandleOperation::kCloseHandleNotTracked);
   }
 
   if (i->second.owner != owner) {
     // Attempting to close a handle not owned by opener.
-    ReportErrorOnScopedHandleOperation(creation_stack_, i->second);
+    ReportErrorOnScopedHandleOperation(creation_stack_, i->second,
+                                       HandleOperation::kCloseHandleNotOwner);
   }
 
   map_.erase(i);
 }
 
-NOINLINE void ScopedHandleVerifier::OnHandleBeingClosedImpl(HANDLE handle) {
+NOINLINE void ScopedHandleVerifier::OnHandleBeingClosedImpl(
+    HANDLE handle,
+    HandleOperation operation) {
   if (closing_.Get())
     return;
 
@@ -258,7 +269,7 @@
   HandleMap::iterator i = map_.find(handle);
   if (i != map_.end()) {
     // CloseHandle called on tracked handle.
-    ReportErrorOnScopedHandleOperation(creation_stack_, i->second);
+    ReportErrorOnScopedHandleOperation(creation_stack_, i->second, operation);
   }
 }
 
diff --git a/base/win/scoped_handle_verifier.h b/base/win/scoped_handle_verifier.h
index 84bb547d..7085e99 100644
--- a/base/win/scoped_handle_verifier.h
+++ b/base/win/scoped_handle_verifier.h
@@ -18,6 +18,7 @@
 
 namespace base {
 namespace win {
+enum class HandleOperation;
 namespace internal {
 
 struct HandleHash {
@@ -76,7 +77,7 @@
   virtual void StopTracking(HANDLE handle, const void* owner, const void* pc1,
                             const void* pc2);
   virtual void Disable();
-  virtual void OnHandleBeingClosed(HANDLE handle);
+  virtual void OnHandleBeingClosed(HANDLE handle, HandleOperation operation);
   virtual HMODULE GetModule() const;
 
  private:
@@ -86,7 +87,7 @@
                          const void* pc2);
   void StopTrackingImpl(HANDLE handle, const void* owner, const void* pc1,
                         const void* pc2);
-  void OnHandleBeingClosedImpl(HANDLE handle);
+  void OnHandleBeingClosedImpl(HANDLE handle, HandleOperation operation);
 
   static base::internal::LockImpl* GetLock();
   static void InstallVerifier();
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 1817f17..c90821e 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -680,16 +680,9 @@
       # of "all" which means number of hardware threads) is faster.
       ldflags += [ "-Wl,--thinlto-jobs=all" ]
       if (is_apple) {
-        # TODO(lgrey): Remove if/when we start running `dsymutil` through the
-        # clang driver. See https://crbug.com/1324104
-        object_path_lto =
-            rebase_path("$root_out_dir/lto-objects", root_build_dir)
-        ldflags += [
-          "-Wl,-cache_path_lto," +
-              rebase_path("$root_out_dir/thinlto-cache", root_build_dir),
-          "-Wl,-object_path_lto,$object_path_lto",
-          "-Wcrl,clean_object_path_lto,$object_path_lto",
-        ]
+        ldflags +=
+            [ "-Wl,-cache_path_lto," +
+              rebase_path("$root_out_dir/thinlto-cache", root_build_dir) ]
       } else {
         ldflags +=
             [ "-Wl,--thinlto-cache-dir=" +
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 93b7c66..afd1c9c4 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220520.1.2
+8.20220520.3.1
diff --git a/build/linux/sysroot_scripts/sysroot-creator.sh b/build/linux/sysroot_scripts/sysroot-creator.sh
index bde29db9..1aca764 100644
--- a/build/linux/sysroot_scripts/sysroot-creator.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator.sh
@@ -373,8 +373,13 @@
 
   # __GLIBC_MINOR__ is used as a feature test macro.  Replace it with the
   # earliest supported version of glibc (2.17, https://crbug.com/376567).
-  local features_h="${INSTALL_ROOT}/usr/include/features.h"
+  local usr_include="${INSTALL_ROOT}/usr/include"
+  local features_h="${usr_include}/features.h"
   sed -i 's|\(#define\s\+__GLIBC_MINOR__\)|\1 17 //|' "${features_h}"
+  # Do not use pthread_cond_clockwait as it was introduced in glibc 2.30.
+  local cppconfig_h="${usr_include}/${arch}-${os}/c++/10/bits/c++config.h"
+  sed -i 's|\(#define\s\+_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT\)|\1 0 //|' \
+    "${cppconfig_h}"
 
   # This is for chrome's ./build/linux/pkg-config-wrapper
   # which overwrites PKG_CONFIG_LIBDIR internally
diff --git a/build/linux/sysroot_scripts/sysroots.json b/build/linux/sysroot_scripts/sysroots.json
index 131f018..6817446 100644
--- a/build/linux/sysroot_scripts/sysroots.json
+++ b/build/linux/sysroot_scripts/sysroots.json
@@ -1,36 +1,36 @@
 {
     "bullseye_amd64": {
-        "Sha1Sum": "3c87f6561722292471f6d5e38df0e554f0f4f31c",
+        "Sha1Sum": "cdfde59c9724fd251d09effe07736ef0649c9c89",
         "SysrootDir": "debian_bullseye_amd64-sysroot",
         "Tarball": "debian_bullseye_amd64_sysroot.tar.xz"
     },
     "bullseye_arm": {
-        "Sha1Sum": "9ac32bc672a9210fb2719f271d80ecb361994cad",
+        "Sha1Sum": "66cfeb1864fb4975b58d08adb9c1e765f6a2c4a5",
         "SysrootDir": "debian_bullseye_arm-sysroot",
         "Tarball": "debian_bullseye_arm_sysroot.tar.xz"
     },
     "bullseye_arm64": {
-        "Sha1Sum": "3dc473ad845d3ae810c3e1be6f377e3eaa301c6e",
+        "Sha1Sum": "f4f28c646025c05f2f09c4efe785cf6e2cc2dfdd",
         "SysrootDir": "debian_bullseye_arm64-sysroot",
         "Tarball": "debian_bullseye_arm64_sysroot.tar.xz"
     },
     "bullseye_armel": {
-        "Sha1Sum": "68862fcbedaf908590162a44f2071070b880548c",
+        "Sha1Sum": "c83c9b7d2ea2f56c873482df5b0417c28ab11b40",
         "SysrootDir": "debian_bullseye_armel-sysroot",
         "Tarball": "debian_bullseye_armel_sysroot.tar.xz"
     },
     "bullseye_i386": {
-        "Sha1Sum": "efd2baea14d4aa1d7ee1fa2f8f590eda3d96c567",
+        "Sha1Sum": "6ab2930de84648fd79ee6f5c75f5701a9409fe93",
         "SysrootDir": "debian_bullseye_i386-sysroot",
         "Tarball": "debian_bullseye_i386_sysroot.tar.xz"
     },
     "bullseye_mips": {
-        "Sha1Sum": "92182dadfff51a92280e4bee1b17121c0d018306",
+        "Sha1Sum": "23527d3b509b2cbfa40a1b4560dcdda53ec2de0c",
         "SysrootDir": "debian_bullseye_mips-sysroot",
         "Tarball": "debian_bullseye_mips_sysroot.tar.xz"
     },
     "bullseye_mips64el": {
-        "Sha1Sum": "afdce1ae326eb695e41c72c945fc94d67f165c97",
+        "Sha1Sum": "d3972efa8edc0727b90ba3a9b953f23c23dba860",
         "SysrootDir": "debian_bullseye_mips64el-sysroot",
         "Tarball": "debian_bullseye_mips64el_sysroot.tar.xz"
     }
diff --git a/build/toolchain/apple/linker_driver.py b/build/toolchain/apple/linker_driver.py
index 68fb418..8aca2a7 100755
--- a/build/toolchain/apple/linker_driver.py
+++ b/build/toolchain/apple/linker_driver.py
@@ -59,8 +59,6 @@
 # -Wcrl,strippath,<strip_path>
 #    Sets the path to the strip to run with -Wcrl,strip, in which case
 #    `xcrun` is not used to invoke it.
-#-Wcrl,clean_object_path_lto,<path>
-#    Cleans up the temporary directory for LTO object files.
 
 
 class LinkerDriver(object):
@@ -86,9 +84,6 @@
             ('unstripped,', self.run_save_unstripped),
             ('strippath,', self.set_strip_path),
             ('strip,', self.run_strip),
-            # TODO(lgrey): Remove if/when we start running `dsymutil` through
-            # the clang driver. See https://crbug.com/1324104
-            ('clean_object_path_lto,', self.clean_obj_path_lto),
         ]
 
         # Linker driver actions can modify the these values.
@@ -315,21 +310,6 @@
         self._strip_cmd = [strip_path]
         return []
 
-    def clean_obj_path_lto(self, path):
-        """Linker driver action for -Wcrl,clean_object_path_lto,<path>.
-
-      Removes the directory at `path`, which is a temporary directory for
-      intermediate LTO object files. These need to persist long enough for
-      `dsymutil` to extract their debug info, and deleted afterwards.
-
-      Args:
-          path: string, The path to delete.
-      Returns:
-          No output - this step is run purely for its side-effect.
-      """
-        _remove_path(path)
-        return []
-
 
 def _find_tools_paths(full_args):
     """Finds all paths where the script should look for additional tools."""
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index fe1171b5f..3f57dbc 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -636,7 +636,8 @@
   if (!element_animations)
     return false;
 
-  return element_animations->HasAnyAnimationTargetingProperty(property);
+  return element_animations->HasAnyAnimationTargetingProperty(property,
+                                                              element_id);
 }
 
 bool AnimationHost::AnimationsPreserveAxisAlignment(
@@ -650,7 +651,7 @@
 float AnimationHost::MaximumScale(ElementId element_id,
                                   ElementListType list_type) const {
   if (auto element_animations = GetElementAnimationsForElementId(element_id))
-    return element_animations->MaximumScale(list_type);
+    return element_animations->MaximumScale(element_id, list_type);
   return kInvalidScale;
 }
 
diff --git a/cc/animation/animation_unittest.cc b/cc/animation/animation_unittest.cc
index 0454c9b..42203ab 100644
--- a/cc/animation/animation_unittest.cc
+++ b/cc/animation/animation_unittest.cc
@@ -428,10 +428,12 @@
   EXPECT_TRUE(animation_->keyframe_effect()->element_animations());
   EXPECT_FALSE(animation_->keyframe_effect()
                    ->element_animations()
-                   ->HasAnyAnimationTargetingProperty(TargetProperty::FILTER));
+                   ->HasAnyAnimationTargetingProperty(TargetProperty::FILTER,
+                                                      element_id_));
   EXPECT_TRUE(animation_->keyframe_effect()
                   ->element_animations()
-                  ->HasAnyAnimationTargetingProperty(TargetProperty::OPACITY));
+                  ->HasAnyAnimationTargetingProperty(TargetProperty::OPACITY,
+                                                     element_id_));
   EXPECT_TRUE(animation_->keyframe_effect()->needs_push_properties());
 
   host_->PushPropertiesTo(host_impl_, client_.GetPropertyTrees());
@@ -552,7 +554,7 @@
   EXPECT_EQ(
       base::StringPrintf("Animation{id=%d, element_id=%s, "
                          "keyframe_models=[KeyframeModel{id=42, "
-                         "group=73, target_property_type=1, "
+                         "group=73, target_property_type=4, "
                          "custom_property_name=, native_property_type=2, "
                          "run_state=WAITING_FOR_TARGET_AVAILABILITY, "
                          "element_id=(0)}]}",
@@ -565,10 +567,10 @@
   EXPECT_EQ(base::StringPrintf(
                 "Animation{id=%d, element_id=%s, "
                 "keyframe_models=[KeyframeModel{id=42, "
-                "group=73, target_property_type=1, custom_property_name=, "
+                "group=73, target_property_type=4, custom_property_name=, "
                 "native_property_type=2, "
                 "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}, "
-                "KeyframeModel{id=45, group=76, target_property_type=5, "
+                "KeyframeModel{id=45, group=76, target_property_type=8, "
                 "custom_property_name=, native_property_type=2, "
                 "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}]}",
                 animation_->id(), element_id_.ToString().c_str()),
diff --git a/cc/animation/element_animations.cc b/cc/animation/element_animations.cc
index 753e500..153e099 100644
--- a/cc/animation/element_animations.cc
+++ b/cc/animation/element_animations.cc
@@ -60,8 +60,10 @@
     : animation_host_(host),
       element_id_(element_id),
       needs_push_properties_(false),
-      active_maximum_scale_(kInvalidScale),
-      pending_maximum_scale_(kInvalidScale) {
+      transform_property_active_maximum_scale_(kInvalidScale),
+      transform_property_pending_maximum_scale_(kInvalidScale),
+      scale_property_active_maximum_scale_(kInvalidScale),
+      scale_property_pending_maximum_scale_(kInvalidScale) {
   InitAffectedElementTypes();
 }
 
@@ -77,6 +79,9 @@
 gfx::TargetProperties ElementAnimations::GetPropertiesMaskForAnimationState() {
   gfx::TargetProperties properties;
   properties[TargetProperty::TRANSFORM] = true;
+  properties[TargetProperty::SCALE] = true;
+  properties[TargetProperty::ROTATE] = true;
+  properties[TargetProperty::TRANSLATE] = true;
   properties[TargetProperty::OPACITY] = true;
   properties[TargetProperty::FILTER] = true;
   properties[TargetProperty::BACKDROP_FILTER] = true;
@@ -158,11 +163,12 @@
   return true;
 }
 
-float ElementAnimations::MaximumScale(ElementListType list_type) const {
+float ElementAnimations::MaximumScale(ElementId element_id,
+                                      ElementListType list_type) const {
   float maximum_scale = kInvalidScale;
   for (auto& keyframe_effect : keyframe_effects_list_) {
-    maximum_scale =
-        std::max(maximum_scale, keyframe_effect.MaximumScale(list_type));
+    maximum_scale = std::max(
+        maximum_scale, keyframe_effect.MaximumScale(element_id, list_type));
   }
   return maximum_scale;
 }
@@ -265,11 +271,34 @@
   // (instead of only changed) recalculated current states to the client.
   pending_state_.Clear();
   active_state_.Clear();
-  active_maximum_scale_ = kInvalidScale;
-  pending_maximum_scale_ = kInvalidScale;
+  transform_property_active_maximum_scale_ = kInvalidScale;
+  transform_property_pending_maximum_scale_ = kInvalidScale;
+  scale_property_active_maximum_scale_ = kInvalidScale;
+  scale_property_pending_maximum_scale_ = kInvalidScale;
   UpdateClientAnimationState();
 }
 
+void ElementAnimations::UpdateMaximumScale(ElementId element_id,
+                                           ElementListType list_type,
+                                           float* cached_scale) {
+  if (element_id) {
+    float maximum_scale = MaximumScale(element_id, list_type);
+    if (*cached_scale != maximum_scale) {
+      animation_host_->mutator_host_client()->MaximumScaleChanged(
+          element_id, list_type, maximum_scale);
+      *cached_scale = maximum_scale;
+    }
+  } else {
+    *cached_scale = kInvalidScale;
+  }
+}
+
+#if DCHECK_IS_ON()
+static inline bool IsInvalidOrOne(float scale) {
+  return scale == kInvalidScale || scale == 1.f;
+}
+#endif
+
 void ElementAnimations::UpdateClientAnimationState() {
   if (!element_id())
     return;
@@ -311,6 +340,11 @@
 
   PropertyToElementIdMap element_id_map = GetPropertyToElementIdMap();
   ElementId transform_element_id = element_id_map[TargetProperty::TRANSFORM];
+  ElementId scale_element_id = element_id_map[TargetProperty::SCALE];
+#if DCHECK_IS_ON()
+  ElementId rotate_element_id = element_id_map[TargetProperty::ROTATE];
+  ElementId translate_element_id = element_id_map[TargetProperty::TRANSLATE];
+#endif
 
   if (prev_active != active_state_) {
     PropertyAnimationState diff_active = prev_active ^ active_state_;
@@ -318,14 +352,16 @@
         element_id_map, ElementListType::ACTIVE, diff_active, active_state_);
   }
 
-  float maximum_scale = transform_element_id
-                            ? MaximumScale(ElementListType::ACTIVE)
-                            : kInvalidScale;
-  if (maximum_scale != active_maximum_scale_) {
-    animation_host_->mutator_host_client()->MaximumScaleChanged(
-        transform_element_id, ElementListType::ACTIVE, maximum_scale);
-    active_maximum_scale_ = maximum_scale;
-  }
+  UpdateMaximumScale(transform_element_id, ElementListType::ACTIVE,
+                     &transform_property_active_maximum_scale_);
+  UpdateMaximumScale(scale_element_id, ElementListType::ACTIVE,
+                     &scale_property_active_maximum_scale_);
+#if DCHECK_IS_ON()
+  DCHECK(
+      IsInvalidOrOne(MaximumScale(rotate_element_id, ElementListType::ACTIVE)));
+  DCHECK(IsInvalidOrOne(
+      MaximumScale(translate_element_id, ElementListType::ACTIVE)));
+#endif
 
   if (prev_pending != pending_state_) {
     PropertyAnimationState diff_pending = prev_pending ^ pending_state_;
@@ -333,13 +369,16 @@
         element_id_map, ElementListType::PENDING, diff_pending, pending_state_);
   }
 
-  maximum_scale = transform_element_id ? MaximumScale(ElementListType::PENDING)
-                                       : kInvalidScale;
-  if (maximum_scale != pending_maximum_scale_) {
-    animation_host_->mutator_host_client()->MaximumScaleChanged(
-        transform_element_id, ElementListType::PENDING, maximum_scale);
-    pending_maximum_scale_ = maximum_scale;
-  }
+  UpdateMaximumScale(transform_element_id, ElementListType::PENDING,
+                     &transform_property_pending_maximum_scale_);
+  UpdateMaximumScale(scale_element_id, ElementListType::PENDING,
+                     &scale_property_pending_maximum_scale_);
+#if DCHECK_IS_ON()
+  DCHECK(IsInvalidOrOne(
+      MaximumScale(rotate_element_id, ElementListType::PENDING)));
+  DCHECK(IsInvalidOrOne(
+      MaximumScale(translate_element_id, ElementListType::PENDING)));
+#endif
 }
 
 void ElementAnimations::AttachToCurve(gfx::AnimationCurve* c) {
@@ -386,10 +425,12 @@
 }
 
 bool ElementAnimations::HasAnyAnimationTargetingProperty(
-    TargetProperty::Type property) const {
+    TargetProperty::Type property,
+    ElementId element_id) const {
   for (auto& keyframe_effect : keyframe_effects_list_) {
-    if (keyframe_effect.GetKeyframeModel(property))
-      return true;
+    if (gfx::KeyframeModel* model = keyframe_effect.GetKeyframeModel(property))
+      if (CalculateTargetElementId(this, model) == element_id)
+        return true;
   }
   return false;
 }
diff --git a/cc/animation/element_animations.h b/cc/animation/element_animations.h
index 10e2565..bdede8e 100644
--- a/cc/animation/element_animations.h
+++ b/cc/animation/element_animations.h
@@ -80,7 +80,8 @@
   // Returns true if there are any KeyframeModels at all to process.
   bool HasAnyKeyframeModel() const;
 
-  bool HasAnyAnimationTargetingProperty(TargetProperty::Type property) const;
+  bool HasAnyAnimationTargetingProperty(TargetProperty::Type property,
+                                        ElementId element_id) const;
 
   // Returns true if there is an animation that is either currently animating
   // the given property or scheduled to animate this property in the future, and
@@ -98,7 +99,7 @@
   // Returns the maximum scale along any dimension at any destination in active
   // scale animations, or kInvalidScale if there is no active transform
   // animation or the scale cannot be computed.
-  float MaximumScale(ElementListType list_type) const;
+  float MaximumScale(ElementId element_id, ElementListType list_type) const;
 
   bool ScrollOffsetAnimationWasInterrupted() const;
 
@@ -187,6 +188,10 @@
 
   static gfx::TargetProperties GetPropertiesMaskForAnimationState();
 
+  void UpdateMaximumScale(ElementId element_id,
+                          ElementListType list_type,
+                          float* cached_scale);
+
   void UpdateKeyframeEffectsTickingState() const;
   void RemoveKeyframeEffectsFromTicking() const;
 
@@ -203,8 +208,10 @@
 
   PropertyAnimationState active_state_;
   PropertyAnimationState pending_state_;
-  float active_maximum_scale_;
-  float pending_maximum_scale_;
+  float transform_property_active_maximum_scale_;
+  float transform_property_pending_maximum_scale_;
+  float scale_property_active_maximum_scale_;
+  float scale_property_pending_maximum_scale_;
 };
 
 }  // namespace cc
diff --git a/cc/animation/element_animations_unittest.cc b/cc/animation/element_animations_unittest.cc
index 021004a..d947861b 100644
--- a/cc/animation/element_animations_unittest.cc
+++ b/cc/animation/element_animations_unittest.cc
@@ -2174,10 +2174,10 @@
   AttachTimelineAnimationLayer();
   CreateImplTimelineAndAnimation();
 
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::PENDING));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::ACTIVE));
 
   animation_impl_->AddKeyframeModel(
       CreateKeyframeModel(std::unique_ptr<gfx::AnimationCurve>(
@@ -2185,10 +2185,10 @@
                           1, TargetProperty::OPACITY));
 
   // Opacity animations aren't non-translation transforms.
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::PENDING));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::ACTIVE));
 
   std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve1(
       gfx::KeyframedTransformAnimationCurve::Create());
@@ -2206,10 +2206,10 @@
   animation_impl_->AddKeyframeModel(std::move(keyframe_model));
 
   // The only transform animation we've added is a translation.
-  EXPECT_EQ(1.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(1.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(1.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(1.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 }
 
 TEST_F(ElementAnimationsTest, MaximumAnimationNonCalculatableScale) {
@@ -2236,10 +2236,10 @@
 
   // All keyframes have perspective, so the ElementAnimations' scale is not
   // calculatable.
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::PENDING));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::ACTIVE));
 }
 
 TEST_F(ElementAnimationsTest, MaximumAnimationPartialNonCalculatableScale) {
@@ -2265,10 +2265,10 @@
 
   // Though some keyframes have perspective and the scale is not calculatable,
   // we use the other keyframes to calculate the ElementAnimations' scale.
-  EXPECT_EQ(2.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(2.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(2.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(2.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 }
 
 TEST_F(ElementAnimationsTest, MaximumScale) {
@@ -2293,16 +2293,16 @@
   keyframe_model->set_affects_active_elements(false);
   animation_impl_->AddKeyframeModel(std::move(keyframe_model));
 
-  EXPECT_EQ(5.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(5.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::ACTIVE));
 
   animation_impl_->ActivateKeyframeModels();
-  EXPECT_EQ(5.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(5.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(5.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(5.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve2(
       gfx::KeyframedTransformAnimationCurve::Create());
@@ -2344,25 +2344,25 @@
   keyframe_model->set_affects_active_elements(false);
   animation_impl_->AddKeyframeModel(std::move(keyframe_model));
 
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(kInvalidScale,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(kInvalidScale, element_animations_impl_->MaximumScale(
+                               element_id_, ElementListType::ACTIVE));
 
   animation_impl_->ActivateKeyframeModels();
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   animation_impl_->keyframe_effect()->GetKeyframeModelById(2)->SetRunState(
       KeyframeModel::FINISHED, TicksFromSecondsF(0.0));
 
   // Only unfinished animations should be considered by MaximumAnimationScale.
-  EXPECT_EQ(5.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(5.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(5.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(5.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 }
 
 TEST_F(ElementAnimationsTest, MaximumAnimationScaleWithDirection) {
@@ -2391,61 +2391,61 @@
 
   // NORMAL direction with positive playback rate.
   keyframe_model->set_direction(KeyframeModel::Direction::NORMAL);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   // ALTERNATE direction with positive playback rate.
   keyframe_model->set_direction(KeyframeModel::Direction::ALTERNATE_NORMAL);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   // REVERSE direction with positive playback rate.
   keyframe_model->set_direction(KeyframeModel::Direction::REVERSE);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   // ALTERNATE reverse direction.
   keyframe_model->set_direction(KeyframeModel::Direction::REVERSE);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   keyframe_model->set_playback_rate(-1.0);
 
   // NORMAL direction with negative playback rate.
   keyframe_model->set_direction(KeyframeModel::Direction::NORMAL);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   // ALTERNATE direction with negative playback rate.
   keyframe_model->set_direction(KeyframeModel::Direction::ALTERNATE_NORMAL);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   // REVERSE direction with negative playback rate.
   keyframe_model->set_direction(KeyframeModel::Direction::REVERSE);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 
   // ALTERNATE reverse direction with negative playback rate.
   keyframe_model->set_direction(KeyframeModel::Direction::REVERSE);
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::PENDING));
-  EXPECT_EQ(6.f,
-            element_animations_impl_->MaximumScale(ElementListType::ACTIVE));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::PENDING));
+  EXPECT_EQ(6.f, element_animations_impl_->MaximumScale(
+                     element_id_, ElementListType::ACTIVE));
 }
 
 TEST_F(ElementAnimationsTest, NewlyPushedAnimationWaitsForActivation) {
diff --git a/cc/animation/keyframe_effect.cc b/cc/animation/keyframe_effect.cc
index e9a10909..6e72d37b 100644
--- a/cc/animation/keyframe_effect.cc
+++ b/cc/animation/keyframe_effect.cc
@@ -443,7 +443,8 @@
   return true;
 }
 
-float KeyframeEffect::MaximumScale(ElementListType list_type) const {
+float KeyframeEffect::MaximumScale(ElementId element_id,
+                                   ElementListType list_type) const {
   float maximum_scale = kInvalidScale;
   for (const auto& keyframe_model : keyframe_models()) {
     if (keyframe_model->is_finished())
@@ -452,6 +453,12 @@
     auto* cc_keyframe_model =
         KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
 
+    ElementId model_element_id = cc_keyframe_model->element_id();
+    if (!model_element_id)
+      model_element_id = element_id_;
+    if (model_element_id != element_id)
+      continue;
+
     if ((list_type == ElementListType::ACTIVE &&
          !cc_keyframe_model->affects_active_elements()) ||
         (list_type == ElementListType::PENDING &&
diff --git a/cc/animation/keyframe_effect.h b/cc/animation/keyframe_effect.h
index 45cb8cf..1ce30cc 100644
--- a/cc/animation/keyframe_effect.h
+++ b/cc/animation/keyframe_effect.h
@@ -120,7 +120,7 @@
   // Returns the maximum scale along any dimension at any destination in active
   // scale animations, or kInvalidScale if there is no active transform
   // animation or the scale cannot be computed.
-  float MaximumScale(ElementListType) const;
+  float MaximumScale(ElementId, ElementListType) const;
 
   // Returns true if there is a keyframe_model that is either currently
   // animating the given property or scheduled to animate this property in the
diff --git a/cc/animation/keyframe_model_unittest.cc b/cc/animation/keyframe_model_unittest.cc
index 8d95bcda0..32edc756 100644
--- a/cc/animation/keyframe_model_unittest.cc
+++ b/cc/animation/keyframe_model_unittest.cc
@@ -1385,7 +1385,7 @@
       std::make_unique<FakeFloatAnimationCurve>(15), 42, 73,
       KeyframeModel::TargetPropertyId(TargetProperty::OPACITY));
   EXPECT_EQ(base::StringPrintf(
-                "KeyframeModel{id=%d, group=73, target_property_type=1, "
+                "KeyframeModel{id=%d, group=73, target_property_type=4, "
                 "custom_property_name=, native_property_type=2, "
                 "run_state=WAITING_FOR_TARGET_AVAILABILITY, element_id=(0)}",
                 keyframe_model->id()),
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index af9a0dbe..2db39bd 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1156,23 +1156,45 @@
                                              int transform_node_index) {
   // This includes all animations, even those that are finished but
   // haven't yet been deleted.
-  if (mutator_host()->HasAnyAnimationTargetingProperty(
-          element_id, TargetProperty::TRANSFORM)) {
-    TransformTree& transform_tree = property_trees()->transform_tree_mutable();
-    if (TransformNode* node = transform_tree.Node(transform_node_index)) {
-      ElementListType list_type = GetElementTypeForAnimation();
-      bool has_potential_animation =
-          mutator_host()->HasPotentiallyRunningAnimationForProperty(
-              element_id, list_type, TargetProperty::TRANSFORM);
-      if (node->has_potential_animation != has_potential_animation) {
-        node->has_potential_animation = has_potential_animation;
-        node->maximum_animation_scale =
-            mutator_host()->MaximumScale(element_id, list_type);
-        transform_tree.set_needs_update(true);
-        set_needs_update_draw_properties();
+
+  // A given ElementId should be associated with only a single transform
+  // property.  However, the ElementId is opaque to cc.  (If it comes from
+  // blink, it was constructed with a CompositorElementIdNamespace specific to
+  // the correct property.  Otherwise, only the transform property should be
+  // used.)
+  const TargetProperty::Type transform_properties[] = {
+      TargetProperty::TRANSFORM, TargetProperty::SCALE, TargetProperty::ROTATE,
+      TargetProperty::TRANSLATE};
+#if DCHECK_IS_ON()
+  unsigned property_count = 0u;
+#endif
+
+  for (TargetProperty::Type property : transform_properties) {
+    if (mutator_host()->HasAnyAnimationTargetingProperty(element_id,
+                                                         property)) {
+#if DCHECK_IS_ON()
+      ++property_count;
+#endif
+      TransformTree& transform_tree =
+          property_trees()->transform_tree_mutable();
+      if (TransformNode* node = transform_tree.Node(transform_node_index)) {
+        ElementListType list_type = GetElementTypeForAnimation();
+        bool has_potential_animation =
+            mutator_host()->HasPotentiallyRunningAnimationForProperty(
+                element_id, list_type, property);
+        if (node->has_potential_animation != has_potential_animation) {
+          node->has_potential_animation = has_potential_animation;
+          node->maximum_animation_scale =
+              mutator_host()->MaximumScale(element_id, list_type);
+          transform_tree.set_needs_update(true);
+          set_needs_update_draw_properties();
+        }
       }
     }
   }
+#if DCHECK_IS_ON()
+  DCHECK_LE(property_count, 1u);
+#endif
 }
 
 void LayerTreeImpl::UpdatePageScaleNode() {
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 5a2ac99..4e8eaad0 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1964,6 +1964,9 @@
     const ElementId element_id = it->second;
     switch (property) {
       case TargetProperty::TRANSFORM:
+      case TargetProperty::SCALE:
+      case TargetProperty::ROTATE:
+      case TargetProperty::TRANSLATE:
         if (TransformNode* transform_node =
                 transform_tree_mutable().FindNodeFromElementId(element_id)) {
           if (mask.currently_running[property])
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 23efda7..4cf9508 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -143,6 +143,16 @@
 }
 
 bool TransformIsAnimating(const MutatorHost& host, Layer* layer) {
+  DCHECK(!host.IsAnimatingProperty(layer->element_id(),
+                                   layer->GetElementTypeForAnimation(),
+                                   TargetProperty::SCALE) &&
+         !host.IsAnimatingProperty(layer->element_id(),
+                                   layer->GetElementTypeForAnimation(),
+                                   TargetProperty::ROTATE) &&
+         !host.IsAnimatingProperty(layer->element_id(),
+                                   layer->GetElementTypeForAnimation(),
+                                   TargetProperty::TRANSLATE))
+      << "individual transform properties only supported in layer lists mode";
   return host.IsAnimatingProperty(layer->element_id(),
                                   layer->GetElementTypeForAnimation(),
                                   TargetProperty::TRANSFORM);
@@ -150,6 +160,16 @@
 
 bool HasPotentiallyRunningTransformAnimation(const MutatorHost& host,
                                              Layer* layer) {
+  DCHECK(!host.HasPotentiallyRunningAnimationForProperty(
+             layer->element_id(), layer->GetElementTypeForAnimation(),
+             TargetProperty::SCALE) &&
+         !host.HasPotentiallyRunningAnimationForProperty(
+             layer->element_id(), layer->GetElementTypeForAnimation(),
+             TargetProperty::ROTATE) &&
+         !host.HasPotentiallyRunningAnimationForProperty(
+             layer->element_id(), layer->GetElementTypeForAnimation(),
+             TargetProperty::TRANSLATE))
+      << "individual transform properties only supported in layer lists mode";
   return host.HasPotentiallyRunningAnimationForProperty(
       layer->element_id(), layer->GetElementTypeForAnimation(),
       TargetProperty::TRANSFORM);
@@ -242,6 +262,13 @@
   // the Running state right after commit on the compositor thread.
   const bool has_any_transform_animation = HasAnyAnimationTargetingProperty(
       mutator_host_, layer, TargetProperty::TRANSFORM);
+  DCHECK(!HasAnyAnimationTargetingProperty(mutator_host_, layer,
+                                           TargetProperty::SCALE) &&
+         !HasAnyAnimationTargetingProperty(mutator_host_, layer,
+                                           TargetProperty::ROTATE) &&
+         !HasAnyAnimationTargetingProperty(mutator_host_, layer,
+                                           TargetProperty::TRANSLATE))
+      << "individual transform properties only supported in layer lists mode";
 
   const bool has_surface = created_render_surface;
 
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index 9d04e0d1e..165278b 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -418,7 +418,7 @@
                            TRACE_EVENT_FLAG_FLOW_IN);
     TRACE_EVENT_WITH_FLOW0(
         "viz,benchmark", "MainFrame.NotifyReadyToCommitOnMain",
-        TRACE_ID_LOCAL(commit_state->trace_id), TRACE_EVENT_FLAG_FLOW_OUT)
+        TRACE_ID_LOCAL(commit_state->trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
   }
   {
     TRACE_EVENT0("cc,raf_investigation", "ProxyMain::BeginMainFrame::commit");
diff --git a/cc/trees/target_property.h b/cc/trees/target_property.h
index a60b371..184e1b74 100644
--- a/cc/trees/target_property.h
+++ b/cc/trees/target_property.h
@@ -14,6 +14,9 @@
 // Must be zero-based as this will be stored in a bitset.
 enum Type {
   TRANSFORM = 0,
+  SCALE,
+  ROTATE,
+  TRANSLATE,
   OPACITY,
   FILTER,
   SCROLL_OFFSET,
diff --git a/cc/trees/transform_node.h b/cc/trees/transform_node.h
index b6177e0..e3e7ca9 100644
--- a/cc/trees/transform_node.h
+++ b/cc/trees/transform_node.h
@@ -112,7 +112,8 @@
   // visibility, not this transform one.
   bool delegates_to_parent_for_backface : 1;
 
-  // Set to true, if the compositing reason is will-change:transform.
+  // Set to true, if the compositing reason is will-change:transform, scale,
+  // rotate, or translate (for the CSS property that created this node).
   bool will_change_transform : 1;
 
   // Set to true, if the node or it's parent |will_change_transform| is true.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java
index 1f7f996..521f867e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java
@@ -344,9 +344,6 @@
     @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
     public void testChainedSearchCreatesNewContent(@EnabledFeature int enabledFeature)
             throws Exception {
-        // This flakes when running the Translations Feature, probably due to DOMUtils issues.
-        if (enabledFeature == EnabledFeature.TRANSLATIONS) return;
-
         // This test depends on preloading the content - which is loaded and not made visible.
         // We only preload when the user has decided to accept the privacy opt-in.
         mPolicy.overrideDecidedStateForTesting(true);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
index d67faf99..a4dc78e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
@@ -424,6 +424,9 @@
         mTestServer = sActivityTestRule.getTestServer();
 
         sActivityTestRule.loadUrl(mTestServer.getURL(mTestPage));
+        // DOMUtils sometimes hits the wrong node due to an incorrect page scale factor,
+        // so wait until that is set. https://crbug.com/1327063
+        sActivityTestRule.assertWaitForPageScaleFactorMatch(1.0f);
 
         mManager = sActivityTestRule.getActivity().getContextualSearchManagerSupplier().get();
         mTestHost = new ContextualSearchInstrumentationTestHost();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java
index dbf11d13..6f1eb97 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediatorTest.java
@@ -6,7 +6,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.geq;
 import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
@@ -52,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /** Unit tests for HistoryClustersMediator. */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -93,6 +96,8 @@
     private Function<GURL, Intent> mUrlIntentCreator;
     @Mock
     private Drawable mDrawable;
+    @Mock
+    private HistoryClustersMediator.Clock mClock;
 
     private ClusterVisit mVisit1;
     private ClusterVisit mVisit2;
@@ -119,14 +124,14 @@
         mToolbarModel = new PropertyModel(HistoryClustersToolbarProperties.ALL_KEYS);
         mMediator = new HistoryClustersMediator(mBridge, mLargeIconBridge, mContext, mResources,
                 mModelList, mToolbarModel, mHistoryActivityIntentFactory, mTabSupplier, false,
-                mUrlIntentCreator);
+                mUrlIntentCreator, mClock);
         mVisit1 = new ClusterVisit(1.0F, mGurl1, "Title 1");
-        mVisit2 = new ClusterVisit(1.0F, mGurl2, "Title 1");
-        mVisit3 = new ClusterVisit(1.0F, mGurl3, "Title 1");
-        mCluster1 = new HistoryCluster(
-                Arrays.asList("foo"), Arrays.asList(mVisit1, mVisit2), "label1", new ArrayList<>());
-        mCluster2 = new HistoryCluster(
-                Arrays.asList("bar", "baz"), Arrays.asList(mVisit3), "label2", new ArrayList<>());
+        mVisit2 = new ClusterVisit(1.0F, mGurl2, "Title 2");
+        mVisit3 = new ClusterVisit(1.0F, mGurl3, "Title 3");
+        mCluster1 = new HistoryCluster(Arrays.asList("foo"), Arrays.asList(mVisit1, mVisit2),
+                "label1", new ArrayList<>(), 456L);
+        mCluster2 = new HistoryCluster(Arrays.asList("bar", "baz"), Arrays.asList(mVisit3),
+                "label2", new ArrayList<>(), 123L);
         mHistoryClustersResultWithQuery = new HistoryClustersResult(
                 Arrays.asList(mCluster1, mCluster2), "query", false, false);
         mHistoryClustersResultEmptyQuery =
@@ -178,7 +183,7 @@
         assertEquals(item.type, ItemType.CLUSTER);
         PropertyModel model = item.model;
         assertTrue(model.getAllSetProperties().containsAll(Arrays.asList(
-                HistoryClustersItemProperties.CLICK_HANDLER, HistoryClustersItemProperties.LABEL)));
+                HistoryClustersItemProperties.CLICK_HANDLER, HistoryClustersItemProperties.TITLE)));
     }
 
     @Test
@@ -237,7 +242,7 @@
     public void testNavigateSeparateActivity() {
         HistoryClustersMediator standaloneMediator = new HistoryClustersMediator(mBridge,
                 mLargeIconBridge, mContext, mResources, mModelList, mToolbarModel,
-                mHistoryActivityIntentFactory, mTabSupplier, true, mUrlIntentCreator);
+                mHistoryActivityIntentFactory, mTabSupplier, true, mUrlIntentCreator, mClock);
         standaloneMediator.navigateToItemUrl(mMockGurl);
 
         verify(mUrlIntentCreator).apply(mMockGurl);
@@ -263,6 +268,41 @@
         assertEquals(visitModel2.get(HistoryClustersItemProperties.VISIBILITY), View.VISIBLE);
     }
 
+    @Test
+    public void testGetTimeString() {
+        String dayString = "1 day ago";
+        String hourString = "1 hour ago";
+        String minuteString = "1 minute ago";
+        String justNowString = "Just now";
+
+        doReturn(dayString)
+                .when(mResources)
+                .getQuantityString(eq(R.plurals.n_days_ago), geq(1), geq(1));
+        doReturn(hourString)
+                .when(mResources)
+                .getQuantityString(eq(R.plurals.n_hours_ago), geq(1), geq(1));
+        doReturn(minuteString)
+                .when(mResources)
+                .getQuantityString(eq(R.plurals.n_minutes_ago), geq(1), geq(1));
+        doReturn(justNowString).when(mResources).getString(R.string.just_now);
+
+        doReturn(TimeUnit.DAYS.toMillis(1)).when(mClock).currentTimeMillis();
+        String timeString = mMediator.getTimeString(0L);
+        assertEquals(timeString, dayString);
+
+        doReturn(TimeUnit.HOURS.toMillis(1)).when(mClock).currentTimeMillis();
+        timeString = mMediator.getTimeString(0L);
+        assertEquals(timeString, hourString);
+
+        doReturn(TimeUnit.MINUTES.toMillis(1)).when(mClock).currentTimeMillis();
+        timeString = mMediator.getTimeString(0L);
+        assertEquals(timeString, minuteString);
+
+        doReturn(TimeUnit.SECONDS.toMillis(1)).when(mClock).currentTimeMillis();
+        timeString = mMediator.getTimeString(0L);
+        assertEquals(timeString, justNowString);
+    }
+
     private <T> void fulfillPromise(Promise<T> promise, T result) {
         promise.fulfill(result);
         ShadowLooper.idleMainLooper();
diff --git a/chrome/android/static_initializers.gni b/chrome/android/static_initializers.gni
index b0c2d5e..76b45c8 100644
--- a/chrome/android/static_initializers.gni
+++ b/chrome/android/static_initializers.gni
@@ -21,15 +21,11 @@
   # Comments show static_initializers according to
   # tools/linux/dump-static-initializers.py.
 
-  # iostream.cpp (initializer offset 0x142e930 size 0x2)
-  #   [empty ctor, but it still has cost on gcc <4.6]
-  expected_static_initializer_count = 1
-
   # TODO(https://crbug.com/1177849): Remove from tflite:
   # tflite_engine.cc (initializer offset 0x34cb88c size 0x2c)
   #   __ThumbV7PILongThunk__ZNSt3__18ios_base4InitC1Ev
   #   __ThumbV7PILongThunk___cxa_atexit
-  expected_static_initializer_count += 1
+  expected_static_initializer_count = 1
 
   # From v8 I think:
   # token.cc (initializer offset 0x27f024c size 0x11c)
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index be62a1d..4fc8a2c4 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -85,7 +85,7 @@
 
 #include <algorithm>
 
-#include "base/debug/close_handle_hook_win.h"
+#include "base/debug/handle_hooks_win.h"
 #include "base/files/important_file_writer_cleaner.h"
 #include "base/threading/platform_thread_win.h"
 #include "base/win/atl.h"
@@ -807,8 +807,8 @@
     *exit_code = 0;
     return true;  // Got a --version switch; exit with a success error code.
   }
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
+  // TODO(crbug.com/1052397): Revisit the macro expression once build flag
+  // switch of lacros-chrome is complete.
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   // This will directly exit if the user asked for help.
   HandleHelpSwitches(command_line);
@@ -822,11 +822,25 @@
     return true;
   }
 
-// HandleVerifier detects and reports incorrect handle manipulations. It tracks
-// handle operations on builds that support DCHECK only.
-// TODO(crbug/1104358): Support 64-bit handle hooks.
-#if DCHECK_IS_ON() && !defined(ARCH_CPU_64_BITS)
-  base::debug::InstallHandleHooks();
+  // HandleVerifier detects and reports incorrect handle manipulations. It
+  // tracks handle operations on builds that support DCHECK only.
+#if DCHECK_IS_ON()
+  // This portion of the hook setup is just for child processes. Browser part is
+  // in ChromeBrowserMainPartsWin::PostProfileInit.
+  if (!is_browser) {
+    // Performing EAT interception first is safer in the presence of other
+    // threads attempting to call CloseHandle.
+#if defined(ARCH_CPU_32_BITS)
+    // Patching EAT of kernel32.dll is only supported on 32-bit because RVA can
+    // only hold 32-bit values.
+    base::debug::HandleHooks::AddEATPatch();
+#endif
+    // Patch once. Cannot monitor for further modules in a child process as
+    // monitoring needs ModuleWatcher, but likely no more should really load in
+    // a child process from this point on. If we miss any then we will lose some
+    // detection but still generate no false positive crashes.
+    base::debug::HandleHooks::PatchLoadedModules();
+  }
 #else
   base::win::DisableHandleVerifier();
 #endif
diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h
index 2ac5fec7..2e666b3 100644
--- a/chrome/browser/app_controller_mac.h
+++ b/chrome/browser/app_controller_mac.h
@@ -82,7 +82,7 @@
 
   std::unique_ptr<TabMenuBridge> _tabMenuBridge;
 
-  // If we're told to open URLs (in particular, via |-application:openFiles:| by
+  // If we're told to open URLs (in particular, via |-application:openURLs:| by
   // Launch Services) before we've launched the browser, we queue them up in
   // |startupUrls_| so that they can go in the first browser window/tab.
   std::vector<GURL> _startupUrls;
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 43ba3f2..9163dc8 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -331,14 +331,13 @@
 
 // Returns the profile path to be used at startup.
 base::FilePath GetStartupProfilePathMac() {
-  // This profile path is used to open URLs passed in application:openFiles: and
+  // This profile path is used to open URLs passed in application:openURLs: and
   // should not default to Guest when the profile picker is shown.
   // TODO(https://crbug.com/1155158): Remove the ignore_profile_picker parameter
   // once the picker supports opening URLs.
-  StartupProfilePathInfo profile_path_info =
-      GetStartupProfilePath(/*current_directory=*/base::FilePath(),
-                            *base::CommandLine::ForCurrentProcess(),
-                            /*ignore_profile_picker=*/true);
+  StartupProfilePathInfo profile_path_info = GetStartupProfilePath(
+      /*cur_dir=*/base::FilePath(), *base::CommandLine::ForCurrentProcess(),
+      /*ignore_profile_picker=*/true);
   DCHECK_EQ(profile_path_info.mode, StartupProfileMode::kBrowserWindow);
   return profile_path_info.path;
 }
@@ -372,8 +371,6 @@
 - (void)initProfileMenu;
 - (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item;
 - (void)registerServicesMenuTypesTo:(NSApplication*)app;
-- (void)getUrl:(NSAppleEventDescriptor*)event
-     withReply:(NSAppleEventDescriptor*)reply;
 - (void)activeSpaceDidChange:(NSNotification*)inNotification;
 - (void)checkForAnyKeyWindows;
 - (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
@@ -530,17 +527,6 @@
 - (void)mainMenuCreated {
   MacStartupProfiler::GetInstance()->Profile(
       MacStartupProfiler::AWAKE_FROM_NIB);
-  // We need to register the handlers early to catch events fired on launch.
-  NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
-  [em setEventHandler:self
-          andSelector:@selector(getUrl:withReply:)
-        forEventClass:kInternetEventClass
-           andEventID:kAEGetURL];
-  [em setEventHandler:self
-          andSelector:@selector(getUrl:withReply:)
-        forEventClass:'WWW!'    // A particularly ancient AppleEvent that dates
-           andEventID:'OURL'];  // back to the Spyglass days.
-
   NSNotificationCenter* notificationCenter =
       [NSNotificationCenter defaultCenter];
   [notificationCenter
@@ -581,11 +567,6 @@
 }
 
 - (void)unregisterEventHandlers {
-  NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
-  [em removeEventHandlerForEventClass:kInternetEventClass
-                           andEventID:kAEGetURL];
-  [em removeEventHandlerForEventClass:'WWW!'
-                           andEventID:'OURL'];
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
 }
@@ -598,18 +579,6 @@
 
   NSWindow.allowsAutomaticWindowTabbing = NO;
 
-  // If the OSX version supports this method, the system will automatically
-  // hide the item if there's no touch bar. However, for unsupported versions,
-  // we'll have to manually remove the item from the menu.
-  if (![NSApp
-          respondsToSelector:@selector(toggleTouchBarCustomizationPalette:)]) {
-    NSMenu* mainMenu = [NSApp mainMenu];
-    NSMenu* viewMenu = [[mainMenu itemWithTag:IDC_VIEW_MENU] submenu];
-    NSMenuItem* customizeItem = [viewMenu itemWithTag:IDC_CUSTOMIZE_TOUCH_BAR];
-    if (customizeItem)
-      [viewMenu removeItem:customizeItem];
-  }
-
   [self initShareMenu];
 }
 
@@ -1003,28 +972,28 @@
   std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
 
   std::vector<Profile*> added_profiles;
-  for (Profile* p : profiles) {
-    for (Profile* otr : p->GetAllOffTheRecordProfiles())
+  for (Profile* profile : profiles) {
+    for (Profile* otr : profile->GetAllOffTheRecordProfiles())
       added_profiles.push_back(otr);
   }
   profiles.insert(profiles.end(), added_profiles.begin(), added_profiles.end());
 
-  for (size_t i = 0; i < profiles.size(); ++i) {
+  for (Profile* profile : profiles) {
     DownloadCoreService* download_core_service =
-        DownloadCoreServiceFactory::GetForBrowserContext(profiles[i]);
+        DownloadCoreServiceFactory::GetForBrowserContext(profile);
     content::DownloadManager* download_manager =
         (download_core_service->HasCreatedDownloadManager()
-             ? profiles[i]->GetDownloadManager()
-             : NULL);
+             ? profile->GetDownloadManager()
+             : nullptr);
     if (download_manager &&
         download_manager->NonMaliciousInProgressCount() > 0) {
       int downloadCount = download_manager->NonMaliciousInProgressCount();
       if ([self userWillWaitForInProgressDownloads:downloadCount]) {
         // Create a new browser window (if necessary) and navigate to the
         // downloads page if the user chooses to wait.
-        Browser* browser = chrome::FindBrowserWithProfile(profiles[i]);
+        Browser* browser = chrome::FindBrowserWithProfile(profile);
         if (!browser) {
-          browser = Browser::Create(Browser::CreateParams(profiles[i], true));
+          browser = Browser::Create(Browser::CreateParams(profile, true));
           browser->window()->Show();
         }
         DCHECK(browser);
@@ -1354,7 +1323,6 @@
                     hasVisibleWindows:(BOOL)hasVisibleWindows {
   // If the browser is currently trying to quit, don't do anything and return NO
   // to prevent AppKit from doing anything.
-  // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
   if (browser_shutdown::IsTryingToQuit())
     return NO;
 
@@ -1568,38 +1536,6 @@
   return ProfileManager::MaybeForceOffTheRecordMode(profile);
 }
 
-- (void)getUrl:(NSAppleEventDescriptor*)event
-     withReply:(NSAppleEventDescriptor*)reply {
-  NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
-                      stringValue];
-
-  GURL gurl(base::SysNSStringToUTF8(urlStr));
-  std::vector<GURL> gurlVector;
-  gurlVector.push_back(gurl);
-
-  [self openUrlsReplacingNTP:gurlVector];
-}
-
-- (void)application:(NSApplication*)sender
-          openFiles:(NSArray*)filenames {
-  std::vector<GURL> gurlVector;
-  for (NSString* file in filenames) {
-    GURL gurl =
-        net::FilePathToFileURL(base::FilePath([file fileSystemRepresentation]));
-    gurlVector.push_back(gurl);
-  }
-
-  if (!gurlVector.empty())
-    [self openUrlsReplacingNTP:gurlVector];
-  else
-    NOTREACHED() << "Nothing to open!";
-
-  [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
-}
-
-// TODO(avi): When Chromium requires 10.13 as a minimum, remove the
-// -[NSApplication application:openFiles:] override and the
-// kInternetEventClass/kAEGetURL Apple Event registration in -mainMenuCreated.
 - (void)application:(NSApplication*)sender openURLs:(NSArray<NSURL*>*)urls {
   std::vector<GURL> gurlVector;
   for (NSURL* url in urls)
@@ -1607,10 +1543,6 @@
 
   if (!gurlVector.empty())
     [self openUrlsReplacingNTP:gurlVector];
-  else
-    NOTREACHED() << "Nothing to open!";
-
-  [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
 }
 
 // Show the preferences window, or bring it to the front if it's already
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm
index 2bcc295f..b051a1a 100644
--- a/chrome/browser/app_controller_mac_browsertest.mm
+++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
 #import <Foundation/Foundation.h>
-#import <Foundation/NSAppleEventDescriptor.h>
-#import <objc/message.h>
-#import <objc/runtime.h>
 #include <stddef.h>
 
 #include <string>
@@ -79,6 +75,7 @@
 #include "extensions/browser/extension_dialog_auto_confirm.h"
 #include "extensions/common/extension.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "net/base/mac/url_conversions.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/blink/public/common/features.h"
@@ -87,37 +84,13 @@
 #include "ui/views/widget/any_widget_observer.h"
 #include "ui/views/widget/widget.h"
 
-using base::SysUTF16ToNSString;
-
-@interface AppController (ForTesting)
-- (void)getUrl:(NSAppleEventDescriptor*)event
-     withReply:(NSAppleEventDescriptor*)reply;
-@end
-
 namespace {
 
 GURL g_open_shortcut_url = GURL::EmptyGURL();
 
-// Returns an Apple Event that instructs the application to open |url|.
-NSAppleEventDescriptor* AppleEventToOpenUrl(const GURL& url) {
-  NSAppleEventDescriptor* shortcut_event = [[[NSAppleEventDescriptor alloc]
-      initWithEventClass:kASAppleScriptSuite
-                 eventID:kASSubroutineEvent
-        targetDescriptor:nil
-                returnID:kAutoGenerateReturnID
-           transactionID:kAnyTransactionID] autorelease];
-  NSString* url_string = base::SysUTF8ToNSString(url.spec());
-  [shortcut_event setParamDescriptor:[NSAppleEventDescriptor
-                                         descriptorWithString:url_string]
-                          forKeyword:keyDirectObject];
-  return shortcut_event;
-}
-
 // Instructs the NSApp's delegate to open |url|.
-void SendAppleEventToOpenUrlToAppController(const GURL& url) {
-  AppController* controller =
-      base::mac::ObjCCast<AppController>([NSApp delegate]);
-  [controller getUrl:AppleEventToOpenUrl(url) withReply:nullptr];
+void SendOpenUrlToAppController(const GURL& url) {
+  [NSApp.delegate application:NSApp openURLs:@[ net::NSURLWithGURL(url) ]];
 }
 
 void RunClosureWhenProfileInitialized(const base::RepeatingClosure& closure,
@@ -199,7 +172,7 @@
   if (!g_open_shortcut_url.is_valid())
     return;
 
-  SendAppleEventToOpenUrlToAppController(g_open_shortcut_url);
+  SendOpenUrlToAppController(g_open_shortcut_url);
 }
 
 @end
@@ -705,8 +678,8 @@
     Method destination = class_getInstanceMethod(openShortcutClass,
         targetMethod);
 
-    ASSERT_TRUE(original != NULL);
-    ASSERT_TRUE(destination != NULL);
+    ASSERT_TRUE(original);
+    ASSERT_TRUE(destination);
 
     method_exchangeImplementations(original, destination);
 
@@ -735,7 +708,7 @@
 
 class AppControllerReplaceNTPBrowserTest : public InProcessBrowserTest {
  protected:
-  AppControllerReplaceNTPBrowserTest() {}
+  AppControllerReplaceNTPBrowserTest() = default;
 
   void SetUpInProcessBrowserTestFixture() override {
     ASSERT_TRUE(embedded_test_server()->Start());
@@ -777,7 +750,7 @@
                 ->GetLastCommittedURL());
 
   GURL simple(embedded_test_server()->GetURL("/simple.html"));
-  SendAppleEventToOpenUrlToAppController(simple);
+  SendOpenUrlToAppController(simple);
 
   EXPECT_EQ(1, browser()->tab_strip_model()->count());
   content::TestNavigationObserver event_navigation_observer(
@@ -801,7 +774,7 @@
   EXPECT_EQ(1, incognito_browser->tab_strip_model()->count());
   // Open a url.
   GURL simple(embedded_test_server()->GetURL("/simple.html"));
-  SendAppleEventToOpenUrlToAppController(simple);
+  SendOpenUrlToAppController(simple);
   // It should be opened in the regular browser.
   content::TestNavigationObserver event_navigation_observer(
       browser()->tab_strip_model()->GetActiveWebContents());
@@ -816,8 +789,7 @@
 
 class AppControllerMainMenuBrowserTest : public InProcessBrowserTest {
  protected:
-  AppControllerMainMenuBrowserTest() {
-  }
+  AppControllerMainMenuBrowserTest() = default;
 };
 
 IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
@@ -979,7 +951,7 @@
   base::ScopedAllowBlockingForTesting allow_blocking;
   base::FilePath path2 = profile_manager->GenerateNextProfileDirectoryPath();
   std::unique_ptr<Profile> profile2 =
-      Profile::CreateProfile(path2, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
+      Profile::CreateProfile(path2, nullptr, Profile::CREATE_MODE_SYNCHRONOUS);
   Profile* profile2_ptr = profile2.get();
   profile_manager->RegisterTestingProfile(std::move(profile2), false);
   bookmarks::test::WaitForBookmarkModelToLoad(
@@ -1003,19 +975,19 @@
   EXPECT_NE(profile1_submenu, profile2_submenu);
 
   // Test that only bookmark 2 is shown.
-  EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
-      SysUTF16ToNSString(title1)]);
-  EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
-      SysUTF16ToNSString(title2)]);
+  EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu()
+      itemWithTitle:base::SysUTF16ToNSString(title1)]);
+  EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu()
+      itemWithTitle:base::SysUTF16ToNSString(title2)]);
 
   // Switch *back* to profile 1 and *don't* force the menu to build.
   [ac setLastProfile:profile1];
 
   // Test that only bookmark 1 is shown in the restored menu.
-  EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
-      SysUTF16ToNSString(title1)]);
-  EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
-      SysUTF16ToNSString(title2)]);
+  EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu()
+      itemWithTitle:base::SysUTF16ToNSString(title1)]);
+  EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu()
+      itemWithTitle:base::SysUTF16ToNSString(title2)]);
 
   // Ensure a cached menu was used.
   EXPECT_EQ(profile1_submenu, [ac bookmarkMenuBridge]->BookmarkMenu());
diff --git a/chrome/browser/apps/app_discovery_service/game_fetcher.cc b/chrome/browser/apps/app_discovery_service/game_fetcher.cc
index 0882d9cc5..232d42f 100644
--- a/chrome/browser/apps/app_discovery_service/game_fetcher.cc
+++ b/chrome/browser/apps/app_discovery_service/game_fetcher.cc
@@ -176,14 +176,15 @@
     return;
   }
 
-  Result* app = nullptr;
-
-  for (Result& candidate : last_results_) {
-    if (candidate.GetAppId() == app_id) {
-      app = &candidate;
-    }
+  auto iterator = app_id_to_result_.find(app_id);
+  if (iterator == app_id_to_result_.end()) {
+    std::move(callback).Run(gfx::ImageSkia(),
+                            DiscoveryError::kErrorRequestFailed);
+    return;
   }
 
+  Result* app = iterator->second;
+
   if (!app) {
     std::move(callback).Run(gfx::ImageSkia(),
                             DiscoveryError::kErrorRequestFailed);
@@ -204,6 +205,11 @@
 
 void GameFetcher::OnAppDataUpdated(const proto::AppWithLocaleList& app_data) {
   last_results_ = GetAppsForCurrentLocale(app_data);
+  std::map<std::string, Result*> map;
+  for (auto& result : last_results_) {
+    map.emplace(result.GetAppId(), &result);
+  }
+  app_id_to_result_ = map;
   result_callback_list_.Notify(last_results_);
 }
 
diff --git a/chrome/browser/apps/app_discovery_service/game_fetcher.h b/chrome/browser/apps/app_discovery_service/game_fetcher.h
index 8618100..d28078f 100644
--- a/chrome/browser/apps/app_discovery_service/game_fetcher.h
+++ b/chrome/browser/apps/app_discovery_service/game_fetcher.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_APPS_APP_DISCOVERY_SERVICE_GAME_FETCHER_H_
 #define CHROME_BROWSER_APPS_APP_DISCOVERY_SERVICE_GAME_FETCHER_H_
 
+#include <map>
+
 #include "base/callback_list.h"
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
@@ -41,6 +43,10 @@
 
   std::vector<Result> last_results_;
 
+  // The key for this map is the App ID, while the value is a pointer to a
+  // Result in the last_results_ vector.
+  std::map<std::string, Result*> app_id_to_result_;
+
   raw_ptr<Profile> profile_;
 
   ResultCallbackList result_callback_list_;
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index d841808..25a05be 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1635,6 +1635,25 @@
 
 IN_PROC_BROWSER_TEST_P(WebViewNewWindowTest, Shim_TestNewWindow) {
   TestHelper("testNewWindow", "web_view/shim", NEEDS_TEST_SERVER);
+
+  // The first <webview> tag in the test will run window.open(), which the
+  // embedder will translate into an injected second <webview> tag.  Ensure
+  // that the two <webview>'s remain in the same BrowsingInstance and
+  // StoragePartition.
+  GetGuestViewManager()->WaitForNumGuestsCreated(2);
+  std::vector<content::WebContents*> guest_contents_list;
+  GetGuestViewManager()->GetGuestWebContentsList(&guest_contents_list);
+  ASSERT_EQ(2u, guest_contents_list.size());
+  content::WebContents* guest1 = guest_contents_list[0];
+  content::WebContents* guest2 = guest_contents_list[1];
+  ASSERT_NE(guest1, guest2);
+  auto* guest_instance1 = guest1->GetMainFrame()->GetSiteInstance();
+  auto* guest_instance2 = guest2->GetMainFrame()->GetSiteInstance();
+  EXPECT_TRUE(guest_instance1->IsGuest());
+  EXPECT_TRUE(guest_instance2->IsGuest());
+  EXPECT_EQ(guest_instance1->GetStoragePartitionConfig(),
+            guest_instance2->GetStoragePartitionConfig());
+  EXPECT_TRUE(guest_instance1->IsRelatedSiteInstance(guest_instance2));
 }
 
 IN_PROC_BROWSER_TEST_P(WebViewNewWindowTest, Shim_TestNewWindowTwoListeners) {
@@ -1673,12 +1692,15 @@
   EXPECT_EQ(guest_instance1->GetStoragePartitionConfig(),
             guest_instance2->GetStoragePartitionConfig());
 
-  // Until <webview> guests have site isolation, both guests should be in the
-  // same SiteInstance, even in this `opener_suppressed` case which typically
-  // places the new window in a new BrowsingInstance.
-  //
-  // TODO(alexmos): revisit this once <webview> guests support site isolation.
-  EXPECT_EQ(guest_instance1, guest_instance2);
+  // Without <webview> site isolation, both guests should be in the same
+  // SiteInstance, even in this `opener_suppressed` case which typically places
+  // the new window in a new BrowsingInstance. With site isolation, the new
+  // guest should be in a different BrowsingInstance.
+  if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) {
+    EXPECT_FALSE(guest_instance1->IsRelatedSiteInstance(guest_instance2));
+  } else {
+    EXPECT_EQ(guest_instance1, guest_instance2);
+  }
 
   // Check that the source SiteInstance used when the first guest opened the
   // new noreferrer window is also a guest SiteInstance in the same
diff --git a/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc b/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc
index 88f0f7f..7aa42c9 100644
--- a/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc
+++ b/chrome/browser/ash/arc/intent_helper/arc_settings_service.cc
@@ -182,6 +182,7 @@
   void SyncAccessibilityLargeMouseCursorEnabled() const;
   void SyncAccessibilityVirtualKeyboardEnabled() const;
   void SyncBackupEnabled() const;
+  void SyncConsumerAutoUpdateToggle() const;
   void SyncDockedMagnifierEnabled() const;
   void SyncFocusHighlightEnabled() const;
   void SyncLocale() const;
@@ -205,9 +206,14 @@
   // Resets Android's display density to the default value.
   void ResetPageZoomToDefault() const;
 
-  // Registers to listen to a particular perf.
+  // Registers to listen to a particular pref.
+  // This should be used when dealing with pref per profile.
   void AddPrefToObserve(const std::string& pref_name);
 
+  // Registers to listen to a particular perf in local state.
+  // This should be used when dealing with pref per device.
+  void AddLocalStatePrefToObserve(const std::string& pref_name);
+
   // Returns the integer value of the pref.  pref_name must exist.
   int GetIntegerPref(const std::string& pref_name) const;
 
@@ -218,6 +224,10 @@
   void SendBoolPrefSettingsBroadcast(const std::string& pref_name,
                                      const std::string& action) const;
 
+  // Sends boolean local state pref broadcast to the delegate.
+  void SendBoolLocalStatePrefSettingsBroadcast(const std::string& pref_name,
+                                               const std::string& action) const;
+
   // Same as above, except sends a specific boolean value.
   void SendBoolValueSettingsBroadcast(bool value,
                                       bool managed,
@@ -235,6 +245,7 @@
 
   // Manages pref observation registration.
   PrefChangeRegistrar registrar_;
+  PrefChangeRegistrar local_state_registrar_;
 
   base::CallbackListSubscription reporting_consent_subscription_;
 
@@ -301,6 +312,8 @@
   } else if (pref_name == ::language::prefs::kApplicationLocale ||
              pref_name == ::language::prefs::kPreferredLanguages) {
     SyncLocale();
+  } else if (pref_name == ::prefs::kConsumerAutoUpdateToggle) {
+    SyncConsumerAutoUpdateToggle();
   } else if (pref_name == ::prefs::kUse24HourClock) {
     SyncUse24HourClock();
   } else if (pref_name == ::prefs::kResolveTimezoneByGeolocationMethod) {
@@ -382,6 +395,7 @@
 
 void ArcSettingsServiceImpl::StartObservingSettingsChanges() {
   registrar_.Init(GetPrefs());
+  local_state_registrar_.Init(g_browser_process->local_state());
 
   // Keep these lines ordered lexicographically.
   AddPrefToObserve(ash::prefs::kAccessibilityFocusHighlightEnabled);
@@ -399,6 +413,9 @@
   AddPrefToObserve(onc::prefs::kDeviceOpenNetworkConfiguration);
   AddPrefToObserve(onc::prefs::kOpenNetworkConfiguration);
 
+  // Keep these lines ordered lexicographically.
+  AddLocalStatePrefToObserve(::prefs::kConsumerAutoUpdateToggle);
+
   // Note that some preferences, such as kArcBackupRestoreEnabled and
   // kArcLocationServiceEnabled, are not dynamically updated after initial
   // ARC setup and therefore are not observed here.
@@ -416,6 +433,7 @@
 
 void ArcSettingsServiceImpl::StopObservingSettingsChanges() {
   registrar_.RemoveAll();
+  local_state_registrar_.RemoveAll();
   reporting_consent_subscription_ = {};
 
   TimezoneSettings::GetInstance()->RemoveObserver(this);
@@ -434,6 +452,7 @@
   // Keep these lines ordered lexicographically.
   SyncAccessibilityLargeMouseCursorEnabled();
   SyncAccessibilityVirtualKeyboardEnabled();
+  SyncConsumerAutoUpdateToggle();
   SyncDockedMagnifierEnabled();
   SyncFocusHighlightEnabled();
   SyncProxySettings();
@@ -730,6 +749,12 @@
                         extras);
 }
 
+void ArcSettingsServiceImpl::SyncConsumerAutoUpdateToggle() const {
+  SendBoolLocalStatePrefSettingsBroadcast(
+      ::prefs::kConsumerAutoUpdateToggle,
+      "org.chromium.arc.intent_helper.SET_CONSUMER_AUTO_UPDATE");
+}
+
 void ArcSettingsServiceImpl::ResetFontScaleToDefault() const {
   base::DictionaryValue extras;
   extras.SetDoubleKey("scale", kAndroidFontScaleNormal);
@@ -748,6 +773,13 @@
                                      base::Unretained(this)));
 }
 
+void ArcSettingsServiceImpl::AddLocalStatePrefToObserve(
+    const std::string& pref_name) {
+  local_state_registrar_.Add(
+      pref_name, base::BindRepeating(&ArcSettingsServiceImpl::OnPrefChanged,
+                                     base::Unretained(this)));
+}
+
 int ArcSettingsServiceImpl::GetIntegerPref(const std::string& pref_name) const {
   const PrefService::Preference* pref =
       registrar_.prefs()->FindPreference(pref_name);
@@ -766,6 +798,20 @@
   return !pref->IsUserModifiable();
 }
 
+void ArcSettingsServiceImpl::SendBoolLocalStatePrefSettingsBroadcast(
+    const std::string& pref_name,
+    const std::string& action) const {
+  DCHECK(g_browser_process);
+  const PrefService* local_state = g_browser_process->local_state();
+  DCHECK(local_state);
+  const PrefService::Preference* local_state_pref =
+      local_state->FindPreference(pref_name);
+  DCHECK(local_state_pref);
+  bool enabled = local_state->GetBoolean(pref_name);
+  SendBoolValueSettingsBroadcast(enabled, !local_state_pref->IsUserModifiable(),
+                                 action);
+}
+
 void ArcSettingsServiceImpl::SendBoolPrefSettingsBroadcast(
     const std::string& pref_name,
     const std::string& action) const {
diff --git a/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater.h b/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater.h
index 3c1f297..dd01910 100644
--- a/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater.h
+++ b/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater.h
@@ -14,7 +14,7 @@
 namespace ash {
 namespace smb_client {
 
-// Updates Kerberos credentials in SmbService after receiving
+// Updates Kerberos credentials in SmbService after receiving a
 // OnAccountsChanged notification from KerberosCredentialsManager.
 class SmbKerberosCredentialsUpdater
     : public KerberosCredentialsManager::Observer {
@@ -41,7 +41,8 @@
   // KerberosCredentialsManager::Observer:
   void OnAccountsChanged() override;
 
-  KerberosCredentialsManager* credentials_manager_;  // Not owned.
+  // Not owned.
+  KerberosCredentialsManager* credentials_manager_;
   std::string active_account_name_;
   const ActiveAccountChangedCallback active_account_changed_callback_;
 };
diff --git a/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater_unittest.cc b/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater_unittest.cc
index 46b21cfe..aaf33c0 100644
--- a/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater_unittest.cc
+++ b/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater_unittest.cc
@@ -105,7 +105,7 @@
 
   callback_called = false;
 
-  // Try to notify the change now without changing/adding user.
+  // Try to notify the change now without changing/adding a user.
   credentials_manager_->SetActiveAccount(kPrincipal);
 
   EXPECT_FALSE(callback_called);
diff --git a/chrome/browser/ash/smb_client/smb_service.cc b/chrome/browser/ash/smb_client/smb_service.cc
index c1d2559..2920d361 100644
--- a/chrome/browser/ash/smb_client/smb_service.cc
+++ b/chrome/browser/ash/smb_client/smb_service.cc
@@ -129,11 +129,12 @@
   KerberosCredentialsManager* credentials_manager =
       KerberosCredentialsManagerFactory::GetExisting(profile);
   if (credentials_manager && credentials_manager->IsKerberosEnabled()) {
-    smb_credentials_updater_ = std::make_unique<SmbKerberosCredentialsUpdater>(
-        credentials_manager,
-        base::BindRepeating(&SmbService::UpdateKerberosCredentials,
-                            AsWeakPtr()));
-    SetupKerberos(smb_credentials_updater_->active_account_name());
+    kerberos_credentials_updater_ =
+        std::make_unique<SmbKerberosCredentialsUpdater>(
+            credentials_manager,
+            base::BindRepeating(&SmbService::UpdateKerberosCredentials,
+                                AsWeakPtr()));
+    SetupKerberos(kerberos_credentials_updater_->active_account_name());
     return;
   }
 
@@ -308,7 +309,7 @@
 
   std::vector<uint8_t> salt;
   if (save_credentials && !password.empty()) {
-    // Only generate a salt if threre's a password and we've been asked to save
+    // Only generate a salt if there's a password and we've been asked to save
     // credentials. If there is no password, there's nothing for smbfs to store
     // and the salt is unused.
     salt.resize(kSaltLength);
@@ -374,11 +375,11 @@
           absl::make_optional<SmbFsShare::KerberosOptions>(
               SmbFsShare::KerberosOptions::Source::kActiveDirectory,
               user->GetAccountId().GetObjGuid());
-    } else if (smb_credentials_updater_) {
+    } else if (kerberos_credentials_updater_) {
       smbfs_options.kerberos_options =
           absl::make_optional<SmbFsShare::KerberosOptions>(
               SmbFsShare::KerberosOptions::Source::kKerberos,
-              smb_credentials_updater_->active_account_name());
+              kerberos_credentials_updater_->active_account_name());
     } else {
       LOG(WARNING) << "No Kerberos credential source available";
       std::move(callback).Run(SmbMountResult::kAuthenticationFailed, {});
@@ -491,8 +492,8 @@
 }
 
 bool SmbService::IsKerberosEnabledViaPolicy() const {
-  return smb_credentials_updater_ &&
-         smb_credentials_updater_->IsKerberosEnabled();
+  return kerberos_credentials_updater_ &&
+         kerberos_credentials_updater_->IsKerberosEnabled();
 }
 
 void SmbService::SetupKerberos(const std::string& account_identifier) {
diff --git a/chrome/browser/ash/smb_client/smb_service.h b/chrome/browser/ash/smb_client/smb_service.h
index 9224d67..ee34c87 100644
--- a/chrome/browser/ash/smb_client/smb_service.h
+++ b/chrome/browser/ash/smb_client/smb_service.h
@@ -63,9 +63,9 @@
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
-  // Starts the process of mounting an SMB file system.
-  // |use_kerberos| indicates whether the share should be mounted with a user's
-  // chromad kerberos tickets.
+  // Starts the process of mounting an SMB file system. |use_kerberos| indicates
+  // whether the share should be mounted with a Kerberos ticket - acquired
+  // though Chromad login or KerberosCredentialsManager.
   void Mount(const std::string& display_name,
              const base::FilePath& share_path,
              const std::string& username,
@@ -235,7 +235,7 @@
   std::unordered_map<std::string, std::unique_ptr<SmbFsShare>> smbfs_shares_;
   SmbPersistedShareRegistry registry_;
 
-  std::unique_ptr<SmbKerberosCredentialsUpdater> smb_credentials_updater_;
+  std::unique_ptr<SmbKerberosCredentialsUpdater> kerberos_credentials_updater_;
 
   base::OnceClosure setup_complete_callback_;
   SmbFsShare::MounterCreationCallback smbfs_mounter_creation_callback_;
diff --git a/chrome/browser/ash/smb_client/smb_service_factory.cc b/chrome/browser/ash/smb_client/smb_service_factory.cc
index 46ec6983..d8e95425 100644
--- a/chrome/browser/ash/smb_client/smb_service_factory.cc
+++ b/chrome/browser/ash/smb_client/smb_service_factory.cc
@@ -66,7 +66,7 @@
 KeyedService* SmbServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   // Check if service is enabled by feature flag, via policy, and if profile has
-  // a user. Lockscreen is the example of a profile that doesn't have a user -
+  // a user. Lock screen is the example of a profile that doesn't have a user -
   // in this case smb service is not needed.
   Profile* const profile = Profile::FromBrowserContext(context);
   bool service_should_run =
diff --git a/chrome/browser/ash/smb_client/smb_service_factory.h b/chrome/browser/ash/smb_client/smb_service_factory.h
index 2845c29..581abc89 100644
--- a/chrome/browser/ash/smb_client/smb_service_factory.h
+++ b/chrome/browser/ash/smb_client/smb_service_factory.h
@@ -26,12 +26,14 @@
   // Gets a singleton instance of the factory.
   static SmbServiceFactory* GetInstance();
 
+  // Disallow copy and assignment.
+  SmbServiceFactory(const SmbServiceFactory&) = delete;
+  SmbServiceFactory& operator=(const SmbServiceFactory&) = delete;
+
  private:
   friend struct base::DefaultSingletonTraits<SmbServiceFactory>;
 
   SmbServiceFactory();
-  SmbServiceFactory(const SmbServiceFactory&) = delete;
-  SmbServiceFactory& operator=(const SmbServiceFactory&) = delete;
   ~SmbServiceFactory() override;
 
   // BrowserContextKeyedServiceFactory overrides:
diff --git a/chrome/browser/ash/smb_client/smb_service_unittest.cc b/chrome/browser/ash/smb_client/smb_service_unittest.cc
index c6637e314..90ac0f4c4 100644
--- a/chrome/browser/ash/smb_client/smb_service_unittest.cc
+++ b/chrome/browser/ash/smb_client/smb_service_unittest.cc
@@ -77,7 +77,7 @@
 constexpr char kMountPath2[] = "/share/mount/second_path";
 
 constexpr char kTestADUser[] = "ad-test-user";
-constexpr char kTestADDomain[] = "foorbar.corp";
+constexpr char kTestADDomain[] = "foobar.corp";
 constexpr char kTestADGuid[] = "ad-user-guid";
 
 void SaveMountResult(SmbMountResult* out, SmbMountResult result) {
@@ -204,7 +204,7 @@
     SmbMountResult result = SmbMountResult::kSuccess;
     smb_service_->Mount("" /* display_name */, base::FilePath(url),
                         "" /* username */, "" /* password */,
-                        false /* use_chromad_kerberos */,
+                        false /* use_kerberos */,
                         false /* should_open_file_manager_after_mount */,
                         false /* save_credentials */,
                         base::BindOnce(&SaveMountResult, &result));
@@ -215,7 +215,7 @@
     SmbMountResult result = SmbMountResult::kSuccess;
     smb_service_->Mount("" /* display_name */, base::FilePath(url),
                         "" /* username */, "" /* password */,
-                        true /* use_chromad_kerberos */,
+                        true /* use_kerberos */,
                         false /* should_open_file_manager_after_mount */,
                         false /* save_credentials */,
                         base::BindOnce(&SaveMountResult, &result));
@@ -294,7 +294,7 @@
     base::RunLoop run_loop;
     smb_service_->Mount(kDisplayName, base::FilePath(share_path),
                         "" /* username */, "" /* password */,
-                        false /* use_chromad_kerberos */,
+                        false /* use_kerberos */,
                         false /* should_open_file_manager_after_mount */,
                         false /* save_credentials */,
                         base::BindLambdaForTesting(
@@ -313,8 +313,12 @@
   file_manager::FakeDiskMountManager* disk_mount_manager_ =
       new file_manager::FakeDiskMountManager;
 
-  TestingProfile* profile_ = nullptr;     // Not owned.
-  TestingProfile* ad_profile_ = nullptr;  // Not owned.
+  // Not owned.
+  TestingProfile* profile_ = nullptr;
+
+  // Not owned.
+  TestingProfile* ad_profile_ = nullptr;
+
   std::unique_ptr<TestingProfileManager> profile_manager_;
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
   std::unique_ptr<SmbService> smb_service_;
@@ -388,7 +392,7 @@
   base::RunLoop run_loop;
   smb_service_->Mount(
       kDisplayName, base::FilePath(kSharePath), kTestUser, kTestPassword,
-      false /* use_chromad_kerberos */,
+      false /* use_kerberos */,
       false /* should_open_file_manager_after_mount */,
       false /* save_credentials */,
       base::BindLambdaForTesting([&run_loop](SmbMountResult result) {
@@ -480,7 +484,7 @@
   base::RunLoop run_loop;
   smb_service_->Mount(
       kDisplayName, base::FilePath(kSharePath), kTestUser, kTestPassword,
-      false /* use_chromad_kerberos */,
+      false /* use_kerberos */,
       false /* should_open_file_manager_after_mount */,
       true /* save_credentials */,
       base::BindLambdaForTesting([&run_loop](SmbMountResult result) {
@@ -551,8 +555,7 @@
   smb_service_->Mount(
       kDisplayName, base::FilePath(kSharePath),
       base::StrCat({kTestUser, "@", kTestDomain}), kTestPassword,
-      true /* use_chromad_kerberos */,
-      false /* should_open_file_manager_after_mount */,
+      true /* use_kerberos */, false /* should_open_file_manager_after_mount */,
       false /* save_credentials */,
       base::BindLambdaForTesting([&run_loop](SmbMountResult result) {
         EXPECT_EQ(SmbMountResult::kSuccess, result);
@@ -698,7 +701,7 @@
 }
 
 TEST_F(SmbServiceWithSmbfsTest, MountExcessiveShares) {
-  // The maxmium number of smbfs shares that can be mounted simultaneously.
+  // The maximum number of smbfs shares that can be mounted simultaneously.
   // Should match the definition in smb_service.cc.
   const size_t kMaxSmbFsShares = 16;
   CreateService(profile_);
diff --git a/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.cc b/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.cc
index d1d7c2d..1b376871 100644
--- a/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.h"
 
 #include "ash/public/cpp/wallpaper/wallpaper_controller.h"
+#include "ash/shell.h"
 #include "chrome/browser/ash/login/users/avatar/user_image_manager.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager.h"
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_utils.h"
@@ -27,6 +28,8 @@
 EnterprisePolicyDelegateImpl::EnterprisePolicyDelegateImpl(
     content::BrowserContext* browser_context)
     : profile_(Profile::FromBrowserContext(browser_context)) {
+  DCHECK(Shell::HasInstance());
+  scoped_shell_observation_.Observe(Shell::Get());
   scoped_user_manager_observation_.Observe(user_manager::UserManager::Get());
   scoped_wallpaper_controller_observation_.Observe(WallpaperController::Get());
 }
@@ -72,4 +75,9 @@
   }
 }
 
+void EnterprisePolicyDelegateImpl::OnShellDestroying() {
+  // WallpaperController is about to be destroyed.
+  scoped_wallpaper_controller_observation_.Reset();
+}
+
 }  // namespace ash::personalization_app
diff --git a/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.h b/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.h
index 4369014a..7618499 100644
--- a/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.h
+++ b/chrome/browser/ash/web_applications/personalization_app/enterprise_policy_delegate_impl.h
@@ -8,6 +8,8 @@
 #include "ash/public/cpp/personalization_app/enterprise_policy_delegate.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller_observer.h"
+#include "ash/shell.h"
+#include "ash/shell_observer.h"
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
 #include "base/scoped_observation.h"
@@ -20,7 +22,8 @@
 
 class EnterprisePolicyDelegateImpl : public EnterprisePolicyDelegate,
                                      public user_manager::UserManager::Observer,
-                                     public WallpaperControllerObserver {
+                                     public WallpaperControllerObserver,
+                                     public ShellObserver {
  public:
   explicit EnterprisePolicyDelegateImpl(
       content::BrowserContext* browser_context);
@@ -46,12 +49,24 @@
   // WallpaperControllerObserver:
   void OnWallpaperChanged() override;
 
+  // ShellObserver:
+  void OnShellDestroying() override;
+
   raw_ptr<Profile> profile_;
+
+  base::ScopedObservation<Shell,
+                          ShellObserver,
+                          &Shell::AddShellObserver,
+                          &Shell::RemoveShellObserver>
+      scoped_shell_observation_{this};
+
   base::ScopedObservation<user_manager::UserManager,
                           user_manager::UserManager::Observer>
       scoped_user_manager_observation_{this};
+
   base::ScopedObservation<WallpaperController, WallpaperControllerObserver>
       scoped_wallpaper_controller_observation_{this};
+
   base::ObserverList<EnterprisePolicyDelegate::Observer> observer_list_;
 };
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 9a1d0b8..5123f2ea 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -494,9 +494,8 @@
 
 class RemoveUkmDataTester {
  public:
-  static constexpr optimization_guide::proto::OptimizationTarget kSegmentId =
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
+  static constexpr auto kSegmentId = segmentation_platform::proto::
+      OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
 
   RemoveUkmDataTester() : test_utils_(&ukm_recorder_) {
     test_utils_.PreProfileInit({kSegmentId});
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 9210fa2..118f2bc 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -757,14 +757,14 @@
   }
 
 #if BUILDFLAG(IS_WIN)
-  // On Windows, we use our startup as an opportunity to do upgrade/uninstall
-  // tasks.  Those care whether the browser is already running.  On Linux/Mac,
-  // upgrade/uninstall happen separately.
-  already_running_ = browser_util::IsBrowserAlreadyRunning();
+  // If we are running stale binaries then relaunch and exit immediately.
+  if (upgrade_util::IsRunningOldChrome()) {
+    if (!upgrade_util::RelaunchChromeBrowser(
+            *base::CommandLine::ForCurrentProcess())) {
+      // The relaunch failed. Feel free to panic now.
+      NOTREACHED();
+    }
 
-  // Do the tasks if chrome has been upgraded while it was last running.
-  if (!already_running_ &&
-      upgrade_util::DoUpgradeTasks(*base::CommandLine::ForCurrentProcess())) {
     // Note, cannot return RESULT_CODE_NORMAL_EXIT here as this code needs to
     // result in browser startup bailing.
     return chrome::RESULT_CODE_NORMAL_EXIT_UPGRADE_RELAUNCHED;
@@ -1361,7 +1361,7 @@
   // If the command line specifies 'uninstall' then we need to work here
   // unless we detect another chrome browser running.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUninstall)) {
-    return DoUninstallTasks(already_running_);
+    return DoUninstallTasks(browser_util::IsBrowserAlreadyRunning());
   }
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHideIcons) ||
@@ -1449,6 +1449,13 @@
       return chrome::RESULT_CODE_PROFILE_IN_USE;
   }
 
+#if BUILDFLAG(IS_WIN)
+  // We must call DoUpgradeTasks now that we own the browser singleton to
+  // finish upgrade tasks (swap) and relaunch if necessary.
+  if (upgrade_util::DoUpgradeTasks(*base::CommandLine::ForCurrentProcess()))
+    return chrome::RESULT_CODE_NORMAL_EXIT_UPGRADE_RELAUNCHED;
+#endif  // BUILDFLAG(IS_WIN)
+
 #if BUILDFLAG(ENABLE_DOWNGRADE_PROCESSING)
   // Begin relaunch processing immediately if User Data migration is required
   // to handle a version downgrade.
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index 514048f..99def0a5 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -219,13 +219,6 @@
   // Observer that triggers `PostProfileInit()` when new user profiles are
   // created.
   std::unique_ptr<ProfileInitManager> profile_init_manager_;
-
-#if BUILDFLAG(IS_WIN)
-  // Whether or not another browser is already running. This is obtained once
-  // early during startup as each attempt to determine this might race another
-  // browser starting at the same time.
-  bool already_running_ = false;
-#endif
 };
 
 #endif  // CHROME_BROWSER_CHROME_BROWSER_MAIN_H_
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 600097b..dc6f7b0 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -20,6 +20,8 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/dcheck_is_on.h"
+#include "base/debug/handle_hooks_win.h"
 #include "base/enterprise_util.h"
 #include "base/environment.h"
 #include "base/feature_list.h"
@@ -44,6 +46,7 @@
 #include "base/win/windows_version.h"
 #include "base/win/wrapped_window_proc.h"
 #include "build/branding_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/util/critical_policy_section_metrics_win.h"
@@ -383,74 +386,6 @@
   return true;
 }
 
-// Used as the callback for ModuleWatcher events in this process. Dispatches
-// them to the ModuleDatabase.
-// Note: This callback may be invoked on any thread, even those not owned by the
-//       task scheduler, under the loader lock, directly on the thread where the
-//       DLL is currently loading.
-void OnModuleEvent(const ModuleWatcher::ModuleEvent& event) {
-  {
-    TRACE_EVENT1("browser", "OnModuleEvent", "module_path",
-                 event.module_path.BaseName().AsUTF8Unsafe());
-
-    switch (event.event_type) {
-      case ModuleWatcher::ModuleEventType::kModuleAlreadyLoaded: {
-        // kModuleAlreadyLoaded comes from the enumeration of loaded modules
-        // using CreateToolhelp32Snapshot().
-        uint32_t time_date_stamp = 0;
-        if (TryGetModuleTimeDateStamp(event.module_load_address,
-                                      event.module_path, event.module_size,
-                                      &time_date_stamp)) {
-          ModuleDatabase::HandleModuleLoadEvent(
-              content::PROCESS_TYPE_BROWSER, event.module_path,
-              event.module_size, time_date_stamp);
-        } else {
-          // Failed to get the TimeDateStamp directly from memory. The next step
-          // to try is to read the file on disk. This must be done in a blocking
-          // task.
-          base::ThreadPool::PostTask(
-              FROM_HERE,
-              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-              base::BindOnce(&HandleModuleLoadEventWithoutTimeDateStamp,
-                             event.module_path, event.module_size));
-        }
-        break;
-      }
-      case ModuleWatcher::ModuleEventType::kModuleLoaded: {
-        ModuleDatabase::HandleModuleLoadEvent(
-            content::PROCESS_TYPE_BROWSER, event.module_path, event.module_size,
-            GetModuleTimeDateStamp(event.module_load_address));
-        break;
-      }
-    }
-  }
-  // Since OnModuleEvent can be invoked from any thread, the above trace event's
-  // END might be the last event on this thread, emit an empty event to force
-  // the END to be flushed. TODO(crbug.com/1021571): Remove this once fixed.
-  PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
-}
-
-// Helper function for initializing the module database subsystem and populating
-// the provided |module_watcher|.
-void SetupModuleDatabase(std::unique_ptr<ModuleWatcher>* module_watcher) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(module_watcher);
-
-  bool third_party_blocking_policy_enabled =
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-      ModuleDatabase::IsThirdPartyBlockingPolicyEnabled();
-#else
-      false;
-#endif
-
-  ModuleDatabase::GetTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&InitializeModuleDatabase,
-                                third_party_blocking_policy_enabled));
-
-  *module_watcher = ModuleWatcher::Create(base::BindRepeating(&OnModuleEvent));
-}
-
 void ShowCloseBrowserFirstMessageBox() {
   chrome::ShowWarningMessageBox(
       nullptr, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
@@ -663,6 +598,18 @@
             .get());
 #endif
 
+#if DCHECK_IS_ON()
+    // Patching EAT of kernel32.dll is only supported on 32-bit because RVA can
+    // only hold 32-bit values.
+#if defined(ARCH_CPU_32_BITS)
+  base::debug::HandleHooks::AddEATPatch();
+#endif
+  // Patch currently loaded modules. Future ones will get patched by the module
+  // watcher. Note: if any modules load between now and when SetupModuleDatabase
+  // is called then these will be missed.
+  base::debug::HandleHooks::PatchLoadedModules();
+#endif  // DCHECK_IS_ON()
+
   // Create the module database and hook up the in-process module watcher. This
   // needs to be done before any child processes are initialized as the
   // ModuleDatabase is an endpoint for IPC from child processes.
@@ -934,3 +881,82 @@
   // duplicates, perhaps harmonize with switches::RemoveSwitchesForAutostart.
   return restart_command;
 }
+
+// Used as the callback for ModuleWatcher events in this process. Dispatches
+// them to the ModuleDatabase.
+// Note: This callback may be invoked on any thread, even those not owned by the
+//       task scheduler, under the loader lock, directly on the thread where the
+//       DLL is currently loading.
+void ChromeBrowserMainPartsWin::OnModuleEvent(
+    const ModuleWatcher::ModuleEvent& event) {
+  {
+    TRACE_EVENT1("browser", "OnModuleEvent", "module_path",
+                 event.module_path.BaseName().AsUTF8Unsafe());
+
+    switch (event.event_type) {
+      case ModuleWatcher::ModuleEventType::kModuleAlreadyLoaded: {
+        // kModuleAlreadyLoaded comes from the enumeration of loaded modules
+        // using CreateToolhelp32Snapshot().
+        uint32_t time_date_stamp = 0;
+        if (TryGetModuleTimeDateStamp(event.module_load_address,
+                                      event.module_path, event.module_size,
+                                      &time_date_stamp)) {
+          ModuleDatabase::HandleModuleLoadEvent(
+              content::PROCESS_TYPE_BROWSER, event.module_path,
+              event.module_size, time_date_stamp);
+        } else {
+          // Failed to get the TimeDateStamp directly from memory. The next step
+          // to try is to read the file on disk. This must be done in a blocking
+          // task.
+          base::ThreadPool::PostTask(
+              FROM_HERE,
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+              base::BindOnce(&HandleModuleLoadEventWithoutTimeDateStamp,
+                             event.module_path, event.module_size));
+        }
+        break;
+      }
+      case ModuleWatcher::ModuleEventType::kModuleLoaded: {
+#if DCHECK_IS_ON() && defined(ARCH_CPU_64_BITS)
+        // This is only needed on 64-bit because on 32-bit the EAT from kernel32
+        // is already patched. This is thread safe against itself as this is
+        // always called under loader lock.
+        HMODULE module =
+            reinterpret_cast<HMODULE>(event.module_load_address.get());
+        base::debug::HandleHooks::AddIATPatch(module);
+#endif  // DCHECK_IS_ON() && defined(ARCH_CPU_64_BITS)
+        ModuleDatabase::HandleModuleLoadEvent(
+            content::PROCESS_TYPE_BROWSER, event.module_path, event.module_size,
+            GetModuleTimeDateStamp(event.module_load_address));
+        break;
+      }
+    }
+  }
+  // Since OnModuleEvent can be invoked from any thread, the above trace event's
+  // END might be the last event on this thread, emit an empty event to force
+  // the END to be flushed. TODO(crbug.com/1021571): Remove this once fixed.
+  PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
+}
+
+// Helper function for initializing the module database subsystem and populating
+// the provided |module_watcher|.
+void ChromeBrowserMainPartsWin::SetupModuleDatabase(
+    std::unique_ptr<ModuleWatcher>* module_watcher) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(module_watcher);
+
+  bool third_party_blocking_policy_enabled =
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+      ModuleDatabase::IsThirdPartyBlockingPolicyEnabled();
+#else
+      false;
+#endif
+
+  ModuleDatabase::GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&InitializeModuleDatabase,
+                                third_party_blocking_policy_enabled));
+
+  *module_watcher = ModuleWatcher::Create(base::BindRepeating(
+      &ChromeBrowserMainPartsWin::OnModuleEvent, base::Unretained(this)));
+}
diff --git a/chrome/browser/chrome_browser_main_win.h b/chrome/browser/chrome_browser_main_win.h
index 66bcce9..87e802df 100644
--- a/chrome/browser/chrome_browser_main_win.h
+++ b/chrome/browser/chrome_browser_main_win.h
@@ -10,8 +10,7 @@
 #include <memory>
 
 #include "chrome/browser/chrome_browser_main.h"
-
-class ModuleWatcher;
+#include "chrome/common/conflicts/module_watcher_win.h"
 
 namespace base {
 class CommandLine;
@@ -77,6 +76,9 @@
       const base::CommandLine& command_line);
 
  private:
+  void OnModuleEvent(const ModuleWatcher::ModuleEvent& event);
+  void SetupModuleDatabase(std::unique_ptr<ModuleWatcher>* module_watcher);
+
   // Watches module load events and forwards them to the ModuleDatabase.
   std::unique_ptr<ModuleWatcher> module_watcher_;
 };
diff --git a/extensions/browser/api/vpn_provider/BUILD.gn b/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn
similarity index 86%
rename from extensions/browser/api/vpn_provider/BUILD.gn
rename to chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn
index 3e3c25a..79675a7 100644
--- a/extensions/browser/api/vpn_provider/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/vpn_provider/BUILD.gn
@@ -18,18 +18,17 @@
     "vpn_service.h",
     "vpn_service_factory.cc",
     "vpn_service_factory.h",
+    "vpn_service_interface.h",
   ]
 
   deps = [
     "//base",
+    "//chrome/common/extensions/api",
     "//chromeos/dbus",
     "//chromeos/login/login_state",
     "//chromeos/network",
     "//components/keyed_service/content",
     "//content/public/browser",
-    "//extensions/common",
-    "//extensions/common/api",
+    "//extensions/browser",
   ]
-
-  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/vpn_provider/DEPS b/chrome/browser/chromeos/extensions/vpn_provider/DEPS
similarity index 100%
rename from extensions/browser/api/vpn_provider/DEPS
rename to chrome/browser/chromeos/extensions/vpn_provider/DEPS
diff --git a/extensions/browser/api/vpn_provider/DIR_METADATA b/chrome/browser/chromeos/extensions/vpn_provider/DIR_METADATA
similarity index 100%
rename from extensions/browser/api/vpn_provider/DIR_METADATA
rename to chrome/browser/chromeos/extensions/vpn_provider/DIR_METADATA
diff --git a/extensions/browser/api/vpn_provider/OWNERS b/chrome/browser/chromeos/extensions/vpn_provider/OWNERS
similarity index 64%
rename from extensions/browser/api/vpn_provider/OWNERS
rename to chrome/browser/chromeos/extensions/vpn_provider/OWNERS
index a913a05..499be61f 100644
--- a/extensions/browser/api/vpn_provider/OWNERS
+++ b/chrome/browser/chromeos/extensions/vpn_provider/OWNERS
@@ -1,2 +1,3 @@
 bartfab@chromium.org
 emaxx@chromium.org
+greengrape@google.com
diff --git a/extensions/browser/api/vpn_provider/vpn_provider_api.cc b/chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.cc
similarity index 76%
rename from extensions/browser/api/vpn_provider/vpn_provider_api.cc
rename to chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.cc
index 4bddaca..18fadc66e3 100644
--- a/extensions/browser/api/vpn_provider/vpn_provider_api.cc
+++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.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 "extensions/browser/api/vpn_provider/vpn_provider_api.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.h"
 
 #include <memory>
 #include <utility>
@@ -13,9 +13,8 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
-#include "extensions/browser/api/vpn_provider/vpn_service.h"
-#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
-#include "extensions/common/api/vpn_provider.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h"
+#include "chrome/common/extensions/api/vpn_provider.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace extensions {
@@ -76,7 +75,7 @@
 }
 
 void ConvertParameters(const api_vpn::Parameters& parameters,
-                       base::DictionaryValue* parameter_value,
+                       base::Value::Dict* parameter_value,
                        std::string* error) {
   if (!CheckIPCIDRSanity(parameters.address, true /* CIDR */,
                          false /*IPV4 */)) {
@@ -102,50 +101,50 @@
     return;
   }
 
-  std::vector<std::string> cidr_parts = base::SplitString(
-      parameters.address, kCIDRSeperator, base::KEEP_WHITESPACE,
-      base::SPLIT_WANT_NONEMPTY);
+  std::vector<std::string> cidr_parts =
+      base::SplitString(parameters.address, kCIDRSeperator,
+                        base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   CHECK_EQ(2u, cidr_parts.size());
 
-  parameter_value->SetKey(shill::kAddressParameterThirdPartyVpn,
-                          base::Value(cidr_parts[0]));
+  parameter_value->Set(shill::kAddressParameterThirdPartyVpn,
+                       base::Value(cidr_parts[0]));
 
-  parameter_value->SetKey(shill::kSubnetPrefixParameterThirdPartyVpn,
-                          base::Value(cidr_parts[1]));
+  parameter_value->Set(shill::kSubnetPrefixParameterThirdPartyVpn,
+                       base::Value(cidr_parts[1]));
 
   std::string ip_delimiter(1, shill::kIPDelimiter);
-  parameter_value->SetKey(
+  parameter_value->Set(
       shill::kExclusionListParameterThirdPartyVpn,
       base::Value(base::JoinString(parameters.exclusion_list, ip_delimiter)));
 
-  parameter_value->SetKey(
+  parameter_value->Set(
       shill::kInclusionListParameterThirdPartyVpn,
       base::Value(base::JoinString(parameters.inclusion_list, ip_delimiter)));
 
   if (parameters.mtu) {
-    parameter_value->SetKey(shill::kMtuParameterThirdPartyVpn,
-                            base::Value(*parameters.mtu));
+    parameter_value->Set(shill::kMtuParameterThirdPartyVpn,
+                         base::Value(*parameters.mtu));
   }
 
   if (parameters.broadcast_address) {
-    parameter_value->SetKey(shill::kBroadcastAddressParameterThirdPartyVpn,
-                            base::Value(*parameters.broadcast_address));
+    parameter_value->Set(shill::kBroadcastAddressParameterThirdPartyVpn,
+                         base::Value(*parameters.broadcast_address));
   }
 
   std::string non_ip_delimiter(1, shill::kNonIPDelimiter);
   if (parameters.domain_search) {
-    parameter_value->SetKey(shill::kDomainSearchParameterThirdPartyVpn,
-                            base::Value(base::JoinString(
-                                *parameters.domain_search, non_ip_delimiter)));
+    parameter_value->Set(shill::kDomainSearchParameterThirdPartyVpn,
+                         base::Value(base::JoinString(*parameters.domain_search,
+                                                      non_ip_delimiter)));
   }
 
-  parameter_value->SetKey(
+  parameter_value->Set(
       shill::kDnsServersParameterThirdPartyVpn,
       base::Value(base::JoinString(parameters.dns_servers, ip_delimiter)));
 
   if (parameters.reconnect) {
-    parameter_value->SetKey(shill::kReconnectParameterThirdPartyVpn,
-                            base::Value(*parameters.reconnect));
+    parameter_value->Set(shill::kReconnectParameterThirdPartyVpn,
+                         base::Value(*parameters.reconnect));
   }
 
   return;
@@ -153,24 +152,15 @@
 
 }  // namespace
 
-VpnThreadExtensionFunction::~VpnThreadExtensionFunction() {
-}
+VpnThreadExtensionFunction::~VpnThreadExtensionFunction() = default;
 
 void VpnThreadExtensionFunction::SignalCallCompletionSuccess() {
   Respond(NoArguments());
 }
 
 void VpnThreadExtensionFunction::SignalCallCompletionSuccessWithId(
-    const std::string& configuration_id) {
-  Respond(OneArgument(base::Value(configuration_id)));
-}
-
-void VpnThreadExtensionFunction::SignalCallCompletionSuccessWithWarning(
-    const std::string& warning) {
-  if (!warning.empty()) {
-    WriteToConsole(blink::mojom::ConsoleMessageLevel::kWarning, warning);
-  }
-  Respond(NoArguments());
+    const std::string& configuration_name) {
+  Respond(OneArgument(base::Value(configuration_name)));
 }
 
 void VpnThreadExtensionFunction::SignalCallCompletionFailure(
@@ -185,8 +175,7 @@
   }
 }
 
-VpnProviderCreateConfigFunction::~VpnProviderCreateConfigFunction() {
-}
+VpnProviderCreateConfigFunction::~VpnProviderCreateConfigFunction() = default;
 
 ExtensionFunction::ResponseAction VpnProviderCreateConfigFunction::Run() {
   std::unique_ptr<api_vpn::CreateConfig::Params> params(
@@ -195,16 +184,14 @@
     return RespondNow(Error("Invalid arguments."));
   }
 
-  chromeos::VpnService* service =
+  chromeos::VpnServiceInterface* service =
       chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
   if (!service) {
     return RespondNow(Error("Invalid profile."));
   }
 
-  // Use the configuration name as ID. In the future, a different ID scheme may
-  // be used, requiring a mapping between the two.
   service->CreateConfiguration(
-      extension_id(), extension()->name(), params->name,
+      extension_id(), params->name,
       base::BindOnce(
           &VpnProviderCreateConfigFunction::SignalCallCompletionSuccessWithId,
           this, params->name),
@@ -215,8 +202,7 @@
   return RespondLater();
 }
 
-VpnProviderDestroyConfigFunction::~VpnProviderDestroyConfigFunction() {
-}
+VpnProviderDestroyConfigFunction::~VpnProviderDestroyConfigFunction() = default;
 
 ExtensionFunction::ResponseAction VpnProviderDestroyConfigFunction::Run() {
   std::unique_ptr<api_vpn::DestroyConfig::Params> params(
@@ -225,7 +211,7 @@
     return RespondNow(Error("Invalid arguments."));
   }
 
-  chromeos::VpnService* service =
+  chromeos::VpnServiceInterface* service =
       chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
   if (!service) {
     return RespondNow(Error("Invalid profile."));
@@ -242,8 +228,7 @@
   return RespondLater();
 }
 
-VpnProviderSetParametersFunction::~VpnProviderSetParametersFunction() {
-}
+VpnProviderSetParametersFunction::~VpnProviderSetParametersFunction() = default;
 
 ExtensionFunction::ResponseAction VpnProviderSetParametersFunction::Run() {
   std::unique_ptr<api_vpn::SetParameters::Params> params(
@@ -252,13 +237,13 @@
     return RespondNow(Error("Invalid arguments."));
   }
 
-  chromeos::VpnService* service =
+  chromeos::VpnServiceInterface* service =
       chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
   if (!service) {
     return RespondNow(Error("Invalid profile."));
   }
 
-  base::DictionaryValue parameter_value;
+  base::Value::Dict parameter_value;
   std::string error;
   ConvertParameters(params->parameters, &parameter_value, &error);
   if (!error.empty()) {
@@ -266,10 +251,9 @@
   }
 
   service->SetParameters(
-      extension_id(), parameter_value,
-      base::BindOnce(&VpnProviderSetParametersFunction::
-                         SignalCallCompletionSuccessWithWarning,
-                     this),
+      extension_id(), std::move(parameter_value),
+      base::BindOnce(
+          &VpnProviderSetParametersFunction::SignalCallCompletionSuccess, this),
       base::BindOnce(&VpnProviderNotifyConnectionStateChangedFunction::
                          SignalCallCompletionFailure,
                      this));
@@ -277,8 +261,7 @@
   return RespondLater();
 }
 
-VpnProviderSendPacketFunction::~VpnProviderSendPacketFunction() {
-}
+VpnProviderSendPacketFunction::~VpnProviderSendPacketFunction() = default;
 
 ExtensionFunction::ResponseAction VpnProviderSendPacketFunction::Run() {
   std::unique_ptr<api_vpn::SendPacket::Params> params(
@@ -287,7 +270,7 @@
     return RespondNow(Error("Invalid arguments."));
   }
 
-  chromeos::VpnService* service =
+  chromeos::VpnServiceInterface* service =
       chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
   if (!service) {
     return RespondNow(Error("Invalid profile."));
@@ -306,8 +289,7 @@
 }
 
 VpnProviderNotifyConnectionStateChangedFunction::
-    ~VpnProviderNotifyConnectionStateChangedFunction() {
-}
+    ~VpnProviderNotifyConnectionStateChangedFunction() = default;
 
 ExtensionFunction::ResponseAction
 VpnProviderNotifyConnectionStateChangedFunction::Run() {
@@ -317,7 +299,7 @@
     return RespondNow(Error("Invalid arguments."));
   }
 
-  chromeos::VpnService* service =
+  chromeos::VpnServiceInterface* service =
       chromeos::VpnServiceFactory::GetForBrowserContext(browser_context());
   if (!service) {
     return RespondNow(Error("Invalid profile."));
diff --git a/extensions/browser/api/vpn_provider/vpn_provider_api.h b/chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.h
similarity index 88%
rename from extensions/browser/api/vpn_provider/vpn_provider_api.h
rename to chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.h
index 3f73331a..480b41b 100644
--- a/extensions/browser/api/vpn_provider/vpn_provider_api.h
+++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.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 EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_PROVIDER_API_H_
-#define EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_PROVIDER_API_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_PROVIDER_API_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_PROVIDER_API_H_
 
 #include <string>
 
@@ -14,9 +14,7 @@
 class VpnThreadExtensionFunction : public ExtensionFunction {
  public:
   void SignalCallCompletionSuccess();
-  void SignalCallCompletionSuccessWithId(const std::string& configuration_id);
-  void SignalCallCompletionSuccessWithWarning(const std::string& warning);
-
+  void SignalCallCompletionSuccessWithId(const std::string& configuration_name);
   void SignalCallCompletionFailure(const std::string& error_name,
                                    const std::string& error_message);
 
@@ -81,4 +79,4 @@
 
 }  // namespace extensions
 
-#endif  // EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_PROVIDER_API_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_PROVIDER_API_H_
diff --git a/extensions/browser/api/vpn_provider/vpn_service.cc b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.cc
similarity index 93%
rename from extensions/browser/api/vpn_provider/vpn_service.cc
rename to chrome/browser/chromeos/extensions/vpn_provider/vpn_service.cc
index c430b2cd..80cea51 100644
--- a/extensions/browser/api/vpn_provider/vpn_service.cc
+++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.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 "extensions/browser/api/vpn_provider/vpn_service.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service.h"
 
 #include <stdint.h>
 #include <utility>
@@ -19,7 +19,7 @@
 #include "base/strings/string_util.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_interface.h"
 #include "chromeos/dbus/shill/shill_third_party_vpn_driver_client.h"
 #include "chromeos/dbus/shill/shill_third_party_vpn_observer.h"
 #include "chromeos/network/network_configuration_handler.h"
@@ -48,6 +48,12 @@
   LOG(ERROR) << error_name << ": " << error_message;
 }
 
+void RunWarningCallback(
+    extensions::api::VpnServiceInterface::SuccessCallback callback,
+    const std::string& /*warning*/) {
+  std::move(callback).Run();
+}
+
 }  // namespace
 
 class VpnService::VpnConfiguration : public ShillThirdPartyVpnObserver {
@@ -170,6 +176,7 @@
             FailureCallback failure,
             std::unique_ptr<content::PepperVpnProviderResourceHostProxy>
                 pepper_vpn_provider_proxy) override;
+
   void SendPacket(const std::string& extension_id,
                   const std::vector<char>& data,
                   SuccessCallback success,
@@ -257,22 +264,22 @@
 
 void VpnService::SendShowConfigureDialogToExtension(
     const std::string& extension_id,
-    const std::string& configuration_id) {
+    const std::string& configuration_name) {
   SendSignalToExtension(
       extension_id, extensions::events::VPN_PROVIDER_ON_UI_EVENT,
       api_vpn::OnUIEvent::kEventName,
       api_vpn::OnUIEvent::Create(api_vpn::UI_EVENT_SHOWCONFIGUREDIALOG,
-                                 configuration_id));
+                                 configuration_name));
 }
 
 void VpnService::SendPlatformError(const std::string& extension_id,
-                                   const std::string& configuration_id,
+                                   const std::string& configuration_name,
                                    const std::string& error_message) {
   SendSignalToExtension(
       extension_id, extensions::events::VPN_PROVIDER_ON_PLATFORM_MESSAGE,
       api_vpn::OnPlatformMessage::kEventName,
       api_vpn::OnPlatformMessage::Create(
-          configuration_id, api_vpn::PLATFORM_MESSAGE_ERROR, error_message));
+          configuration_name, api_vpn::PLATFORM_MESSAGE_ERROR, error_message));
 }
 
 std::string VpnService::GetKey(const std::string& extension_id,
@@ -353,7 +360,6 @@
 }
 
 void VpnService::CreateConfiguration(const std::string& extension_id,
-                                     const std::string& extension_name,
                                      const std::string& configuration_name,
                                      SuccessCallback success,
                                      FailureCallback failure) {
@@ -381,23 +387,17 @@
   VpnConfiguration* configuration =
       CreateConfigurationInternal(extension_id, configuration_name, key);
 
-  base::DictionaryValue properties;
-  properties.SetKey(shill::kTypeProperty, base::Value(shill::kTypeVPN));
-  properties.SetKey(shill::kNameProperty, base::Value(configuration_name));
-  properties.SetKey(shill::kProviderHostProperty, base::Value(extension_id));
-  properties.SetKey(shill::kObjectPathSuffixProperty,
-                    base::Value(configuration->key()));
-  properties.SetKey(shill::kProviderTypeProperty,
-                    base::Value(shill::kProviderThirdPartyVpn));
-  properties.SetKey(shill::kProfileProperty, base::Value(profile->path));
-
-  // Note: This will not create an entry in |policy_util|. TODO(pneubeck):
-  // Determine the correct thing to do here, crbug.com/459278.
-  std::string guid = base::GenerateGUID();
-  properties.SetKey(shill::kGuidProperty, base::Value(guid));
+  base::Value::Dict properties;
+  properties.Set(shill::kTypeProperty, shill::kTypeVPN);
+  properties.Set(shill::kNameProperty, configuration_name);
+  properties.Set(shill::kProviderHostProperty, extension_id);
+  properties.Set(shill::kObjectPathSuffixProperty, configuration->key());
+  properties.Set(shill::kProviderTypeProperty, shill::kProviderThirdPartyVpn);
+  properties.Set(shill::kProfileProperty, profile->path);
+  properties.Set(shill::kGuidProperty, base::GenerateGUID());
 
   network_configuration_handler_->CreateShillConfiguration(
-      properties,
+      base::Value(std::move(properties)),
       base::BindOnce(&VpnService::OnCreateConfigurationSuccess,
                      weak_factory_.GetWeakPtr(), std::move(success),
                      configuration),
@@ -407,11 +407,11 @@
 }
 
 void VpnService::DestroyConfiguration(const std::string& extension_id,
-                                      const std::string& configuration_id,
+                                      const std::string& configuration_name,
                                       SuccessCallback success,
                                       FailureCallback failure) {
   // The ID is the configuration name for now. This may change in the future.
-  const std::string key = GetKey(extension_id, configuration_id);
+  const std::string key = GetKey(extension_id, configuration_name);
   if (!base::Contains(key_to_configuration_map_, key)) {
     std::move(failure).Run(std::string(), std::string("Unauthorized access."));
     return;
@@ -438,16 +438,18 @@
 }
 
 void VpnService::SetParameters(const std::string& extension_id,
-                               const base::DictionaryValue& parameters,
-                               StringCallback success,
+                               base::Value::Dict parameters,
+                               SuccessCallback success,
                                FailureCallback failure) {
   if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
     std::move(failure).Run(std::string(), std::string("Unauthorized access."));
     return;
   }
 
-  shill_client_->SetParameters(active_configuration_->object_path(), parameters,
-                               std::move(success), std::move(failure));
+  shill_client_->SetParameters(
+      active_configuration_->object_path(), base::Value(std::move(parameters)),
+      base::BindOnce(&RunWarningCallback, std::move(success)),
+      std::move(failure));
 }
 
 void VpnService::SendPacket(const std::string& extension_id,
diff --git a/extensions/browser/api/vpn_provider/vpn_service.h b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.h
similarity index 74%
rename from extensions/browser/api/vpn_provider/vpn_service.h
rename to chrome/browser/chromeos/extensions/vpn_provider/vpn_service.h
index c831a5af..43d22b2 100644
--- a/extensions/browser/api/vpn_provider/vpn_service.h
+++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service.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 EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_SERVICE_H_
-#define EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_SERVICE_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_H_
 
 #include <map>
 #include <memory>
@@ -13,12 +13,11 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_interface.h"
 #include "chromeos/network/network_configuration_observer.h"
 #include "chromeos/network/network_state_handler_observer.h"
-#include "components/keyed_service/core/keyed_service.h"
 #include "extensions/browser/extension_event_histogram_value.h"
 #include "extensions/browser/extension_registry_observer.h"
-#include "extensions/common/api/vpn_provider.h"
 
 namespace content {
 
@@ -43,17 +42,11 @@
 class ShillThirdPartyVpnDriverClient;
 
 // The class manages the VPN configurations.
-class VpnService : public KeyedService,
+class VpnService : public extensions::api::VpnServiceInterface,
                    public NetworkConfigurationObserver,
                    public NetworkStateHandlerObserver,
                    public extensions::ExtensionRegistryObserver {
  public:
-  using SuccessCallback = base::OnceClosure;
-  using StringCallback = base::OnceCallback<void(const std::string& result)>;
-  using FailureCallback =
-      base::OnceCallback<void(const std::string& error_name,
-                              const std::string& error_message)>;
-
   VpnService(content::BrowserContext* browser_context,
              const std::string& userid_hash,
              extensions::ExtensionRegistry* extension_registry,
@@ -68,13 +61,8 @@
 
   ~VpnService() override;
 
-  void SendShowAddDialogToExtension(const std::string& extension_id);
-
-  void SendShowConfigureDialogToExtension(const std::string& extension_id,
-                                          const std::string& configuration_id);
-
   void SendPlatformError(const std::string& extension_id,
-                         const std::string& configuration_id,
+                         const std::string& configuration_name,
                          const std::string& error_message);
 
   // NetworkConfigurationObserver:
@@ -92,47 +80,33 @@
                            const extensions::Extension* extension,
                            extensions::UnloadedExtensionReason reason) override;
 
-  // Creates a new VPN configuration with |configuration_name| as the name and
-  // attaches it to the extension with id |extension_id|.
-  // Calls |success| or |failure| based on the outcome.
+  // extensions::api::VpnServiceThinClientDelegate:
+  void SendShowAddDialogToExtension(const std::string& extension_id) override;
+  void SendShowConfigureDialogToExtension(
+      const std::string& extension_id,
+      const std::string& configuration_name) override;
   void CreateConfiguration(const std::string& extension_id,
-                           const std::string& extension_name,
                            const std::string& configuration_name,
-                           SuccessCallback success,
-                           FailureCallback failure);
-
-  // Destroys the VPN configuration with |configuration_id| after verifying that
-  // it belongs to the extension with id |extension_id|.
-  // Calls |success| or |failure| based on the outcome.
+                           SuccessCallback,
+                           FailureCallback) override;
   void DestroyConfiguration(const std::string& extension_id,
                             const std::string& configuration_id,
-                            SuccessCallback success,
-                            FailureCallback failure);
-
-  // Set |parameters| for the active VPN configuration after verifying that it
-  // belongs to the extension with id |extension_id|.
-  // Calls |success| or |failure| based on the outcome.
+                            SuccessCallback,
+                            FailureCallback) override;
   void SetParameters(const std::string& extension_id,
-                     const base::DictionaryValue& parameters,
-                     StringCallback success,
-                     FailureCallback failure);
-
-  // Sends an IP packet contained in |data| to the active VPN configuration
-  // after verifying that it belongs to the extension with id |extension_id|.
-  // Calls |success| or |failure| based on the outcome.
+                     base::Value::Dict parameters,
+                     SuccessCallback,
+                     FailureCallback) override;
   void SendPacket(const std::string& extension_id,
                   const std::vector<char>& data,
-                  SuccessCallback success,
-                  FailureCallback failure);
-
-  // Notifies connection state |state| to the active VPN configuration after
-  // verifying that it belongs to the extension with id |extension_id|.
-  // Calls |success| or |failure| based on the outcome.
+                  SuccessCallback,
+                  FailureCallback) override;
   void NotifyConnectionStateChanged(
       const std::string& extension_id,
-      extensions::api::vpn_provider::VpnConnectionState state,
-      SuccessCallback success,
-      FailureCallback failure);
+      extensions::api::vpn_provider::VpnConnectionState,
+      SuccessCallback,
+      FailureCallback) override;
+  std::unique_ptr<content::VpnServiceProxy> GetVpnServiceProxy() override;
 
   // Verifies if a configuration with name |configuration_name| exists for the
   // extension with id |extension_id|.
@@ -148,10 +122,6 @@
   static std::string GetKey(const std::string& extension_id,
                             const std::string& configuration_name);
 
-  // Creates a new VpnServiceProxy. The caller owns the returned value. It's
-  // valid to return nullptr.
-  std::unique_ptr<content::VpnServiceProxy> GetVpnServiceProxy();
-
   // Returns the single entry of |service_path_to_configuration_map_| for
   // testing (see VpnProviderApiTest);
   const std::string GetSingleServicepathForTesting();
@@ -243,4 +213,4 @@
 
 }  // namespace chromeos
 
-#endif  // EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_SERVICE_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_H_
diff --git a/extensions/browser/api/vpn_provider/vpn_service_factory.cc b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.cc
similarity index 88%
rename from extensions/browser/api/vpn_provider/vpn_service_factory.cc
rename to chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.cc
index 21583a6..1738067 100644
--- a/extensions/browser/api/vpn_provider/vpn_service_factory.cc
+++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.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 "extensions/browser/api/vpn_provider/vpn_service_factory.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h"
 
 #include "base/memory/singleton.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service.h"
 #include "chromeos/dbus/shill/shill_third_party_vpn_driver_client.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/network_handler.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "extensions/browser/api/vpn_provider/vpn_service.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extensions_browser_client.h"
@@ -17,9 +17,9 @@
 namespace chromeos {
 
 // static
-VpnService* VpnServiceFactory::GetForBrowserContext(
+VpnServiceInterface* VpnServiceFactory::GetForBrowserContext(
     content::BrowserContext* context) {
-  return static_cast<VpnService*>(
+  return static_cast<VpnServiceInterface*>(
       GetInstance()->GetServiceForBrowserContext(context, true));
 }
 
diff --git a/extensions/browser/api/vpn_provider/vpn_service_factory.h b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h
similarity index 69%
rename from extensions/browser/api/vpn_provider/vpn_service_factory.h
rename to chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h
index b371aeff..b9d5031f 100644
--- a/extensions/browser/api/vpn_provider/vpn_service_factory.h
+++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_SERVICE_FACTORY_H_
-#define EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_SERVICE_FACTORY_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_FACTORY_H_
 
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_interface.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 namespace content {
@@ -20,7 +21,7 @@
 
 namespace chromeos {
 
-class VpnService;
+using VpnServiceInterface = extensions::api::VpnServiceInterface;
 
 // Factory to create VpnService.
 class VpnServiceFactory : public BrowserContextKeyedServiceFactory {
@@ -28,7 +29,8 @@
   VpnServiceFactory(const VpnServiceFactory&) = delete;
   VpnServiceFactory& operator=(const VpnServiceFactory&) = delete;
 
-  static VpnService* GetForBrowserContext(content::BrowserContext* context);
+  static VpnServiceInterface* GetForBrowserContext(
+      content::BrowserContext* context);
   static VpnServiceFactory* GetInstance();
 
  private:
@@ -46,4 +48,4 @@
 
 }  // namespace chromeos
 
-#endif  // EXTENSIONS_BROWSER_API_VPN_PROVIDER_VPN_SERVICE_FACTORY_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_FACTORY_H_
diff --git a/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_interface.h b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_interface.h
new file mode 100644
index 0000000..beac89dea
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/vpn_provider/vpn_service_interface.h
@@ -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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_INTERFACE_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_INTERFACE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/values.h"
+#include "chrome/common/extensions/api/vpn_provider.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace content {
+
+class VpnServiceProxy;
+
+}  // namespace content
+
+namespace extensions::api {
+
+// This class is the basic interface for chrome.vpnProvider extension API
+// methods.
+class VpnServiceInterface : public KeyedService {
+ public:
+  using SuccessCallback = base::OnceClosure;
+  using FailureCallback =
+      base::OnceCallback<void(const std::string& error_name,
+                              const std::string& error_message)>;
+
+  // Sends UIEvent.showAddDialog to the extension with |extension_id|.
+  virtual void SendShowAddDialogToExtension(
+      const std::string& extension_id) = 0;
+
+  // Sends UIEvent.showConfigureDialog for |configuration_name| to
+  // the extension with |extension_id|.
+  virtual void SendShowConfigureDialogToExtension(
+      const std::string& extension_id,
+      const std::string& configuration_name) = 0;
+
+  // Creates a new VPN configuration with |configuration_name| as the name and
+  // attaches it to the extension with id |extension_id|.
+  // Calls |success| or |failure| based on the outcome.
+  virtual void CreateConfiguration(const std::string& extension_id,
+                                   const std::string& configuration_name,
+                                   SuccessCallback,
+                                   FailureCallback) = 0;
+
+  // Destroys the VPN configuration with |configuration_name| after verifying
+  // that it belongs to the extension with id |extension_id|. Calls |success| or
+  // |failure| based on the outcome.
+  virtual void DestroyConfiguration(const std::string& extension_id,
+                                    const std::string& configuration_name,
+                                    SuccessCallback,
+                                    FailureCallback) = 0;
+
+  // Set |parameters| for the active VPN configuration after verifying that it
+  // belongs to the extension with id |extension_id|.
+  // Calls |success| or |failure| based on the outcome.
+  virtual void SetParameters(const std::string& extension_id,
+                             base::Value::Dict parameters,
+                             SuccessCallback,
+                             FailureCallback) = 0;
+
+  // Sends an IP packet contained in |data| to the active VPN configuration
+  // after verifying that it belongs to the extension with id |extension_id|.
+  // Calls |success| or |failure| based on the outcome.
+  virtual void SendPacket(const std::string& extension_id,
+                          const std::vector<char>& data,
+                          SuccessCallback,
+                          FailureCallback) = 0;
+
+  // Notifies connection state |state| to the active VPN configuration after
+  // verifying that it belongs to the extension with id |extension_id|.
+  // Calls |success| or |failure| based on the outcome.
+  virtual void NotifyConnectionStateChanged(
+      const std::string& extension_id,
+      extensions::api::vpn_provider::VpnConnectionState,
+      SuccessCallback,
+      FailureCallback) = 0;
+
+  // Returns a VpnServiceProxy that is used by Pepper API.
+  virtual std::unique_ptr<content::VpnServiceProxy> GetVpnServiceProxy() = 0;
+};
+
+}  // namespace extensions::api
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_VPN_PROVIDER_VPN_SERVICE_INTERFACE_H_
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 8a43db0..6d242c5 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1125,6 +1125,7 @@
       "//ash/webui/resources:media_app_bundle_resources_grit",
       "//chrome/browser/ash/crosapi",
       "//chrome/browser/ash/crostini:crostini_installer_types_mojom",
+      "//chrome/browser/chromeos/extensions/vpn_provider",
       "//chrome/browser/devtools",
       "//chrome/browser/nearby_sharing/common",
       "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
diff --git a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
index 672728ad..c04959d2 100644
--- a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
+++ b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
@@ -10,6 +10,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h"
@@ -21,9 +24,6 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
-#include "extensions/browser/api/vpn_provider/vpn_provider_api.h"
-#include "extensions/browser/api/vpn_provider/vpn_service.h"
-#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/extension.h"
 #include "extensions/test/result_catcher.h"
@@ -116,8 +116,8 @@
 
 class VpnProviderApiTest : public extensions::ExtensionApiTest {
  public:
-  VpnProviderApiTest() {}
-  ~VpnProviderApiTest() override {}
+  VpnProviderApiTest() = default;
+  ~VpnProviderApiTest() override = default;
 
   void SetUpInProcessBrowserTestFixture() override {
     extensions::ExtensionApiTest::SetUpInProcessBrowserTestFixture();
@@ -136,7 +136,8 @@
   void LoadVpnExtension() {
     extension_ = LoadExtension(test_data_dir_.AppendASCII("vpn_provider"));
     extension_id_ = extension_->id();
-    service_ = VpnServiceFactory::GetForBrowserContext(profile());
+    service_ = static_cast<chromeos::VpnService*>(
+        VpnServiceFactory::GetForBrowserContext(profile()));
     content::RunAllPendingInMessageLoop();
   }
 
@@ -335,18 +336,17 @@
   AddNetworkProfileForUser();
   EXPECT_FALSE(DoesConfigExist(kTestConfig));
 
-  base::DictionaryValue properties;
-  properties.SetKey(shill::kTypeProperty, base::Value(shill::kTypeVPN));
-  properties.SetKey(shill::kNameProperty, base::Value(kTestConfig));
-  properties.SetKey(shill::kProviderHostProperty, base::Value(extension_id_));
-  properties.SetKey(shill::kObjectPathSuffixProperty,
-                    base::Value(GetKey(kTestConfig)));
-  properties.SetKey(shill::kProviderTypeProperty,
-                    base::Value(shill::kProviderThirdPartyVpn));
-  properties.SetKey(shill::kProfileProperty, base::Value(kNetworkProfilePath));
+  base::Value::Dict properties;
+  properties.Set(shill::kTypeProperty, shill::kTypeVPN);
+  properties.Set(shill::kNameProperty, kTestConfig);
+  properties.Set(shill::kProviderHostProperty, extension_id_);
+  properties.Set(shill::kObjectPathSuffixProperty, GetKey(kTestConfig));
+  properties.Set(shill::kProviderTypeProperty, shill::kProviderThirdPartyVpn);
+  properties.Set(shill::kProfileProperty, kNetworkProfilePath);
+
   NetworkHandler::Get()
       ->network_configuration_handler()
-      ->CreateShillConfiguration(properties,
+      ->CreateShillConfiguration(base::Value(std::move(properties)),
                                  base::BindOnce(DoNothingSuccessCallback),
                                  base::BindOnce(DoNothingFailureCallback));
   content::RunAllPendingInMessageLoop();
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
index 2d962229..3f40faa6 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -75,8 +75,7 @@
 #include "url/origin.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "extensions/browser/api/vpn_provider/vpn_service.h"
-#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 using blink::web_pref::WebPreferences;
@@ -219,12 +218,10 @@
 }  // namespace
 
 ChromeContentBrowserClientExtensionsPart::
-    ChromeContentBrowserClientExtensionsPart() {
-}
+    ChromeContentBrowserClientExtensionsPart() = default;
 
 ChromeContentBrowserClientExtensionsPart::
-    ~ChromeContentBrowserClientExtensionsPart() {
-}
+    ~ChromeContentBrowserClientExtensionsPart() = default;
 
 // static
 GURL ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(
@@ -351,7 +348,8 @@
 
 // static
 bool ChromeContentBrowserClientExtensionsPart::CanCommitURL(
-    content::RenderProcessHost* process_host, const GURL& url) {
+    content::RenderProcessHost* process_host,
+    const GURL& url) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Enforce that extension URLs commit in the correct extension process where
@@ -573,7 +571,7 @@
 ChromeContentBrowserClientExtensionsPart::GetVpnServiceProxy(
     content::BrowserContext* browser_context) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  chromeos::VpnService* vpn_service =
+  chromeos::VpnServiceInterface* vpn_service =
       chromeos::VpnServiceFactory::GetForBrowserContext(browser_context);
   if (!vpn_service)
     return nullptr;
diff --git a/chrome/browser/first_run/upgrade_util_win.cc b/chrome/browser/first_run/upgrade_util_win.cc
index 563d5e1..3837a74d7 100644
--- a/chrome/browser/first_run/upgrade_util_win.cc
+++ b/chrome/browser/first_run/upgrade_util_win.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/first_run/upgrade_util.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/browser/win/browser_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/install_static/install_util.h"
@@ -231,17 +232,26 @@
 bool DoUpgradeTasks(const base::CommandLine& command_line) {
   TRACE_EVENT0("startup", "upgrade_util::DoUpgradeTasks");
   const auto begin_time = base::TimeTicks::Now();
-  if (!SwapNewChromeExeIfPresent() && !IsRunningOldChrome()) {
+  // If there is no other instance already running then check if there is a
+  // pending update and complete it by performing the swap and then relaunch.
+  bool did_swap = false;
+  if (!browser_util::IsBrowserAlreadyRunning())
+    did_swap = SwapNewChromeExeIfPresent();
+
+  // We don't need to relaunch if we didn't swap and we aren't running stale
+  // binaries.
+  if (!did_swap && !IsRunningOldChrome()) {
     UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.NoRelaunch",
                                base::TimeTicks::Now() - begin_time);
     return false;
   }
+
   // At this point the chrome.exe has been swapped with the new one.
   if (RelaunchChromeBrowser(command_line)) {
     UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.RelaunchSucceeded",
                                base::TimeTicks::Now() - begin_time);
   } else {
-    // The re-launch failed. Feel free to panic now.
+    // The relaunch failed. Feel free to panic now.
     NOTREACHED();
     // Log a metric anyways to see if this is at fault in crbug.com/1252004
     UMA_HISTOGRAM_MEDIUM_TIMES("Startup.DoUpgradeTasks.RelaunchFailed",
diff --git a/chrome/browser/first_run/upgrade_util_win.h b/chrome/browser/first_run/upgrade_util_win.h
index e61a7e64..ce6a9a8 100644
--- a/chrome/browser/first_run/upgrade_util_win.h
+++ b/chrome/browser/first_run/upgrade_util_win.h
@@ -11,9 +11,16 @@
 
 namespace upgrade_util {
 
-// If the new_chrome.exe exists (placed by the installer then is swapped
-// to chrome.exe and the old chrome is renamed to old_chrome.exe. If there
-// is no new_chrome.exe or the swap fails the return is false;
+// This function is called from chrome.exe and checks if new_chrome.exe exists
+// and if so, it attempts to swap it to become chrome.exe. new_chrome.exe is
+// placed by the installer if there is an update while the browser is being
+// used. If there is no new_chrome.exe or the swap fails the function returns
+// false.
+// To peform the swap, chrome.exe is renamed to old_chrome.exe and moved to a
+// temporary folder, then new_chrome.exe is renamed to chrome.exe. The instance
+// performing the swap runs as old_chrome.exe until it exits.
+// The caller must own the browser ChromeProcessSingleton.
+// TODO: enforce via DCHECK that the caller owns the ChromeProcessSingleton.
 bool SwapNewChromeExeIfPresent();
 
 // Returns true if the currently running browser is running from old_chrome.exe.
@@ -30,6 +37,8 @@
 // existing browser process. If this returns true before message loop is
 // executed, simply exit the main function. If browser is already running, you
 // will need to exit it.
+// The caller must own the browser ChromeProcessSingleton.
+// TODO: enforce via DCHECK that the caller owns the ChromeProcessSingleton.
 bool DoUpgradeTasks(const base::CommandLine& command_line);
 
 }  // namespace upgrade_util
diff --git a/chrome/browser/headless/headless_mode_browsertest.cc b/chrome/browser/headless/headless_mode_browsertest.cc
index 3d91f41..55756eb 100644
--- a/chrome/browser/headless/headless_mode_browsertest.cc
+++ b/chrome/browser/headless/headless_mode_browsertest.cc
@@ -26,6 +26,8 @@
 #include "chrome/browser/headless/headless_mode_util.h"
 #include "chrome/browser/process_singleton.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/exclusive_access/exclusive_access_test.h"
 #include "chrome/common/chrome_switches.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_task_environment.h"
@@ -73,6 +75,12 @@
   }
 }
 
+void ToggleFullscreenModeSync(Browser* browser) {
+  FullscreenNotificationObserver observer(browser);
+  chrome::ToggleFullscreenMode(browser);
+  observer.Wait();
+}
+
 #if BUILDFLAG(IS_LINUX)
 IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTest, OzonePlatformHeadless) {
   // On Linux, the Native Headless Chrome uses Ozone/Headless.
diff --git a/chrome/browser/headless/headless_mode_browsertest.h b/chrome/browser/headless/headless_mode_browsertest.h
index 6bbfa63..930ab42 100644
--- a/chrome/browser/headless/headless_mode_browsertest.h
+++ b/chrome/browser/headless/headless_mode_browsertest.h
@@ -48,4 +48,7 @@
   StartWindowMode start_window_mode() const { return GetParam(); };
 };
 
+// Toggles browser fullscreen mode synchronously.
+void ToggleFullscreenModeSync(Browser* browser);
+
 #endif  // CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_H_
diff --git a/chrome/browser/headless/headless_mode_browsertest_win.cc b/chrome/browser/headless/headless_mode_browsertest_win.cc
index 4fa86cc..9257f6f 100644
--- a/chrome/browser/headless/headless_mode_browsertest_win.cc
+++ b/chrome/browser/headless/headless_mode_browsertest_win.cc
@@ -33,3 +33,28 @@
           browser()->window()->GetNativeWindow()->GetHost());
   EXPECT_FALSE(::IsWindowVisible(desktop_window_tree_host->GetHWND()));
 }
+
+IN_PROC_BROWSER_TEST_F(HeadlessModeBrowserTest,
+                       ToggleFullscreenWindowVisibility) {
+  DesktopWindowTreeHostWinWrapper* desktop_window_tree_host =
+      static_cast<DesktopWindowTreeHostWinWrapper*>(
+          browser()->window()->GetNativeWindow()->GetHost());
+  HWND desktop_window_hwnd = desktop_window_tree_host->GetHWND();
+
+  // Verify initial state.
+  ASSERT_FALSE(browser()->window()->IsFullscreen());
+  EXPECT_TRUE(browser()->window()->IsVisible());
+  EXPECT_FALSE(::IsWindowVisible(desktop_window_hwnd));
+
+  // Verify fullscreen state.
+  ToggleFullscreenModeSync(browser());
+  ASSERT_TRUE(browser()->window()->IsFullscreen());
+  EXPECT_TRUE(browser()->window()->IsVisible());
+  EXPECT_FALSE(::IsWindowVisible(desktop_window_hwnd));
+
+  // Verify back to normal state.
+  ToggleFullscreenModeSync(browser());
+  ASSERT_FALSE(browser()->window()->IsFullscreen());
+  EXPECT_TRUE(browser()->window()->IsVisible());
+  EXPECT_FALSE(::IsWindowVisible(desktop_window_hwnd));
+}
diff --git a/chrome/browser/hid/chrome_hid_delegate.cc b/chrome/browser/hid/chrome_hid_delegate.cc
index da8f8215..46ed71e 100644
--- a/chrome/browser/hid/chrome_hid_delegate.cc
+++ b/chrome/browser/hid/chrome_hid_delegate.cc
@@ -18,8 +18,8 @@
 
 namespace {
 
-HidChooserContext* GetChooserContext(content::RenderFrameHost* frame) {
-  auto* profile = Profile::FromBrowserContext(frame->GetBrowserContext());
+HidChooserContext* GetChooserContext(content::BrowserContext* browser_context) {
+  auto* profile = Profile::FromBrowserContext(browser_context);
   return HidChooserContextFactory::GetForProfile(profile);
 }
 
@@ -34,7 +34,9 @@
     std::vector<blink::mojom::HidDeviceFilterPtr> filters,
     std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
     content::HidChooser::Callback callback) {
-  auto* chooser_context = GetChooserContext(render_frame_host);
+  DCHECK(render_frame_host);
+  auto* chooser_context =
+      GetChooserContext(render_frame_host->GetBrowserContext());
   if (!device_observation_.IsObserving())
     device_observation_.Observe(chooser_context);
   if (!permission_observation_.IsObserving())
@@ -48,49 +50,36 @@
 }
 
 bool ChromeHidDelegate::CanRequestDevicePermission(
-    content::RenderFrameHost* render_frame_host) {
-  // The use below of GetMainFrame is safe as content::HidService instances are
-  // not created for fenced frames.
-  DCHECK(!render_frame_host->IsNestedWithinFencedFrame());
-
-  auto* chooser_context = GetChooserContext(render_frame_host);
-  const auto& origin =
-      render_frame_host->GetMainFrame()->GetLastCommittedOrigin();
-  return chooser_context->CanRequestObjectPermission(origin);
+    content::BrowserContext* browser_context,
+    const url::Origin& origin) {
+  return GetChooserContext(browser_context)->CanRequestObjectPermission(origin);
 }
 
 bool ChromeHidDelegate::HasDevicePermission(
-    content::RenderFrameHost* render_frame_host,
+    content::BrowserContext* browser_context,
+    const url::Origin& origin,
     const device::mojom::HidDeviceInfo& device) {
-  // The use below of GetMainFrame is safe as content::HidService instances are
-  // not created for fenced frames.
-  DCHECK(!render_frame_host->IsNestedWithinFencedFrame());
-
-  auto* chooser_context = GetChooserContext(render_frame_host);
-  const auto& origin =
-      render_frame_host->GetMainFrame()->GetLastCommittedOrigin();
-  return chooser_context->HasDevicePermission(origin, device);
+  return GetChooserContext(browser_context)
+      ->HasDevicePermission(origin, device);
 }
 
 void ChromeHidDelegate::RevokeDevicePermission(
-    content::RenderFrameHost* render_frame_host,
+    content::BrowserContext* browser_context,
+    const url::Origin& origin,
     const device::mojom::HidDeviceInfo& device) {
-  auto* chooser_context = GetChooserContext(render_frame_host);
-  const auto& origin =
-      render_frame_host->GetMainFrame()->GetLastCommittedOrigin();
-  return chooser_context->RevokeDevicePermission(origin, device);
+  return GetChooserContext(browser_context)
+      ->RevokeDevicePermission(origin, device);
 }
 
 device::mojom::HidManager* ChromeHidDelegate::GetHidManager(
-    content::RenderFrameHost* render_frame_host) {
-  auto* chooser_context = GetChooserContext(render_frame_host);
-  return chooser_context->GetHidManager();
+    content::BrowserContext* browser_context) {
+  return GetChooserContext(browser_context)->GetHidManager();
 }
 
-void ChromeHidDelegate::AddObserver(content::RenderFrameHost* render_frame_host,
+void ChromeHidDelegate::AddObserver(content::BrowserContext* browser_context,
                                     Observer* observer) {
   observer_list_.AddObserver(observer);
-  auto* chooser_context = GetChooserContext(render_frame_host);
+  auto* chooser_context = GetChooserContext(browser_context);
   if (!device_observation_.IsObserving())
     device_observation_.Observe(chooser_context);
   if (!permission_observation_.IsObserving())
@@ -98,22 +87,21 @@
 }
 
 void ChromeHidDelegate::RemoveObserver(
-    content::RenderFrameHost* render_frame_host,
     content::HidDelegate::Observer* observer) {
   observer_list_.RemoveObserver(observer);
 }
 
 const device::mojom::HidDeviceInfo* ChromeHidDelegate::GetDeviceInfo(
-    content::RenderFrameHost* render_frame_host,
+    content::BrowserContext* browser_context,
     const std::string& guid) {
-  auto* chooser_context = GetChooserContext(render_frame_host);
+  auto* chooser_context = GetChooserContext(browser_context);
   return chooser_context->GetDeviceInfo(guid);
 }
 
 bool ChromeHidDelegate::IsFidoAllowedForOrigin(
-    content::RenderFrameHost* render_frame_host,
+    content::BrowserContext* browser_context,
     const url::Origin& origin) {
-  auto* chooser_context = GetChooserContext(render_frame_host);
+  auto* chooser_context = GetChooserContext(browser_context);
   return chooser_context->IsFidoAllowedForOrigin(origin);
 }
 
diff --git a/chrome/browser/hid/chrome_hid_delegate.h b/chrome/browser/hid/chrome_hid_delegate.h
index e221684..6635633 100644
--- a/chrome/browser/hid/chrome_hid_delegate.h
+++ b/chrome/browser/hid/chrome_hid_delegate.h
@@ -29,23 +29,24 @@
       std::vector<blink::mojom::HidDeviceFilterPtr> filters,
       std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
       content::HidChooser::Callback callback) override;
-  bool CanRequestDevicePermission(
-      content::RenderFrameHost* render_frame_host) override;
-  bool HasDevicePermission(content::RenderFrameHost* render_frame_host,
+  bool CanRequestDevicePermission(content::BrowserContext* browser_context,
+                                  const url::Origin& origin) override;
+  bool HasDevicePermission(content::BrowserContext* browser_context,
+                           const url::Origin& origin,
                            const device::mojom::HidDeviceInfo& device) override;
   void RevokeDevicePermission(
-      content::RenderFrameHost* render_frame_host,
+      content::BrowserContext* browser_context,
+      const url::Origin& origin,
       const device::mojom::HidDeviceInfo& device) override;
   device::mojom::HidManager* GetHidManager(
-      content::RenderFrameHost* render_frame_host) override;
-  void AddObserver(content::RenderFrameHost* render_frame_host,
+      content::BrowserContext* browser_context) override;
+  void AddObserver(content::BrowserContext* browser_context,
                    content::HidDelegate::Observer* observer) override;
-  void RemoveObserver(content::RenderFrameHost* render_frame_host,
-                      content::HidDelegate::Observer* observer) override;
+  void RemoveObserver(content::HidDelegate::Observer* observer) override;
   const device::mojom::HidDeviceInfo* GetDeviceInfo(
-      content::RenderFrameHost* render_frame_host,
+      content::BrowserContext* browser_context,
       const std::string& guid) override;
-  bool IsFidoAllowedForOrigin(content::RenderFrameHost* render_frame_host,
+  bool IsFidoAllowedForOrigin(content::BrowserContext* browser_context,
                               const url::Origin& origin) override;
 
   // permissions::ObjectPermissionContextBase::PermissionObserver:
diff --git a/chrome/browser/history_clusters/history_clusters_bridge.cc b/chrome/browser/history_clusters/history_clusters_bridge.cc
index 31d84c3..d6aaa5d6 100644
--- a/chrome/browser/history_clusters/history_clusters_bridge.cc
+++ b/chrome/browser/history_clusters/history_clusters_bridge.cc
@@ -111,6 +111,9 @@
                   env, visit.annotated_visit.url_row.title()));
       cluster_visits.push_back(j_cluster_visit);
     }
+    base::Time visit_time;
+    if (!cluster.visits.empty())
+      visit_time = cluster.visits[0].annotated_visit.visit_row.visit_time;
     ScopedJavaLocalRef<jclass> cluster_visit_type = base::android::GetClass(
         env, "org/chromium/chrome/browser/history_clusters/ClusterVisit");
     std::u16string label = cluster.label.value_or(u"no_label");
@@ -131,7 +134,8 @@
             base::android::ToJavaArrayOfStrings(env, cluster.keywords),
             base::android::ConvertUTF16ToJavaString(env, label),
             base::android::ToJavaIntArray(env, label_match_starts),
-            base::android::ToJavaIntArray(env, label_match_ends));
+            base::android::ToJavaIntArray(env, label_match_ends),
+            visit_time.ToJavaTime());
     j_clusters.push_back(j_cluster);
   }
   ScopedJavaLocalRef<jclass> cluster_type = base::android::GetClass(
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryCluster.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryCluster.java
index 13e716c..755f043 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryCluster.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryCluster.java
@@ -21,13 +21,15 @@
     private final List<ClusterVisit> mVisits;
     private final String mLabel;
     private final List<MatchPosition> mMatchPositions;
+    private final long mTimestamp;
 
     public HistoryCluster(List<String> keywords, List<ClusterVisit> visits, String label,
-            List<MatchPosition> matchPositions) {
+            List<MatchPosition> matchPositions, long timestamp) {
         mKeywords = keywords;
         mVisits = visits;
         mLabel = label;
         mMatchPositions = matchPositions;
+        mTimestamp = timestamp;
     }
 
     public List<ClusterVisit> getVisits() {
@@ -37,4 +39,8 @@
     public String getLabel() {
         return mLabel;
     }
+
+    public long getTimestamp() {
+        return mTimestamp;
+    }
 }
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java
index c70af5c..7b78b6e 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClusterView.java
@@ -27,10 +27,14 @@
     @Override
     protected void onClick() {}
 
-    void setLabel(String text) {
+    void setTitle(String text) {
         mTitleView.setText(text);
     }
 
+    void setLabel(String text) {
+        mDescriptionView.setText(text);
+    }
+
     void setIconDrawable(Drawable drawable) {
         mStartIconView.setImageDrawable(drawable);
     }
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java
index 91a6f75..a597d6f 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersBridge.java
@@ -62,7 +62,7 @@
 
     @CalledByNative
     static HistoryCluster buildCluster(ClusterVisit[] visits, String[] keywords, String label,
-            int[] labelMatchStarts, int[] labelMatchEnds) {
+            int[] labelMatchStarts, int[] labelMatchEnds, long timestamp) {
         List<String> keywordList = Arrays.asList(keywords);
         List<ClusterVisit> clusterVisitList = Arrays.asList(visits);
 
@@ -73,7 +73,7 @@
             matchPositions.add(matchPosition);
         }
 
-        return new HistoryCluster(keywordList, clusterVisitList, label, matchPositions);
+        return new HistoryCluster(keywordList, clusterVisitList, label, matchPositions, timestamp);
     }
 
     @CalledByNative
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
index d3d22e9f..71f98a9 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
@@ -73,7 +73,7 @@
         mMediator = new HistoryClustersMediator(HistoryClustersBridge.getForProfile(profile),
                 new LargeIconBridge(profile), context, context.getResources(), mModelList,
                 mToolbarModel, historyActivityIntentFactory, tabSupplier, tabSupplier == null,
-                openUrlIntentCreator);
+                openUrlIntentCreator, System::currentTimeMillis);
     }
 
     public void destroy() {
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
index 1a7224620..2941eda 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersMediator.java
@@ -39,8 +39,13 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 class HistoryClustersMediator implements SearchDelegate {
+    interface Clock {
+        long currentTimeMillis();
+    }
+
     private final HistoryClustersBridge mHistoryClustersBridge;
     private final Context mContext;
     private final Resources mResources;
@@ -55,6 +60,7 @@
     private final boolean mIsSeparateActivity;
     private Function<GURL, Intent> mOpenUrlIntentCreator;
     private CallbackController mCallbackController = new CallbackController();
+    private Clock mClock;
 
     /**
      * Create a new HistoryClustersMediator.
@@ -73,12 +79,13 @@
      *         should launch an intent or directly navigate a tab.
      * @param openUrlIntentCreator Function that creates an intent that opens the given url in the
      *         correct main browsing activity.
+     * @param clock Provider of the current time in ms relative to the unix epoch.
      */
     HistoryClustersMediator(@NonNull HistoryClustersBridge historyClustersBridge,
             LargeIconBridge largeIconBridge, @NonNull Context context, @NonNull Resources resources,
             @NonNull ModelList modelList, @NonNull PropertyModel toolbarModel,
             Supplier<Intent> historyActivityIntentFactory, @Nullable Supplier<Tab> tabSupplier,
-            boolean isSeparateActivity, Function<GURL, Intent> openUrlIntentCreator) {
+            boolean isSeparateActivity, Function<GURL, Intent> openUrlIntentCreator, Clock clock) {
         mHistoryClustersBridge = historyClustersBridge;
         mLargeIconBridge = largeIconBridge;
         mModelList = modelList;
@@ -91,6 +98,7 @@
         mIconGenerator = FaviconUtils.createCircularIconGenerator(mContext);
         mIsSeparateActivity = isSeparateActivity;
         mOpenUrlIntentCreator = openUrlIntentCreator;
+        mClock = clock;
     }
 
     // SearchDelegate implementation.
@@ -149,7 +157,7 @@
         boolean isQueryless = result.getQuery().isEmpty();
         for (HistoryCluster cluster : result.getClusters()) {
             PropertyModel clusterModel = new PropertyModel(HistoryClustersItemProperties.ALL_KEYS);
-            clusterModel.set(HistoryClustersItemProperties.LABEL, cluster.getLabel());
+            clusterModel.set(HistoryClustersItemProperties.TITLE, cluster.getLabel());
             Drawable journeysDrawable = UiUtils.getTintedDrawable(
                     mContext, R.drawable.ic_journeys, R.color.default_icon_color_tint_list);
             clusterModel.set(HistoryClustersItemProperties.ICON_DRAWABLE, journeysDrawable);
@@ -190,6 +198,8 @@
             Drawable chevron = UiUtils.getTintedDrawable(mContext,
                     R.drawable.ic_expand_more_black_24dp, R.color.default_icon_color_tint_list);
             clusterModel.set(HistoryClustersItemProperties.END_BUTTON_DRAWABLE, chevron);
+            clusterModel.set(
+                    HistoryClustersItemProperties.LABEL, getTimeString(cluster.getTimestamp()));
         }
     }
 
@@ -234,4 +244,25 @@
         LoadUrlParams loadUrlParams = new LoadUrlParams(gurl);
         currentTab.loadUrl(loadUrlParams);
     }
+
+    @VisibleForTesting
+    String getTimeString(long timestampMillis) {
+        long timeDeltaMs = mClock.currentTimeMillis() - timestampMillis;
+        if (timeDeltaMs < 0) timeDeltaMs = 0;
+
+        int daysElapsed = (int) TimeUnit.MILLISECONDS.toDays(timeDeltaMs);
+        int hoursElapsed = (int) TimeUnit.MILLISECONDS.toHours(timeDeltaMs);
+        int minutesElapsed = (int) TimeUnit.MILLISECONDS.toMinutes(timeDeltaMs);
+
+        if (daysElapsed > 0) {
+            return mResources.getQuantityString(R.plurals.n_days_ago, daysElapsed, daysElapsed);
+        } else if (hoursElapsed > 0) {
+            return mResources.getQuantityString(R.plurals.n_hours_ago, hoursElapsed, hoursElapsed);
+        } else if (minutesElapsed > 0) {
+            return mResources.getQuantityString(
+                    R.plurals.n_minutes_ago, minutesElapsed, minutesElapsed);
+        } else {
+            return mResources.getString(R.string.just_now);
+        }
+    }
 }
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersViewBinder.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersViewBinder.java
index 9e504fd..d8ad8c1 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersViewBinder.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersViewBinder.java
@@ -65,6 +65,8 @@
             clusterView.setIconDrawable(model.get(HistoryClustersItemProperties.ICON_DRAWABLE));
         } else if (key == HistoryClustersItemProperties.LABEL) {
             clusterView.setLabel(model.get(HistoryClustersItemProperties.LABEL));
+        } else if (key == HistoryClustersItemProperties.TITLE) {
+            clusterView.setTitle(model.get(HistoryClustersItemProperties.TITLE));
         }
     }
 }
\ No newline at end of file
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java
index a008357..23058571 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java
@@ -6,7 +6,6 @@
 
 import android.app.Activity;
 import android.content.res.Resources;
-import android.os.SystemClock;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -56,7 +55,6 @@
     private PropertyModel mLoadingModal;
     private LanguageItemAdapter mAdapter;
     private RestartAction mRestartAction;
-    private long mStartTime;
 
     /** Annotation for row item type. Either a LanguageItem or separator */
     @IntDef({ItemType.LANGUAGE, ItemType.SEPARATOR, ItemType.MORE_LANGUAGES})
@@ -368,26 +366,24 @@
         });
 
         mAppLanguageModal.set(ModalDialogProperties.CUSTOM_VIEW, customView);
-        mStartTime = SystemClock.elapsedRealtime();
         mModalDialogManager.showDialog(mAppLanguageModal, ModalDialogManager.ModalDialogType.APP);
     }
 
     public void onDismissAppLanguageModal(@DialogDismissalCause int dismissalCause) {
-        long displayTime = SystemClock.elapsedRealtime() - mStartTime;
         if (dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED) {
             String languageCode = mAdapter.getSelectedLanguage().getCode();
             if (AppLocaleUtils.isAppLanguagePref(languageCode)) {
-                recordDismissAction(ActionType.OK_SAME_LANGUAGE, displayTime);
+                recordDismissAction(ActionType.OK_SAME_LANGUAGE);
             } else {
-                recordDismissAction(ActionType.OK_CHANGE_LANGUAGE, displayTime);
+                recordDismissAction(ActionType.OK_CHANGE_LANGUAGE);
             }
             startAppLanguageInstall();
         } else if (dismissalCause == DialogDismissalCause.NEGATIVE_BUTTON_CLICKED) {
-            recordDismissAction(ActionType.DISMISSED_CANCEL_BUTTON, displayTime);
+            recordDismissAction(ActionType.DISMISSED_CANCEL_BUTTON);
         } else if (dismissalCause == DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE) {
-            recordDismissAction(ActionType.DISMISSED_SYSTEM_BACK, displayTime);
+            recordDismissAction(ActionType.DISMISSED_SYSTEM_BACK);
         } else {
-            recordDismissAction(ActionType.OTHER, displayTime);
+            recordDismissAction(ActionType.OTHER);
         }
         recordOtherLanguagesShown(mAdapter.areOtherLanguagesShown());
         TranslateBridge.setAppLanguagePromptShown();
@@ -587,27 +583,10 @@
     /**
      * Record the action type when dismissing the dialog and how long the dialog was shown for.
      * @param @ActionType int.
-     * @param displayTime Time in ms that the app language promo dialog is showing for.
      */
-    private static void recordDismissAction(@ActionType int actionType, long displayTime) {
+    private static void recordDismissAction(@ActionType int actionType) {
         RecordHistogram.recordEnumeratedHistogram(
                 "LanguageSettings.AppLanguagePrompt.Action", actionType, ActionType.NUM_ENTRIES);
-        switch (actionType) {
-            case ActionType.DISMISSED_CANCEL_BUTTON:
-                recordOpenDuration("Cancel", displayTime);
-                break;
-            case ActionType.DISMISSED_SYSTEM_BACK:
-                recordOpenDuration("Back", displayTime);
-                break;
-            case ActionType.OK_CHANGE_LANGUAGE:
-                recordOpenDuration("Change", displayTime);
-                break;
-            case ActionType.OK_SAME_LANGUAGE:
-                recordOpenDuration("Same", displayTime);
-                break;
-            default:
-                // Do not record a time for other action types.
-        }
     }
 
     private static void recordOnlineStatus(boolean isOnline) {
@@ -620,11 +599,6 @@
                 "LanguageSettings.AppLanguagePrompt.HasTopULPMatch", hasMatch);
     }
 
-    private static void recordOpenDuration(String type, long displayTime) {
-        RecordHistogram.recordLongTimesHistogram100(
-                "LanguageSettings.AppLanguagePrompt.OpenDuration." + type, displayTime);
-    }
-
     private static void recordIsTopLanguage(boolean isTopLanguage) {
         RecordHistogram.recordBooleanHistogram(
                 "LanguageSettings.AppLanguagePrompt.IsTopLanguageSelected", isTopLanguage);
diff --git a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc
index 83026a3..d44ebd3c4 100644
--- a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc
+++ b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc
@@ -17,11 +17,11 @@
 namespace segmentation_platform {
 
 namespace {
-using optimization_guide::proto::OptimizationTarget;
+using proto::SegmentId;
 
 // Default parameters for Chrome Start model.
-constexpr OptimizationTarget kChromeStartOptimizationTarget =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID;
+constexpr SegmentId kChromeStartSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID;
 constexpr proto::TimeUnit kChromeStartTimeUnit = proto::TimeUnit::DAY;
 constexpr uint64_t kChromeStartBucketDuration = 1;
 constexpr int64_t kChromeStartSignalStorageLength = 28;
@@ -59,8 +59,7 @@
 
 }  // namespace
 
-ChromeStartModel::ChromeStartModel()
-    : ModelProvider(kChromeStartOptimizationTarget) {}
+ChromeStartModel::ChromeStartModel() : ModelProvider(kChromeStartSegmentId) {}
 
 void ChromeStartModel::InitAndFetchModel(
     const ModelUpdatedCallback& model_updated_callback) {
@@ -81,9 +80,9 @@
 
   constexpr int kModelVersion = 1;
   base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindRepeating(
-                     model_updated_callback, kChromeStartOptimizationTarget,
-                     std::move(chrome_start_metadata), kModelVersion));
+      FROM_HERE,
+      base::BindRepeating(model_updated_callback, kChromeStartSegmentId,
+                          std::move(chrome_start_metadata), kModelVersion));
 }
 
 void ChromeStartModel::ExecuteModelWithInput(const std::vector<float>& inputs,
diff --git a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc
index edd98512..4dc7545a 100644
--- a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc
@@ -35,11 +35,10 @@
     loop.Run();
   }
 
-  void OnInitFinishedCallback(
-      base::RepeatingClosure closure,
-      optimization_guide::proto::OptimizationTarget target,
-      proto::SegmentationModelMetadata metadata,
-      int64_t) {
+  void OnInitFinishedCallback(base::RepeatingClosure closure,
+                              proto::SegmentId target,
+                              proto::SegmentationModelMetadata metadata,
+                              int64_t) {
     EXPECT_EQ(metadata_utils::ValidateMetadataAndFeatures(metadata),
               metadata_utils::ValidationResult::kValidationSuccess);
     std::move(closure).Run();
diff --git a/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc b/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc
index bdb4f4ca..ceb598c 100644
--- a/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc
+++ b/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc
@@ -17,11 +17,11 @@
 namespace segmentation_platform {
 
 namespace {
-using optimization_guide::proto::OptimizationTarget;
+using proto::SegmentId;
 
 // Default parameters for Chrome Start model.
-constexpr OptimizationTarget kFeedUserOptimizationTarget =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER;
+constexpr SegmentId kFeedUserSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER;
 constexpr proto::TimeUnit kFeedUserTimeUnit = proto::TimeUnit::DAY;
 constexpr uint64_t kFeedUserBucketDuration = 1;
 constexpr int64_t kFeedUserSignalStorageLength = 28;
@@ -102,8 +102,7 @@
 
 }  // namespace
 
-FeedUserSegment::FeedUserSegment()
-    : ModelProvider(kFeedUserOptimizationTarget) {}
+FeedUserSegment::FeedUserSegment() : ModelProvider(kFeedUserSegmentId) {}
 
 absl::optional<std::string> FeedUserSegment::GetSubsegmentName(
     int subsegment_rank) {
@@ -139,7 +138,7 @@
   constexpr int kModelVersion = 1;
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      base::BindRepeating(model_updated_callback, kFeedUserOptimizationTarget,
+      base::BindRepeating(model_updated_callback, kFeedUserSegmentId,
                           std::move(chrome_start_metadata), kModelVersion));
 }
 
diff --git a/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc b/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc
index af0e807..af2f21a8 100644
--- a/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc
@@ -35,11 +35,10 @@
     loop.Run();
   }
 
-  void OnInitFinishedCallback(
-      base::RepeatingClosure closure,
-      optimization_guide::proto::OptimizationTarget target,
-      proto::SegmentationModelMetadata metadata,
-      int64_t) {
+  void OnInitFinishedCallback(base::RepeatingClosure closure,
+                              proto::SegmentId target,
+                              proto::SegmentationModelMetadata metadata,
+                              int64_t) {
     EXPECT_EQ(metadata_utils::ValidateMetadataAndFeatures(metadata),
               metadata_utils::ValidationResult::kValidationSuccess);
     fetched_metadata_ = metadata;
diff --git a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc
index 69e6eab9..9722661e 100644
--- a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc
+++ b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc
@@ -15,12 +15,11 @@
 
 namespace {
 
-using optimization_guide::proto::OptimizationTarget;
+using proto::SegmentId;
 
 // Default parameters for Chrome Start model.
-constexpr OptimizationTarget kChromeStartOptimizationTarget =
-    OptimizationTarget::
-        OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
+constexpr SegmentId kChromeStartSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
 constexpr proto::TimeUnit kChromeStartTimeUnit = proto::TimeUnit::DAY;
 constexpr uint64_t kChromeStartBucketDuration = 1;
 constexpr int64_t kChromeStartSignalStorageLength = 28;
@@ -47,7 +46,7 @@
 }  // namespace
 
 LowUserEngagementModel::LowUserEngagementModel()
-    : ModelProvider(kChromeStartOptimizationTarget) {}
+    : ModelProvider(kChromeStartSegmentId) {}
 
 void LowUserEngagementModel::InitAndFetchModel(
     const ModelUpdatedCallback& model_updated_callback) {
@@ -68,9 +67,9 @@
 
   constexpr int kModelVersion = 1;
   base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindRepeating(
-                     model_updated_callback, kChromeStartOptimizationTarget,
-                     std::move(chrome_start_metadata), kModelVersion));
+      FROM_HERE,
+      base::BindRepeating(model_updated_callback, kChromeStartSegmentId,
+                          std::move(chrome_start_metadata), kModelVersion));
 }
 
 void LowUserEngagementModel::ExecuteModelWithInput(
diff --git a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc
index dbd9a6f1..58df4e1 100644
--- a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc
@@ -30,11 +30,10 @@
     loop.Run();
   }
 
-  void OnInitFinishedCallback(
-      base::RepeatingClosure closure,
-      optimization_guide::proto::OptimizationTarget target,
-      proto::SegmentationModelMetadata metadata,
-      int64_t) {
+  void OnInitFinishedCallback(base::RepeatingClosure closure,
+                              proto::SegmentId target,
+                              proto::SegmentationModelMetadata metadata,
+                              int64_t) {
     EXPECT_EQ(metadata_utils::ValidateMetadataAndFeatures(metadata),
               metadata_utils::ValidationResult::kValidationSuccess);
     std::move(closure).Run();
diff --git a/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc b/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc
index f63f6d7..d2b41635 100644
--- a/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc
+++ b/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc
@@ -14,11 +14,11 @@
 namespace segmentation_platform {
 
 namespace {
-using optimization_guide::proto::OptimizationTarget;
+using proto::SegmentId;
 
 // Default parameters for query tiles model.
-constexpr OptimizationTarget kQueryTilesOptimizationTarget =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES;
+constexpr SegmentId kQueryTilesSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES;
 constexpr proto::TimeUnit kQueryTilesTimeUnit = proto::TimeUnit::DAY;
 constexpr uint64_t kQueryTilesBucketDuration = 1;
 constexpr int64_t kQueryTilesSignalStorageLength = 28;
@@ -42,8 +42,7 @@
 
 }  // namespace
 
-QueryTilesModel::QueryTilesModel()
-    : ModelProvider(kQueryTilesOptimizationTarget) {}
+QueryTilesModel::QueryTilesModel() : ModelProvider(kQueryTilesSegmentId) {}
 
 void QueryTilesModel::InitAndFetchModel(
     const ModelUpdatedCallback& model_updated_callback) {
@@ -64,7 +63,7 @@
 
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      base::BindRepeating(model_updated_callback, kQueryTilesOptimizationTarget,
+      base::BindRepeating(model_updated_callback, kQueryTilesSegmentId,
                           std::move(query_tiles_metadata), 2));
 }
 
diff --git a/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc b/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc
index 82949d74..98f41aaa 100644
--- a/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc
@@ -35,11 +35,10 @@
     loop.Run();
   }
 
-  void OnInitFinishedCallback(
-      base::RepeatingClosure closure,
-      optimization_guide::proto::OptimizationTarget target,
-      proto::SegmentationModelMetadata metadata,
-      int64_t) {
+  void OnInitFinishedCallback(base::RepeatingClosure closure,
+                              proto::SegmentId target,
+                              proto::SegmentationModelMetadata metadata,
+                              int64_t) {
     EXPECT_EQ(metadata_utils::ValidateMetadataAndFeatures(metadata),
               metadata_utils::ValidationResult::kValidationSuccess);
     std::move(closure).Run();
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
index efab0f4..d091d24e 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
@@ -6,8 +6,8 @@
 
 #include "chrome/browser/segmentation_platform/segmentation_platform_config.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"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 namespace {
@@ -15,8 +15,7 @@
 class DummyModelProvider : public ModelProvider {
  public:
   DummyModelProvider()
-      : ModelProvider(optimization_guide::proto::OptimizationTarget::
-                          OPTIMIZATION_TARGET_UNKNOWN) {}
+      : ModelProvider(proto::SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {}
   void InitAndFetchModel(
       const ModelUpdatedCallback& model_updated_callback) override {}
 
@@ -40,24 +39,22 @@
 ModelProviderFactoryImpl::~ModelProviderFactoryImpl() = default;
 
 std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateProvider(
-    optimization_guide::proto::OptimizationTarget optimization_target) {
+    proto::SegmentId segment_id) {
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   if (!optimization_guide_provider_) {
     // Optimization guide may not be available in some tests,
     return std::make_unique<DummyModelProvider>();
   }
   return std::make_unique<OptimizationGuideSegmentationModelProvider>(
-      optimization_guide_provider_, background_task_runner_,
-      optimization_target);
+      optimization_guide_provider_, background_task_runner_, segment_id);
 #else
   return std::make_unique<DummyModelProvider>();
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 }
 
 std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateDefaultProvider(
-    optimization_guide::proto::OptimizationTarget optimization_target) {
-  return DefaultModelsRegister::GetInstance().GetModelProvider(
-      optimization_target);
+    proto::SegmentId segment_id) {
+  return DefaultModelsRegister::GetInstance().GetModelProvider(segment_id);
 }
 
 }  // 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
index b108b26b..b7777020 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl.h
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl.h
@@ -9,8 +9,8 @@
 
 #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"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace optimization_guide {
 class OptimizationGuideModelProvider;
@@ -32,11 +32,9 @@
 
   // ModelProviderFactory impl:
   std::unique_ptr<ModelProvider> CreateProvider(
-      optimization_guide::proto::OptimizationTarget optimization_target)
-      override;
+      proto::SegmentId segment_id) override;
   std::unique_ptr<ModelProvider> CreateDefaultProvider(
-      optimization_guide::proto::OptimizationTarget optimization_target)
-      override;
+      proto::SegmentId segment_id) override;
 
  private:
   raw_ptr<optimization_guide::OptimizationGuideModelProvider>
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
index e785850..12f2067 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
@@ -41,11 +41,9 @@
 
 TEST_F(ModelProviderFactoryImplTest, ProviderCreated) {
   EXPECT_TRUE(provider_factory_->CreateProvider(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
   EXPECT_TRUE(provider_factory_->CreateProvider(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
 }
 
 class DummyModelProviderFactoryImplTest : public ModelProviderFactoryImplTest {
@@ -59,12 +57,10 @@
 
 TEST_F(DummyModelProviderFactoryImplTest, ProviderCreated) {
   EXPECT_TRUE(provider_factory_->CreateProvider(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
 
   auto provider = provider_factory_->CreateProvider(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
   ASSERT_TRUE(provider);
 
   EXPECT_FALSE(provider->ModelAvailable());
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_config.cc b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
index 207ed623..354a38f 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_config.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
@@ -13,10 +13,10 @@
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/segmentation_platform/default_model/feed_user_segment.h"
 #include "chrome/browser/segmentation_platform/default_model/low_user_engagement_model.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/features.h"
 #include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/feature_guide/notifications/feature_notification_guide_service.h"
@@ -28,10 +28,10 @@
 #include "components/query_tiles/switches.h"
 #endif
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 namespace {
 
 constexpr char kDefaultModelEnabledParam[] = "enable_default_model";
@@ -77,9 +77,9 @@
 
   // A hardcoded list of segment IDs known to the segmentation platform.
   config->segment_ids = {
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
   };
 
   return config;
@@ -90,7 +90,7 @@
   auto config = std::make_unique<Config>();
   config->segmentation_key = kDummySegmentationKey;
   config->segment_ids = {
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY,
   };
   config->segment_selection_ttl = base::Days(kDummyFeatureSelectionTTLDays);
   config->unknown_selection_ttl = base::Days(kDummyFeatureSelectionTTLDays);
@@ -111,7 +111,7 @@
   auto config = std::make_unique<Config>();
   config->segmentation_key = kChromeStartAndroidSegmentationKey;
   config->segment_ids = {
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
   };
 
   int segment_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
@@ -139,7 +139,7 @@
   auto config = std::make_unique<Config>();
   config->segmentation_key = kQueryTilesSegmentationKey;
   config->segment_ids = {
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES,
   };
 
   int segment_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
@@ -181,8 +181,7 @@
   auto config = std::make_unique<Config>();
   config->segmentation_key = kChromeLowUserEngagementSegmentationKey;
   config->segment_ids = {
-      OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT,
   };
 
 #if BUILDFLAG(IS_ANDROID)
@@ -204,7 +203,7 @@
   auto config = std::make_unique<Config>();
   config->segmentation_key = kFeedUserSegmentationKey;
   config->segment_ids = {
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER,
   };
   config->segment_selection_ttl =
       base::Days(base::GetFieldTrialParamByFeatureAsInt(
@@ -268,7 +267,7 @@
 }
 
 std::unique_ptr<ModelProvider> DefaultModelsRegister::GetModelProvider(
-    optimization_guide::proto::OptimizationTarget target) {
+    proto::SegmentId target) {
   auto it = providers_.find(target);
   if (it != providers_.end()) {
     DCHECK(it->second);
@@ -276,29 +275,25 @@
   }
 
 #if BUILDFLAG(IS_ANDROID)
-  if (target ==
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES) {
+  if (target == proto::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES) {
     return GetQueryTilesDefaultModel();
   }
-  if (target == optimization_guide::proto::
-                    OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID) {
+  if (target == proto::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID) {
     return GetChromeStartAndroidModel();
   }
 #endif
   if (target ==
-      optimization_guide::proto::
-          OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT) {
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT) {
     return GetLowEngagementDefaultModel();
   }
-  if (target ==
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER) {
+  if (target == proto::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER) {
     return GetFeedUserSegmentDefautlModel();
   }
   return nullptr;
 }
 
 void DefaultModelsRegister::SetModelForTesting(
-    optimization_guide::proto::OptimizationTarget target,
+    proto::SegmentId target,
     std::unique_ptr<ModelProvider> provider) {
   providers_[target] = std::move(provider);
 }
@@ -327,14 +322,13 @@
 
 void FieldTrialRegisterImpl::RegisterSubsegmentFieldTrialIfNeeded(
     base::StringPiece trial_name,
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     int subsegment_rank) {
   absl::optional<std::string> group_name;
   // TODO(ssid): Make GetSubsegmentName as a ModelProvider API so that clients
   // can simply implement it instead of adding conditions here, once the
   // subsegment process is more stable.
-  if (segment_id ==
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER) {
+  if (segment_id == SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER) {
     group_name = FeedUserSegment::GetSubsegmentName(subsegment_rank);
   }
 
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_config.h b/chrome/browser/segmentation_platform/segmentation_platform_config.h
index 174d51f..a9dd02c 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_config.h
+++ b/chrome/browser/segmentation_platform/segmentation_platform_config.h
@@ -9,8 +9,8 @@
 #include <vector>
 
 #include "base/no_destructor.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/public/field_trial_register.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 struct Config;
@@ -29,10 +29,9 @@
   DefaultModelsRegister& operator=(const DefaultModelsRegister& client) =
       delete;
 
-  std::unique_ptr<ModelProvider> GetModelProvider(
-      optimization_guide::proto::OptimizationTarget target);
+  std::unique_ptr<ModelProvider> GetModelProvider(proto::SegmentId target);
 
-  void SetModelForTesting(optimization_guide::proto::OptimizationTarget target,
+  void SetModelForTesting(proto::SegmentId target,
                           std::unique_ptr<ModelProvider>);
 
  private:
@@ -40,9 +39,7 @@
 
   DefaultModelsRegister();
 
-  std::map<optimization_guide::proto::OptimizationTarget,
-           std::unique_ptr<ModelProvider>>
-      providers_;
+  std::map<proto::SegmentId, std::unique_ptr<ModelProvider>> providers_;
 };
 
 // Implementation of FieldTrialRegister that uses synthetic field trials to
@@ -58,10 +55,9 @@
   void RegisterFieldTrial(base::StringPiece trial_name,
                           base::StringPiece group_name) override;
 
-  void RegisterSubsegmentFieldTrialIfNeeded(
-      base::StringPiece trial_name,
-      optimization_guide::proto::OptimizationTarget segment_id,
-      int subsegment_rank) override;
+  void RegisterSubsegmentFieldTrialIfNeeded(base::StringPiece trial_name,
+                                            proto::SegmentId segment_id,
+                                            int subsegment_rank) override;
 };
 
 }  // namespace segmentation_platform
diff --git a/chrome/browser/segmentation_platform/service_browsertest.cc b/chrome/browser/segmentation_platform/service_browsertest.cc
index 7eab6daa..71f1670 100644
--- a/chrome/browser/segmentation_platform/service_browsertest.cc
+++ b/chrome/browser/segmentation_platform/service_browsertest.cc
@@ -34,8 +34,8 @@
 using ::testing::Return;
 using ::testing::SaveArg;
 
-constexpr OptimizationTarget kSegmentId = OptimizationTarget::
-    OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
+constexpr SegmentId kSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
 
 constexpr char kSqlFeatureQuery[] = "SELECT COUNT(*) from metrics";
 
diff --git a/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc b/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc
index bf6cbfcc..3aeff88 100644
--- a/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc
+++ b/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.cc
@@ -8,13 +8,13 @@
 #include "chrome/browser/segmentation_platform/segmentation_platform_config.h"
 #include "chrome/browser/segmentation_platform/ukm_database_client.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/ukm_database.h"
 #include "components/segmentation_platform/internal/execution/mock_model_provider.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
 #include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
 #include "components/segmentation_platform/internal/signals/ukm_observer.h"
 #include "components/segmentation_platform/internal/ukm_data_manager.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -23,7 +23,7 @@
 
 namespace {
 
-using ::optimization_guide::proto::OptimizationTarget;
+using ::segmentation_platform::proto::SegmentId;
 using ::testing::Return;
 using ::ukm::builders::PageLoad;
 
@@ -73,7 +73,7 @@
 UkmDataManagerTestUtils::~UkmDataManagerTestUtils() = default;
 
 void UkmDataManagerTestUtils::PreProfileInit(
-    const std::set<OptimizationTarget>& default_overrides) {
+    const std::set<SegmentId>& default_overrides) {
   // Set test recorder before UkmObserver is created.
   UkmDatabaseClient::GetInstance().set_ukm_recorder_for_testing(ukm_recorder_);
 
@@ -92,13 +92,13 @@
 }
 
 void UkmDataManagerTestUtils::StoreModelUpdateCallback(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const ModelProvider::ModelUpdatedCallback& callback) {
   callbacks_[segment_id].push_back(callback);
 }
 
 void UkmDataManagerTestUtils::WaitForModelRequestAndUpdateWith(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const proto::SegmentationModelMetadata& metadata) {
   // Waits for the platform to fetch the default model metadata. At init time,
   // the platform fetches metadata from default model for:
@@ -167,7 +167,7 @@
 }
 
 MockModelProvider* UkmDataManagerTestUtils::GetDefaultOverride(
-    optimization_guide::proto::OptimizationTarget segment_id) {
+    proto::SegmentId segment_id) {
   return default_overrides_[segment_id];
 }
 
diff --git a/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.h b/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.h
index 7706dc5f..8fb1098 100644
--- a/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.h
+++ b/chrome/browser/segmentation_platform/ukm_data_manager_test_utils.h
@@ -10,9 +10,9 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.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/public/proto/segmentation_platform.pb.h"
 #include "components/ukm/test_ukm_recorder.h"
 
 class GURL;
@@ -34,14 +34,12 @@
 
   // Must be called before the first profile initialization, sets up default
   // model overrides for the given `default_overrides`
-  void PreProfileInit(
-      const std::set<optimization_guide::proto::OptimizationTarget>&
-          default_overrides);
+  void PreProfileInit(const std::set<proto::SegmentId>& default_overrides);
 
   // Waits for platform to initialize and request default model for
   // `segment_id`, and then returns the provided `metadata` to the platform.
   void WaitForModelRequestAndUpdateWith(
-      optimization_guide::proto::OptimizationTarget segment_id,
+      proto::SegmentId segment_id,
       const proto::SegmentationModelMetadata& metadata);
 
   // Creates a sample page load UKM based model metadata, with a simple SQL
@@ -58,8 +56,7 @@
   bool IsUrlInDatabase(const GURL& url);
 
   // Returns the model provider override for the `segment_id`.
-  MockModelProvider* GetDefaultOverride(
-      optimization_guide::proto::OptimizationTarget segment_id);
+  MockModelProvider* GetDefaultOverride(proto::SegmentId segment_id);
 
   // History service is needed for validating test URLs written to database.
   void set_history_service(history::HistoryService* history_service) {
@@ -68,17 +65,15 @@
 
  private:
   void StoreModelUpdateCallback(
-      optimization_guide::proto::OptimizationTarget segment_id,
+      proto::SegmentId segment_id,
       const ModelProvider::ModelUpdatedCallback& callback);
 
   const raw_ptr<ukm::TestUkmRecorder> ukm_recorder_;
   int source_id_counter_ = 1;
   raw_ptr<history::HistoryService> history_service_;
 
-  std::map<optimization_guide::proto::OptimizationTarget, MockModelProvider*>
-      default_overrides_;
-  std::map<optimization_guide::proto::OptimizationTarget,
-           std::vector<ModelProvider::ModelUpdatedCallback>>
+  std::map<proto::SegmentId, MockModelProvider*> default_overrides_;
+  std::map<proto::SegmentId, std::vector<ModelProvider::ModelUpdatedCallback>>
       callbacks_;
 
   base::WeakPtrFactory<UkmDataManagerTestUtils> weak_factory_{this};
diff --git a/chrome/browser/segmentation_platform/ukm_database_client.cc b/chrome/browser/segmentation_platform/ukm_database_client.cc
index 4e1fd75..9709a41e 100644
--- a/chrome/browser/segmentation_platform/ukm_database_client.cc
+++ b/chrome/browser/segmentation_platform/ukm_database_client.cc
@@ -44,13 +44,10 @@
 
 void UkmDatabaseClient::PreProfileInit() {
   if (ukm_recorder_for_testing_) {
-    ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_for_testing_,
-                                                  /*is_ukm_allowed=*/true);
+    ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_for_testing_);
   } else {
     ukm_observer_ = std::make_unique<UkmObserver>(
-        g_browser_process->GetMetricsServicesManager()->GetUkmService(),
-        g_browser_process->GetMetricsServicesManager()
-            ->IsUkmAllowedForAllProfiles());
+        g_browser_process->GetMetricsServicesManager()->GetUkmService());
   }
 
   // Path service is setup at early startup.
diff --git a/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc b/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
index 1f83507..b5bf63a 100644
--- a/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
@@ -245,59 +245,6 @@
   EXPECT_TRUE(NavigateIframeToURL(active_web_contents, "test", frame_url));
 }
 
-// Check that window.focus works for cross-process popups.
-// TODO(crbug.com/1326293): Re-enable this test
-IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, DISABLED_PopupWindowFocus) {
-  GURL main_url(embedded_test_server()->GetURL("/page_with_focus_events.html"));
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
-
-  // Set window.name on main page.  This will be used to identify the page
-  // later when it sends messages from its focus/blur events.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_TRUE(
-      ExecuteScriptWithoutUserGesture(web_contents, "window.name = 'main'"));
-
-  // Open a popup for a cross-site page.
-  GURL popup_url =
-      embedded_test_server()->GetURL("foo.com", "/page_with_focus_events.html");
-  content::TestNavigationObserver popup_observer(nullptr);
-  popup_observer.StartWatchingNewWebContents();
-  EXPECT_TRUE(ExecuteScript(web_contents,
-                            "openPopup('" + popup_url.spec() + "','popup')"));
-  popup_observer.Wait();
-  ASSERT_EQ(2, browser()->tab_strip_model()->count());
-  content::WebContents* popup =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ(popup_url, popup->GetLastCommittedURL());
-  EXPECT_NE(popup, web_contents);
-
-  // Switch focus to the original tab, since opening a popup also focused it.
-  web_contents->GetDelegate()->ActivateContents(web_contents);
-  EXPECT_EQ(web_contents, browser()->tab_strip_model()->GetActiveWebContents());
-
-  // Focus the popup via window.focus(), this needs user gesture.
-  content::DOMMessageQueue queue;
-  ExecuteScriptAsync(web_contents, "focusPopup()");
-
-  // Wait for main page to lose focus and for popup to gain focus.  Each event
-  // will send a message, and the two messages can arrive in any order.
-  std::string status;
-  bool main_lost_focus = false;
-  bool popup_got_focus = false;
-  while (queue.WaitForMessage(&status)) {
-    if (status == "\"main-lost-focus\"")
-      main_lost_focus = true;
-    if (status == "\"popup-got-focus\"")
-      popup_got_focus = true;
-    if (main_lost_focus && popup_got_focus)
-      break;
-  }
-
-  // The popup should be focused now.
-  EXPECT_EQ(popup, browser()->tab_strip_model()->GetActiveWebContents());
-}
-
 // Verify that ctrl-click of an anchor targeting a remote frame works (i.e. that
 // it opens the link in a new tab).  See also https://crbug.com/647772.
 IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
diff --git a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
index 513e7fa8..c68baea 100644
--- a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
+++ b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
@@ -1616,3 +1616,54 @@
   EXPECT_EQ(1,
             browser()->tab_strip_model()->GetIndexOfWebContents(new_contents));
 }
+
+// Check that window.focus works for cross-process popups.
+IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest, PopupWindowFocus) {
+  GURL main_url(embedded_test_server()->GetURL("/page_with_focus_events.html"));
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
+
+  // Set window.name on main page.  This will be used to identify the page
+  // later when it sends messages from its focus/blur events.
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(
+      ExecuteScriptWithoutUserGesture(web_contents, "window.name = 'main'"));
+
+  // Open a popup for a cross-site page.
+  GURL popup_url =
+      embedded_test_server()->GetURL("foo.com", "/page_with_focus_events.html");
+  content::TestNavigationObserver popup_observer(nullptr);
+  popup_observer.StartWatchingNewWebContents();
+  EXPECT_TRUE(ExecuteScript(web_contents,
+                            "openPopup('" + popup_url.spec() + "','popup')"));
+  popup_observer.Wait();
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+  content::WebContents* popup =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_EQ(popup_url, popup->GetLastCommittedURL());
+  EXPECT_NE(popup, web_contents);
+
+  // Switch focus to the original tab, since opening a popup also focused it.
+  web_contents->GetDelegate()->ActivateContents(web_contents);
+  EXPECT_EQ(web_contents, browser()->tab_strip_model()->GetActiveWebContents());
+
+  // Focus the popup via window.focus(), this needs user gesture.
+  content::DOMMessageQueue main_queue(web_contents);
+  content::DOMMessageQueue popup_queue(popup);
+  ExecuteScriptAsync(web_contents, "focusPopup()");
+
+  // Wait for main page to lose focus and for popup to gain focus.  Each event
+  // will send a message, and the two messages can arrive in any order.
+  std::string status;
+  while (main_queue.WaitForMessage(&status)) {
+    if (status == "\"main-lost-focus\"")
+      break;
+  }
+  while (popup_queue.WaitForMessage(&status)) {
+    if (status == "\"popup-got-focus\"")
+      break;
+  }
+
+  // The popup should be focused now.
+  EXPECT_EQ(popup, browser()->tab_strip_model()->GetActiveWebContents());
+}
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index d69b184b8..08e0e719 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -159,10 +159,11 @@
 };
 
 BrowserThemePack::PersistentID GetPersistentIDByName(const std::string& key) {
-  auto* it = std::find_if(std::begin(kPersistingImages),
-                          std::end(kPersistingImages), [&](const auto& image) {
-                            return base::LowerCaseEqualsASCII(key, image.key);
-                          });
+  auto* it =
+      std::find_if(std::begin(kPersistingImages), std::end(kPersistingImages),
+                   [&](const auto& image) {
+                     return base::EqualsCaseInsensitiveASCII(key, image.key);
+                   });
   return it == std::end(kPersistingImages) ? PRS::kInvalid : it->persistent_id;
 }
 
@@ -299,7 +300,7 @@
                     const StringToIntTable* table,
                     size_t table_length) {
   for (size_t i = 0; i < table_length; ++i) {
-    if (base::LowerCaseEqualsASCII(key, table[i].key)) {
+    if (base::EqualsCaseInsensitiveASCII(key, table[i].key)) {
       return table[i].id;
     }
   }
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index 8d735ad6..52560d8 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -205,13 +205,13 @@
   for (const std::string& component : base::SplitString(
            alignment, base::kWhitespaceASCII,
            base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
-    if (base::LowerCaseEqualsASCII(component, kAlignmentTop))
+    if (base::EqualsCaseInsensitiveASCII(component, kAlignmentTop))
       alignment_mask |= ALIGN_TOP;
-    else if (base::LowerCaseEqualsASCII(component, kAlignmentBottom))
+    else if (base::EqualsCaseInsensitiveASCII(component, kAlignmentBottom))
       alignment_mask |= ALIGN_BOTTOM;
-    else if (base::LowerCaseEqualsASCII(component, kAlignmentLeft))
+    else if (base::EqualsCaseInsensitiveASCII(component, kAlignmentLeft))
       alignment_mask |= ALIGN_LEFT;
-    else if (base::LowerCaseEqualsASCII(component, kAlignmentRight))
+    else if (base::EqualsCaseInsensitiveASCII(component, kAlignmentRight))
       alignment_mask |= ALIGN_RIGHT;
   }
   return alignment_mask;
@@ -219,11 +219,11 @@
 
 // static
 int ThemeProperties::StringToTiling(const std::string& tiling) {
-  if (base::LowerCaseEqualsASCII(tiling, kTilingRepeatX))
+  if (base::EqualsCaseInsensitiveASCII(tiling, kTilingRepeatX))
     return REPEAT_X;
-  if (base::LowerCaseEqualsASCII(tiling, kTilingRepeatY))
+  if (base::EqualsCaseInsensitiveASCII(tiling, kTilingRepeatY))
     return REPEAT_Y;
-  if (base::LowerCaseEqualsASCII(tiling, kTilingRepeat))
+  if (base::EqualsCaseInsensitiveASCII(tiling, kTilingRepeat))
     return REPEAT;
   // NO_REPEAT is the default choice.
   return NO_REPEAT;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 2eb0d3ef..2b055fcf 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3057,6 +3057,7 @@
       "//chrome/browser/ash/crosapi",
       "//chrome/browser/ash/crostini:crostini_installer_types_mojom",
       "//chrome/browser/chromeos",
+      "//chrome/browser/chromeos/extensions/vpn_provider",
       "//chrome/browser/media/router/discovery/access_code:access_code_cast_feature",
       "//chrome/browser/media/router/discovery/access_code:discovery_resources_proto",
       "//chrome/browser/nearby_sharing:share_target",
@@ -3168,7 +3169,6 @@
       "//content/public/common",
       "//extensions/browser/api/messaging",
       "//extensions/browser/api/virtual_keyboard_private",
-      "//extensions/browser/api/vpn_provider",
       "//google_apis/calendar",
       "//google_apis/common",
       "//google_apis/drive",
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 700c648..572eec6 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -1127,9 +1127,6 @@
   // Sticky apps are the ones in either system or vendor image. They are called
   // "sticky" because uninstalling them is not possible. 0 for opt-out users.
   size_t num_sticky_apps = 0;
-  // Apps that are unknown to this class. The number of such apps should be
-  // zero.
-  size_t num_unknown_apps = 0;
   // "Installed" apps are the ones that the user has manually installed. This
   // includes apps installed by Chrome's app sync feature. 0 for opt-out users.
   size_t num_installed_apps = 0;
@@ -1138,12 +1135,8 @@
   for (const auto& app_id : app_ids) {
     std::unique_ptr<AppInfo> app_info = GetApp(app_id);
     DCHECK(app_info) << app_id;
-    // TODO(yusukes): Remove the path for handling null |app_info| in M104+.
-    if (!app_info) {
-      LOG(WARNING) << "App ID " << app_id << " is not associated with AppInfo";
-      ++num_unknown_apps;
+    if (!app_info)
       continue;
-    }
     const bool is_default = IsDefault(app_id);
     const bool is_sticky = app_info->sticky;
     DVLOG(1) << "App ID on startup: name=" << app_info->name
@@ -1161,17 +1154,13 @@
     }
   }
 
-  const bool has_installed_or_unknown_apps =
-      num_installed_apps || num_unknown_apps;
+  const bool has_installed_apps = num_installed_apps;
   VLOG(1) << "Non-PAI (aka non-default) and non-sticky (aka"
           << " not-in-system/vendor-images) ARC app(s) are "
-          << (has_installed_or_unknown_apps ? "" : "not ") << "found.";
+          << (has_installed_apps ? "" : "not ") << "found.";
 
   // Record the UMA. For more context of the metrics, see b/219115916.
   base::UmaHistogramExactLinear(
-      base::StrCat({kAppCountUmaPrefix, "UnknownApp"}), num_unknown_apps,
-      kAppCountUmaExclusiveMax);
-  base::UmaHistogramExactLinear(
       base::StrCat({kAppCountUmaPrefix, "DefaultApp"}), num_default_apps,
       kAppCountUmaExclusiveMax);
   base::UmaHistogramExactLinear(base::StrCat({kAppCountUmaPrefix, "StickyApp"}),
@@ -1181,7 +1170,7 @@
       kAppCountUmaExclusiveMax);
   base::UmaHistogramBoolean(
       base::StrCat({kAppCountUmaPrefix, "HasInstalledOrUnknownApp"}),
-      has_installed_or_unknown_apps);
+      has_installed_apps);
 }
 
 void ArcAppListPrefs::OnPolicySent(const std::string& policy) {
diff --git a/chrome/browser/ui/ash/system_tray_client_impl.cc b/chrome/browser/ui/ash/system_tray_client_impl.cc
index ddb261f9..8bbc3c6 100644
--- a/chrome/browser/ui/ash/system_tray_client_impl.cc
+++ b/chrome/browser/ui/ash/system_tray_client_impl.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_metrics.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -66,8 +67,6 @@
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/core/session_manager_observer.h"
 #include "components/user_manager/user_manager.h"
-#include "extensions/browser/api/vpn_provider/vpn_service.h"
-#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 #include "ui/events/event_constants.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/ui/extensions/extensions_container.h b/chrome/browser/ui/extensions/extensions_container.h
index ad36121c..bc1c909 100644
--- a/chrome/browser/ui/extensions/extensions_container.h
+++ b/chrome/browser/ui/extensions/extensions_container.h
@@ -70,10 +70,6 @@
   virtual void ShowToolbarActionBubble(
       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) = 0;
 
-  // Same as above, but uses PostTask() in all cases.
-  virtual void ShowToolbarActionBubbleAsync(
-      std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) = 0;
-
   // Toggle the Extensions menu (as if the user clicked the puzzle piece icon).
   virtual void ToggleExtensionsMenu() = 0;
 
diff --git a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
index a21788e..8d3b883 100644
--- a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
+++ b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
@@ -76,7 +76,7 @@
   settings_api_bubble->SetIsActiveBubble();
   std::unique_ptr<ToolbarActionsBarBubbleDelegate> bridge(
       new ExtensionMessageBubbleBridge(std::move(settings_api_bubble)));
-  browser->window()->GetExtensionsContainer()->ShowToolbarActionBubbleAsync(
+  browser->window()->GetExtensionsContainer()->ShowToolbarActionBubble(
       std::move(bridge));
 }
 #endif
diff --git a/chrome/browser/ui/login/login_handler.cc b/chrome/browser/ui/login/login_handler.cc
index a5ec8021..f6bf84a 100644
--- a/chrome/browser/ui/login/login_handler.cc
+++ b/chrome/browser/ui/login/login_handler.cc
@@ -399,10 +399,11 @@
     const GURL& request_url,
     const net::AuthChallengeInfo& auth_info) {
   PasswordForm dialog_form;
-  if (base::LowerCaseEqualsASCII(auth_info.scheme, net::kBasicAuthScheme)) {
+  if (base::EqualsCaseInsensitiveASCII(auth_info.scheme,
+                                       net::kBasicAuthScheme)) {
     dialog_form.scheme = PasswordForm::Scheme::kBasic;
-  } else if (base::LowerCaseEqualsASCII(auth_info.scheme,
-                                        net::kDigestAuthScheme)) {
+  } else if (base::EqualsCaseInsensitiveASCII(auth_info.scheme,
+                                              net::kDigestAuthScheme)) {
     dialog_form.scheme = PasswordForm::Scheme::kDigest;
   } else {
     dialog_form.scheme = PasswordForm::Scheme::kOther;
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
index b54a3fd6..4d70b5c 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -450,11 +450,6 @@
   ShowWidgetForExtension(widget, extension_id);
 }
 
-void ExtensionsToolbarContainer::ShowToolbarActionBubbleAsync(
-    std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) {
-  ShowToolbarActionBubble(std::move(bubble));
-}
-
 void ExtensionsToolbarContainer::ToggleExtensionsMenu() {
   GetExtensionsButton()->ToggleExtensionsMenu();
 }
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
index 281e4fb..383473b6 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
@@ -145,8 +145,6 @@
                                         ShowPopupCallback callback) override;
   void ShowToolbarActionBubble(
       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
-  void ShowToolbarActionBubbleAsync(
-      std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
   void ToggleExtensionsMenu() override;
   bool HasAnyExtensions() const override;
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index 766ae84..3bcac7a 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -263,7 +263,7 @@
 class BrowserAddedWaiter final : public BrowserListObserver {
  public:
   BrowserAddedWaiter() { BrowserList::AddObserver(this); }
-  ~BrowserAddedWaiter() override = default;
+  ~BrowserAddedWaiter() override { BrowserList::RemoveObserver(this); }
 
   void Wait() { run_loop_.Run(); }
 
@@ -671,15 +671,13 @@
   BeforeStateChangeAction(__FUNCTION__);
   MaybeNavigateTabbedBrowserInScope(InstallableSiteToSite(site));
   chrome::SetAutoAcceptPWAInstallConfirmationForTesting(/*auto_accept=*/true);
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
+  BrowserAddedWaiter browser_added_waiter;
   WebAppTestInstallWithOsHooksObserver install_observer(profile());
   install_observer.BeginListening();
   CHECK(chrome::ExecuteCommand(browser(), IDC_INSTALL_PWA));
-  app_loaded_observer.Wait();
+  browser_added_waiter.Wait();
   active_app_id_ = install_observer.Wait();
-  app_browser_ = GetBrowserForAppId(active_app_id_);
+  app_browser_ = browser_added_waiter.browser_added();
   chrome::SetAutoAcceptPWAInstallConfirmationForTesting(/*auto_accept=*/false);
   AfterStateChangeAction();
 }
@@ -721,20 +719,18 @@
         app_id = installed_app_id;
         run_loop.Quit();
       }));
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
 
+  BrowserAddedWaiter browser_added_waiter;
   ASSERT_TRUE(pwa_install_view()->GetVisible());
   WebAppTestInstallWithOsHooksObserver install_observer(profile());
   install_observer.BeginListening();
   pwa_install_view()->ExecuteForTesting();
 
   run_loop.Run();
-  app_loaded_observer.Wait();
+  browser_added_waiter.Wait();
   active_app_id_ = install_observer.Wait();
   DCHECK_EQ(app_id, active_app_id_);
-  app_browser_ = GetBrowserForAppId(active_app_id_);
+  app_browser_ = browser_added_waiter.browser_added();
 
   chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false);
   AfterStateChangeAction();
@@ -864,7 +860,11 @@
 
 void WebAppIntegrationTestDriver::LaunchFromLaunchIcon(Site site) {
   BeforeStateChangeAction(__FUNCTION__);
-  NavigateBrowser(site);
+  AppId app_id = GetAppIdBySiteMode(site);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
+      << "No app installed for site: " << static_cast<int>(site);
+
+  NavigateTabbedBrowserToSite(GetInScopeURL(site), NavigationMode::kNewTab);
 
   EXPECT_TRUE(intent_picker_view()->GetVisible());
 
@@ -894,17 +894,12 @@
   AppId app_id = GetAppIdBySiteMode(site);
   ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << static_cast<int>(site);
-  ;
 
-  NavigateBrowser(site);
+  NavigateTabbedBrowserToSite(GetInScopeURL(site), NavigationMode::kNewTab);
 
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
   BrowserAddedWaiter browser_added_waiter;
   CHECK(chrome::ExecuteCommand(browser(), IDC_OPEN_IN_PWA_WINDOW));
   browser_added_waiter.Wait();
-  app_loaded_observer.Wait();
   app_browser_ = browser_added_waiter.browser_added();
   active_app_id_ = app_id;
 
@@ -1098,7 +1093,7 @@
 
 void WebAppIntegrationTestDriver::NavigateBrowser(Site site) {
   BeforeStateChangeAction(__FUNCTION__);
-  NavigateTabbedBrowserToSite(GetInScopeURL(site));
+  NavigateTabbedBrowserToSite(GetInScopeURL(site), NavigationMode::kCurrentTab);
   AfterStateChangeAction();
 }
 
@@ -1119,7 +1114,8 @@
 void WebAppIntegrationTestDriver::NavigateNotfoundUrl() {
   BeforeStateChangeAction(__FUNCTION__);
   NavigateTabbedBrowserToSite(
-      delegate_->EmbeddedTestServer()->GetURL("/non-existant/index.html"));
+      delegate_->EmbeddedTestServer()->GetURL("/non-existant/index.html"),
+      NavigationMode::kCurrentTab);
   AfterStateChangeAction();
 }
 
@@ -2237,15 +2233,13 @@
       /*auto_open_in_window=*/open_in_window);
   WebAppTestInstallWithOsHooksObserver observer(profile());
   observer.BeginListening();
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
+  BrowserAddedWaiter browser_added_waiter;
   CHECK(chrome::ExecuteCommand(browser(), IDC_CREATE_SHORTCUT));
   active_app_id_ = observer.Wait();
   chrome::SetAutoAcceptWebAppDialogForTesting(false, false);
   if (open_in_window) {
-    app_loaded_observer.Wait();
-    app_browser_ = GetBrowserForAppId(active_app_id_);
+    browser_added_waiter.Wait();
+    app_browser_ = browser_added_waiter.browser_added();
   }
 }
 
@@ -2348,7 +2342,8 @@
   // Manifest updates must occur as the first navigation after a webapp is
   // installed, otherwise the throttle is tripped.
   ASSERT_FALSE(provider()->manifest_update_manager().IsUpdateConsumed(app_id));
-  NavigateTabbedBrowserToSite(app_url_with_manifest_param);
+  NavigateTabbedBrowserToSite(app_url_with_manifest_param,
+                              NavigationMode::kCurrentTab);
 }
 
 void WebAppIntegrationTestDriver::MaybeWaitForManifestUpdates() {
@@ -2378,17 +2373,26 @@
   auto browser_url = GetCurrentTab(browser())->GetURL();
   auto dest_url = GetInScopeURL(site);
   if (browser_url.is_empty() || browser_url != dest_url) {
-    NavigateTabbedBrowserToSite(dest_url);
+    NavigateTabbedBrowserToSite(dest_url, NavigationMode::kCurrentTab);
   }
 }
 
-void WebAppIntegrationTestDriver::NavigateTabbedBrowserToSite(const GURL& url) {
+void WebAppIntegrationTestDriver::NavigateTabbedBrowserToSite(
+    const GURL& url,
+    NavigationMode mode) {
   DCHECK(browser());
   content::WebContents* web_contents = GetCurrentTab(browser());
   auto* app_banner_manager =
       webapps::TestAppBannerManagerDesktop::FromWebContents(web_contents);
 
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  if (mode == NavigationMode::kNewTab) {
+    ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL(url), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+            ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+  } else {
+    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  }
   app_banner_manager->WaitForInstallableCheck();
 }
 
@@ -2483,16 +2487,12 @@
 
 void WebAppIntegrationTestDriver::LaunchAppStartupBrowserCreator(
     const AppId& app_id) {
-  content::WindowedNotificationObserver app_loaded_observer(
-      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-      content::NotificationService::AllSources());
   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   command_line.AppendSwitchASCII(switches::kAppId, app_id);
   command_line.AppendSwitchASCII(switches::kTestType, "browser");
   ASSERT_TRUE(StartupBrowserCreator().ProcessCmdLineImpl(
       command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
       {browser()->profile(), StartupProfileMode::kBrowserWindow}, {}));
-  app_loaded_observer.Wait();
   content::RunAllTasksUntilIdle();
 }
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
index 6cc24b5..b68a41d 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
@@ -321,7 +321,8 @@
 
   void MaybeNavigateTabbedBrowserInScope(Site site);
 
-  void NavigateTabbedBrowserToSite(const GURL& url);
+  enum class NavigationMode { kNewTab, kCurrentTab };
+  void NavigateTabbedBrowserToSite(const GURL& url, NavigationMode mode);
 
   // Returns an existing app browser if one exists, or launches a new one if
   // not.
diff --git a/chrome/browser/ui/webui/segmentation_internals/segmentation_internals_page_handler_impl.cc b/chrome/browser/ui/webui/segmentation_internals/segmentation_internals_page_handler_impl.cc
index 0a9e403..efc42c4 100644
--- a/chrome/browser/ui/webui/segmentation_internals/segmentation_internals_page_handler_impl.cc
+++ b/chrome/browser/ui/webui/segmentation_internals/segmentation_internals_page_handler_impl.cc
@@ -11,7 +11,7 @@
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/segmentation_platform/public/segmentation_platform_service.h"
 
-using optimization_guide::proto::OptimizationTarget;
+using segmentation_platform::proto::SegmentId;
 
 SegmentationInternalsPageHandlerImpl::SegmentationInternalsPageHandlerImpl(
     mojo::PendingReceiver<segmentation_internals::mojom::PageHandler> receiver,
@@ -40,15 +40,14 @@
 void SegmentationInternalsPageHandlerImpl::ExecuteModel(int segment_id) {
   if (!service_proxy_)
     return;
-  service_proxy_->ExecuteModel(static_cast<OptimizationTarget>(segment_id));
+  service_proxy_->ExecuteModel(static_cast<SegmentId>(segment_id));
 }
 
 void SegmentationInternalsPageHandlerImpl::OverwriteResult(int segment_id,
                                                            float result) {
   if (!service_proxy_)
     return;
-  service_proxy_->OverwriteResult(static_cast<OptimizationTarget>(segment_id),
-                                  result);
+  service_proxy_->OverwriteResult(static_cast<SegmentId>(segment_id), result);
 }
 
 void SegmentationInternalsPageHandlerImpl::SetSelected(
@@ -57,8 +56,8 @@
   if (!service_proxy_)
     return;
 
-  service_proxy_->SetSelectedSegment(
-      segmentation_key, static_cast<OptimizationTarget>(segment_id));
+  service_proxy_->SetSelectedSegment(segmentation_key,
+                                     static_cast<SegmentId>(segment_id));
 }
 
 void SegmentationInternalsPageHandlerImpl::OnServiceStatusChanged(
@@ -75,13 +74,11 @@
     auto client = segmentation_internals::mojom::ClientInfo::New();
     client->segmentation_key = info.segmentation_key;
     client->selected_segment =
-        optimization_guide::GetStringNameForOptimizationTarget(
-            info.selected_segment);
+        segmentation_platform::proto::SegmentId_Name(info.selected_segment);
     for (const auto& status : info.segment_status) {
       auto segment_data = segmentation_internals::mojom::SegmentInfo::New();
       segment_data->segment_name =
-          optimization_guide::GetStringNameForOptimizationTarget(
-              status.segment_id);
+          segmentation_platform::proto::SegmentId_Name(status.segment_id);
       segment_data->segment_id = status.segment_id;
       segment_data->segment_data = status.segment_metadata;
       segment_data->prediction_result = status.prediction_result;
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc b/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
index 6dd146c..8138071 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/tether/tether_service.h"
+#include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
@@ -28,8 +29,6 @@
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
-#include "extensions/browser/api/vpn_provider/vpn_service.h"
-#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/events/event_constants.h"
 
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 76a1810..0d1565c2 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1653069473-d07fe23385329eeeb7f995e7ff7633dac32dc02b.profdata
+chrome-win32-main-1653080329-91bf123bcb3e9ba70b94f02ea7e7350b2125a93e.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 454ed1f..0542033 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1653069473-c79c9e7950bdfe05e738c8e7567cce66780b1c14.profdata
+chrome-win64-main-1653080329-7935f15a2d49d4c91b84af35c70659205f234f2a.profdata
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index f1df6a3..572798af 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -86,6 +86,7 @@
     "platform_keys_internal.idl",
     "platform_keys.idl",
     "quick_unlock_private.idl",
+    "vpn_provider.idl",
   ]
 
   if (use_cups) {
diff --git a/extensions/common/api/vpn_provider.idl b/chrome/common/extensions/api/vpn_provider.idl
similarity index 98%
rename from extensions/common/api/vpn_provider.idl
rename to chrome/common/extensions/api/vpn_provider.idl
index 330e007..f99f416 100644
--- a/extensions/common/api/vpn_provider.idl
+++ b/chrome/common/extensions/api/vpn_provider.idl
@@ -4,6 +4,8 @@
 
 // Use the <code>chrome.vpnProvider</code> API to implement a VPN
 // client.
+[platforms=("chromeos"),
+ implemented_in="chrome/browser/chromeos/extensions/vpn_provider/vpn_provider_api.h"]
 namespace vpnProvider {
   // A parameters class for the VPN interface.
   dictionary Parameters {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 346422c..77e062d4 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3093,6 +3093,7 @@
 
         deps += [
           "../browser/chromeos/extensions/telemetry/api:browser_tests",
+          "//chrome/browser/chromeos/extensions/vpn_provider",
           "//chrome/browser/error_reporting:test_support",
           "//chromeos/components/chromebox_for_meetings/buildflags",
           "//chromeos/dbus/dlp:dlp",
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
index 4ecd04df..35cd8cec 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
@@ -15,6 +15,7 @@
     ":confirmation_page_test",
     ":fake_help_content_provider_test",
     ":feedback_flow_test",
+    ":file_attachment_test",
     ":help_content_test",
     ":mojo_interface_provider_test",
     ":os_feedback_unified_test",
@@ -51,6 +52,13 @@
     "//ash/webui/os_feedback_ui/resources:search_page",
     "//ash/webui/os_feedback_ui/resources:share_data_page",
   ]
+}
+
+js_library("file_attachment_test") {
+  deps = [
+    "../..:chai_assert",
+    "//ash/webui/os_feedback_ui/resources:file_attachment",
+  ]
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
new file mode 100644
index 0000000..029dafb
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
@@ -0,0 +1,58 @@
+// 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 {FileAttachmentElement} from 'chrome://os-feedback/file_attachment.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {flushTasks} from '../../test_util.js';
+
+export function fileAttachmentTestSuite() {
+  /** @type {?FileAttachmentElement} */
+  let page = null;
+
+  setup(() => {
+    document.body.innerHTML = '';
+  });
+
+  teardown(() => {
+    page.remove();
+    page = null;
+  });
+
+  function initializePage() {
+    assertFalse(!!page);
+    page =
+        /** @type {!FileAttachmentElement} */ (
+            document.createElement('file-attachment'));
+    assertTrue(!!page);
+    document.body.appendChild(page);
+    return flushTasks();
+  }
+
+  /**
+   * @param {string} selector
+   * @returns {Element|null}
+   */
+  function getElement(selector) {
+    const element = page.shadowRoot.querySelector(selector);
+    return element;
+  }
+
+  /**
+   * @param {string} selector
+   * @returns {string}
+   */
+  function getElementContent(selector) {
+    const element = getElement(selector);
+    assertTrue(!!element);
+    return element.textContent.trim();
+  }
+
+  // Test the page is loaded with expected HTML elements.
+  test('shareDataPageLoaded', async () => {
+    await initializePage();
+    // Verify the add file label is in the page.
+    assertEquals('Add file', getElementContent('#AddFileLabel'));
+  });
+}
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
index dd0112a..3dd478f9 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
@@ -7,14 +7,14 @@
  * Unifieid polymer testing suite for feedback tool.
  *
  * To run all tests in a single instance (default, faster):
- * `browser_tests --gtest_filter=OSFeedback*``
+ * `browser_tests --gtest_filter=OSFeedbackBrowserTest*``
  *
  * To run each test in a new instance:
- * `browser_tests --run-manual --gtest_filter=OSFeedback.MANUAL_*``
+ * `browser_tests --run-manual --gtest_filter=OSFeedbackBrowserTest.MANUAL_*``
  *
  * To run a single test suite, such as 'ConfirmationPageTest':
  * `browser_tests --run-manual \
- *  --gtest_filter=OSFeedback.MANUAL_ConfirmationPageTest`
+ *  --gtest_filter=OSFeedbackBrowserTest.MANUAL_ConfirmationPageTest`
  *
  */
 
@@ -44,6 +44,7 @@
   'fakeHelpContentProviderTest',
   'fakeMojoProviderTest',
   'feedbackFlowTest',
+  'fileAttachmentTest',
   'helpContentTest',
   'searchPageTest',
   'shareDataPageTest',
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
index 15e8cf1..5f2b7dc 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
@@ -7,6 +7,7 @@
 import {confirmationPageTest} from './confirmation_page_test.js';
 import {fakeHelpContentProviderTestSuite} from './fake_help_content_provider_test.js';
 import {FeedbackFlowTestSuite} from './feedback_flow_test.js';
+import {fileAttachmentTestSuite} from './file_attachment_test.js';
 import {helpContentTestSuite} from './help_content_test.js';
 import {fakeMojoProviderTestSuite} from './mojo_interface_provider_test.js';
 import {searchPageTestSuite} from './search_page_test.js';
@@ -23,6 +24,7 @@
 runSuite('fakeHelpContentProviderTest', fakeHelpContentProviderTestSuite);
 runSuite('fakeMojoProviderTest', fakeMojoProviderTestSuite);
 runSuite('feedbackFlowTest', FeedbackFlowTestSuite);
+runSuite('fileAttachmentTest', fileAttachmentTestSuite);
 runSuite('helpContentTest', helpContentTestSuite);
 runSuite('searchPageTest', searchPageTestSuite);
 runSuite('shareDataPageTest', shareDataPageTestSuite);
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
index c468b0c..690362d 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -94,8 +94,8 @@
     assertEquals('Screenshot', getElementContent('#screenshotCheckLabel'));
     assertTrue(!!getElement('#screenshotImage'));
 
-    // Add file element.
-    assertEquals('Add file', getElementContent('#addFile'));
+    // Add file attachment element.
+    assertTrue(!!getElement('file-attachment'));
 
     // Email elements.
     assertEquals('Email', getElementContent('#userEmailLabel'));
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 541f95c..6f5d57ad 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -233,38 +233,17 @@
   mocha.run();
 });
 
-var OSSettingsAmbientModePageV3Test = class extends OSSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/ambient_mode_page_test.js';
-  }
-
-  /** @override */
-  get featureList() {
-    return {disabled: ['ash::features::kPersonalizationHub']};
-  }
-};
-
-TEST_F('OSSettingsAmbientModePageV3Test', 'All', () => mocha.run());
-
-var OSSettingsAmbientModePhotosPageV3Test =
-    class extends OSSettingsV3BrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/ambient_mode_photos_page_test.js';
-  }
-
-  /** @override */
-  get featureList() {
-    return {disabled: ['ash::features::kPersonalizationHub']};
-  }
-};
-
-TEST_F('OSSettingsAmbientModePhotosPageV3Test', 'All', () => mocha.run());
-
 [['AccessibilityPage', 'os_a11y_page_tests.js'],
  ['AboutPage', 'os_about_page_tests.js'],
  ['AccountsPage', 'add_users_tests.js'],
+ [
+   'AmbientModePage', 'ambient_mode_page_test.js',
+   {disabled: ['ash::features::kPersonalizationHub']}
+ ],
+ [
+   'AmbientModePhotosPage', 'ambient_mode_photos_page_test.js',
+   {disabled: ['ash::features::kPersonalizationHub']}
+ ],
  ['AppsPage', 'apps_page_test.js'],
  ['AppNotificationsSubpage', 'app_notifications_subpage_tests.js'],
  ['AppManagementAppDetailsItem', 'app_management/app_details_item_test.js'],
@@ -397,7 +376,15 @@
  ['PeoplePage', 'os_people_page_test.js'],
  ['PeoplePageChangePicture', 'people_page_change_picture_test.js'],
  ['PeoplePageQuickUnlock', 'quick_unlock_authenticate_browsertest_chromeos.js'],
- ['PersonalizationPage', 'personalization_page_test.js'],
+ [
+   'PersonalizationPage', 'personalization_page_test.js',
+   {disabled: ['ash::features::kPersonalizationHub']}
+ ],
+ [
+   'PersonalizationPageWithPersonalizationHub',
+   'personalization_page_with_personalization_hub_test.js',
+   {enabled: ['ash::features::kPersonalizationHub']}
+ ],
  ['PrintingPage', 'os_printing_page_tests.js'],
  ['PrivacyPage', 'os_privacy_page_test.js'],
  ['ResetPage', 'os_reset_page_test.js'],
@@ -421,7 +408,7 @@
  ['UserPage', 'user_page_tests.js'],
 ].forEach(test => registerTest(...test));
 
-function registerTest(testName, module, caseName) {
+function registerTest(testName, module, featureList) {
   const className = `OSSettings${testName}V3Test`;
   this[className] = class extends OSSettingsV3BrowserTest {
     /** @override */
@@ -431,6 +418,14 @@
     }
   };
 
+  if (featureList) {
+    Object.defineProperty(this[className].prototype, 'featureList', {
+      get() {
+        return featureList;
+      },
+    });
+  }
+
   // AboutPage has a test suite that can only succeed on official builds where
   // the is_chrome_branded build flag is enabled.
   if (testName === 'AboutPage') {
@@ -456,6 +451,6 @@
     });
     GEN('#endif');
   } else {
-    TEST_F(className, caseName || 'All', () => mocha.run());
+    TEST_F(className, 'All', () => mocha.run());
   }
 }
diff --git a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
index 544dce8..974d1b0 100644
--- a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
@@ -2,26 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PersonalizationHubBrowserProxyImpl, Router, routes, WallpaperBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
+import {Router, routes, WallpaperBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {waitAfterNextRender} from 'chrome://test/test_util.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 
-import {TestPersonalizationHubBrowserProxy} from './test_personalization_hub_browser_proxy.js';
 import {TestWallpaperBrowserProxy} from './test_wallpaper_browser_proxy.js';
 
 let personalizationPage = null;
 
-/** @type {?TestPersonalizationHubBrowserProxy} */
-let PersonalizationHubBrowserProxy = null;
-
 /** @type {?TestWallpaperBrowserProxy} */
 let WallpaperBrowserProxy = null;
 
 function createPersonalizationPage() {
-  PersonalizationHubBrowserProxy.reset();
   WallpaperBrowserProxy.reset();
   PolymerTest.clearBody();
 
@@ -49,18 +44,15 @@
 
 suite('PersonalizationHandler', function() {
   suiteSetup(function() {
+    assertFalse(
+        loadTimeData.getBoolean('isPersonalizationHubEnabled'),
+        'this test should only run with PersonalizationHub disabled');
     testing.Test.disableAnimationsAndTransitions();
   });
 
   setup(function() {
-    // Most tests rely on this feature being off. For the few that need it,
-    // explicitly turn it on again.
-    loadTimeData.overrideValues({isPersonalizationHubEnabled: false});
     WallpaperBrowserProxy = new TestWallpaperBrowserProxy();
     WallpaperBrowserProxyImpl.instance_ = WallpaperBrowserProxy;
-    PersonalizationHubBrowserProxy = new TestPersonalizationHubBrowserProxy();
-    PersonalizationHubBrowserProxyImpl.instance_ =
-        PersonalizationHubBrowserProxy;
     createPersonalizationPage();
   });
 
@@ -153,32 +145,4 @@
         deepLinkElement, getDeepActiveElement(),
         'Account picture elem should be focused for settingId=503.');
   });
-
-  test('Personalization hub feature shows only link to hub', async () => {
-    loadTimeData.overrideValues({isPersonalizationHubEnabled: true});
-    assertTrue(loadTimeData.getBoolean('isPersonalizationHubEnabled'));
-    createPersonalizationPage();
-    flush();
-    await waitAfterNextRender(personalizationPage);
-
-    const crLinks =
-        personalizationPage.shadowRoot.querySelectorAll('cr-link-row');
-
-    assertEquals(1, crLinks.length);
-    assertEquals('personalizationHubButton', crLinks[0].id);
-  });
-
-  test('Opens personalization hub when clicked', async () => {
-    loadTimeData.overrideValues({isPersonalizationHubEnabled: true});
-    assertTrue(loadTimeData.getBoolean('isPersonalizationHubEnabled'));
-    createPersonalizationPage();
-    flush();
-    await waitAfterNextRender(personalizationPage);
-
-    const hubLink = personalizationPage.shadowRoot.getElementById(
-        'personalizationHubButton');
-    hubLink.click();
-
-    await PersonalizationHubBrowserProxy.whenCalled('openPersonalizationHub');
-  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/personalization_page_with_personalization_hub_test.js b/chrome/test/data/webui/settings/chromeos/personalization_page_with_personalization_hub_test.js
new file mode 100644
index 0000000..e5a0b52
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/personalization_page_with_personalization_hub_test.js
@@ -0,0 +1,69 @@
+// 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 {PersonalizationHubBrowserProxyImpl, Router} from 'chrome://os-settings/chromeos/os_settings.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
+
+import {assertEquals, assertTrue} from '../../chai_assert.js';
+
+import {TestPersonalizationHubBrowserProxy} from './test_personalization_hub_browser_proxy.js';
+
+let personalizationPage = null;
+
+/** @type {?TestPersonalizationHubBrowserProxy} */
+let PersonalizationHubBrowserProxy = null;
+
+function createPersonalizationPage() {
+  PersonalizationHubBrowserProxy.reset();
+  PolymerTest.clearBody();
+  personalizationPage = document.createElement('settings-personalization-page');
+  document.body.appendChild(personalizationPage);
+  flush();
+}
+
+suite('PersonalizationHandler', function() {
+  suiteSetup(function() {
+    assertTrue(
+        loadTimeData.getBoolean('isPersonalizationHubEnabled'),
+        'this test only runs with PersonalizationHub enabled');
+    testing.Test.disableAnimationsAndTransitions();
+  });
+
+  setup(function() {
+    PersonalizationHubBrowserProxy = new TestPersonalizationHubBrowserProxy();
+    PersonalizationHubBrowserProxyImpl.instance_ =
+        PersonalizationHubBrowserProxy;
+    createPersonalizationPage();
+  });
+
+  teardown(function() {
+    personalizationPage.remove();
+    Router.getInstance().resetRouteForTesting();
+  });
+
+  test('Personalization hub feature shows only link to hub', async () => {
+    createPersonalizationPage();
+    flush();
+    await waitAfterNextRender(personalizationPage);
+
+    const crLinks =
+        personalizationPage.shadowRoot.querySelectorAll('cr-link-row');
+
+    assertEquals(1, crLinks.length);
+    assertEquals('personalizationHubButton', crLinks[0].id);
+  });
+
+  test('Opens personalization hub when clicked', async () => {
+    createPersonalizationPage();
+    flush();
+    await waitAfterNextRender(personalizationPage);
+
+    const hubLink = personalizationPage.shadowRoot.getElementById(
+        'personalizationHubButton');
+    hubLink.click();
+
+    await PersonalizationHubBrowserProxy.whenCalled('openPersonalizationHub');
+  });
+});
diff --git a/chrome/updater/configurator.cc b/chrome/updater/configurator.cc
index e14d59c..19dac39b 100644
--- a/chrome/updater/configurator.cc
+++ b/chrome/updater/configurator.cc
@@ -57,7 +57,7 @@
 Configurator::Configurator(scoped_refptr<UpdaterPrefs> prefs,
                            scoped_refptr<ExternalConstants> external_constants)
     : prefs_(prefs),
-      policy_service_(PolicyService::Create()),
+      policy_service_(PolicyService::Create(external_constants)),
       external_constants_(external_constants),
       activity_data_service_(
           std::make_unique<ActivityDataService>(GetUpdaterScope())),
diff --git a/chrome/updater/constants.cc b/chrome/updater/constants.cc
index 00b4b21..8bde3ba 100644
--- a/chrome/updater/constants.cc
+++ b/chrome/updater/constants.cc
@@ -83,6 +83,7 @@
 const char kDevOverrideKeyInitialDelay[] = "initial_delay";
 const char kDevOverrideKeyServerKeepAliveSeconds[] = "server_keep_alive";
 const char kDevOverrideKeyCrxVerifierFormat[] = "crx_verifier_format";
+const char kDevOverrideKeyGroupPolicies[] = "group_policies";
 
 // Developer override file name, relative to app data directory.
 const char kDevOverrideFileName[] = "overrides.json";
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index 15502bb2..f721596 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -193,6 +193,7 @@
 extern const char kDevOverrideKeyInitialDelay[];
 extern const char kDevOverrideKeyServerKeepAliveSeconds[];
 extern const char kDevOverrideKeyCrxVerifierFormat[];
+extern const char kDevOverrideKeyGroupPolicies[];
 
 // File name of developer overrides file.
 extern const char kDevOverrideFileName[];
diff --git a/chrome/updater/external_constants.h b/chrome/updater/external_constants.h
index 8e33d3a..489fe5cb 100644
--- a/chrome/updater/external_constants.h
+++ b/chrome/updater/external_constants.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/values.h"
 
 class GURL;
 
@@ -42,6 +43,9 @@
   // CRX format verification requirements.
   virtual crx_file::VerifierFormat CrxVerifierFormat() const = 0;
 
+  // Overrides for the `GroupPolicyManager`.
+  virtual base::Value::DictStorage GroupPolicies() const = 0;
+
  protected:
   friend class base::RefCountedThreadSafe<ExternalConstants>;
   scoped_refptr<ExternalConstants> next_provider_;
diff --git a/chrome/updater/external_constants_builder.cc b/chrome/updater/external_constants_builder.cc
index e4be756..c14fe1d 100644
--- a/chrome/updater/external_constants_builder.cc
+++ b/chrome/updater/external_constants_builder.cc
@@ -4,19 +4,40 @@
 
 #include "chrome/updater/external_constants_builder.h"
 
+#include <algorithm>
+#include <iterator>
 #include <string>
 #include <vector>
 
 #include "base/json/json_file_value_serializer.h"
 #include "base/logging.h"
+#include "base/values.h"
 #include "chrome/updater/constants.h"
+#include "chrome/updater/external_constants_default.h"
+#include "chrome/updater/external_constants_override.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util.h"
 #include "components/crx_file/crx_verifier.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
 
 namespace updater {
 
+namespace {
+
+std::vector<std::string> StringVectorFromGURLVector(
+    const std::vector<GURL>& gurls) {
+  std::vector<std::string> ret;
+  ret.reserve(gurls.size());
+
+  std::transform(gurls.begin(), gurls.end(), std::back_inserter(ret),
+                 [](const GURL& gurl) { return gurl.possibly_invalid_spec(); });
+
+  return ret;
+}
+
+}  // namespace
+
 ExternalConstantsBuilder::~ExternalConstantsBuilder() {
   LOG_IF(WARNING, !written_) << "An ExternalConstantsBuilder with "
                              << overrides_.DictSize() << " entries is being "
@@ -85,6 +106,17 @@
   return *this;
 }
 
+ExternalConstantsBuilder& ExternalConstantsBuilder::SetGroupPolicies(
+    const base::Value::DictStorage& group_policies) {
+  overrides_.SetKey(kDevOverrideKeyGroupPolicies, base::Value(group_policies));
+  return *this;
+}
+
+ExternalConstantsBuilder& ExternalConstantsBuilder::ClearGroupPolicies() {
+  overrides_.RemoveKey(kDevOverrideKeyGroupPolicies);
+  return *this;
+}
+
 bool ExternalConstantsBuilder::Overwrite() {
   const absl::optional<base::FilePath> base_path =
       GetBaseDirectory(GetUpdaterScope());
@@ -92,6 +124,7 @@
     LOG(ERROR) << "Can't find base directory; can't save constant overrides.";
     return false;
   }
+
   const base::FilePath override_file_path =
       base_path.value().AppendASCII(kDevOverrideFileName);
   bool ok = JSONFileValueSerializer(override_file_path).Serialize(overrides_);
@@ -99,4 +132,27 @@
   return ok;
 }
 
+bool ExternalConstantsBuilder::Modify() {
+  scoped_refptr<ExternalConstantsOverrider> verifier =
+      ExternalConstantsOverrider::FromDefaultJSONFile(
+          CreateDefaultExternalConstants());
+  if (!verifier)
+    return Overwrite();
+
+  if (!overrides_.FindKey(kDevOverrideKeyUrl))
+    SetUpdateURL(StringVectorFromGURLVector(verifier->UpdateURL()));
+  if (!overrides_.FindKey(kDevOverrideKeyUseCUP))
+    SetUseCUP(verifier->UseCUP());
+  if (!overrides_.FindKey(kDevOverrideKeyInitialDelay))
+    SetInitialDelay(verifier->InitialDelay());
+  if (!overrides_.FindKey(kDevOverrideKeyServerKeepAliveSeconds))
+    SetServerKeepAliveSeconds(verifier->ServerKeepAliveSeconds());
+  if (!overrides_.FindKey(kDevOverrideKeyCrxVerifierFormat))
+    SetCrxVerifierFormat(verifier->CrxVerifierFormat());
+  if (!overrides_.FindKey(kDevOverrideKeyGroupPolicies))
+    SetGroupPolicies(verifier->GroupPolicies());
+
+  return Overwrite();
+}
+
 }  // namespace updater
diff --git a/chrome/updater/external_constants_builder.h b/chrome/updater/external_constants_builder.h
index 0746276..6c574ca4 100644
--- a/chrome/updater/external_constants_builder.h
+++ b/chrome/updater/external_constants_builder.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/files/file_path.h"
 #include "base/values.h"
 
 namespace crx_file {
@@ -50,6 +51,10 @@
       crx_file::VerifierFormat crx_verifier_format);
   ExternalConstantsBuilder& ClearCrxVerifierFormat();
 
+  ExternalConstantsBuilder& SetGroupPolicies(
+      const base::Value::DictStorage& group_policies);
+  ExternalConstantsBuilder& ClearGroupPolicies();
+
   // Write the external constants overrides file in the default location
   // with the values that have been previously set, replacing any file
   // previously there. The builder remains usable, does not forget its state,
@@ -58,6 +63,10 @@
   // Returns true on success, false on failure.
   bool Overwrite();
 
+  // Blend the set values in this instance with the external constants overrides
+  // file in the default location.
+  bool Modify();
+
  private:
   base::Value overrides_{base::Value::Type::DICTIONARY};
   bool written_ = false;
diff --git a/chrome/updater/external_constants_builder_unittest.cc b/chrome/updater/external_constants_builder_unittest.cc
index ef65b6de..4a027fd 100644
--- a/chrome/updater/external_constants_builder_unittest.cc
+++ b/chrome/updater/external_constants_builder_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/values.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/external_constants.h"
 #include "chrome/updater/external_constants_builder.h"
@@ -70,14 +71,20 @@
 
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveSeconds(), kServerKeepAliveSeconds);
+  EXPECT_EQ(verifier->GroupPolicies().size(), 0U);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestOverridingEverything) {
+  base::Value::DictStorage group_policies;
+  group_policies["a"] = base::Value(1);
+  group_policies["b"] = base::Value(2);
+
   ExternalConstantsBuilder builder;
   builder.SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
       .SetUseCUP(false)
       .SetInitialDelay(123)
-      .SetServerKeepAliveSeconds(2);
+      .SetServerKeepAliveSeconds(2)
+      .SetGroupPolicies(group_policies);
   EXPECT_TRUE(builder.Overwrite());
 
   scoped_refptr<ExternalConstantsOverrider> verifier =
@@ -92,6 +99,7 @@
 
   EXPECT_EQ(verifier->InitialDelay(), 123);
   EXPECT_EQ(verifier->ServerKeepAliveSeconds(), 2);
+  EXPECT_EQ(verifier->GroupPolicies().size(), 2U);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestPartialOverrideWithMultipleURLs) {
@@ -114,6 +122,7 @@
 
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveSeconds(), kServerKeepAliveSeconds);
+  EXPECT_EQ(verifier->GroupPolicies().size(), 0U);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestClearedEverything) {
@@ -127,6 +136,7 @@
                   .ClearUseCUP()
                   .ClearInitialDelay()
                   .ClearServerKeepAliveSeconds()
+                  .ClearGroupPolicies()
                   .Overwrite());
 
   scoped_refptr<ExternalConstantsOverrider> verifier =
@@ -140,15 +150,20 @@
 
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveSeconds(), kServerKeepAliveSeconds);
+  EXPECT_EQ(verifier->GroupPolicies().size(), 0U);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestOverSet) {
+  base::Value::DictStorage group_policies;
+  group_policies["a"] = base::Value(1);
+
   EXPECT_TRUE(
       ExternalConstantsBuilder()
           .SetUpdateURL(std::vector<std::string>{"https://www.google.com"})
           .SetUseCUP(true)
           .SetInitialDelay(123.4)
           .SetServerKeepAliveSeconds(2)
+          .SetGroupPolicies(group_policies)
           .SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
           .SetUseCUP(false)
           .SetInitialDelay(937.6)
@@ -167,16 +182,23 @@
 
   EXPECT_EQ(verifier->InitialDelay(), 937.6);
   EXPECT_EQ(verifier->ServerKeepAliveSeconds(), 3);
+  EXPECT_EQ(verifier->GroupPolicies().size(), 1U);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestReuseBuilder) {
   ExternalConstantsBuilder builder;
+
+  base::Value::DictStorage group_policies;
+  group_policies["a"] = base::Value(1);
+  group_policies["b"] = base::Value(2);
+
   EXPECT_TRUE(
       builder.SetUpdateURL(std::vector<std::string>{"https://www.google.com"})
           .SetUseCUP(false)
           .SetInitialDelay(123.4)
           .SetServerKeepAliveSeconds(3)
           .SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
+          .SetGroupPolicies(group_policies)
           .Overwrite());
 
   scoped_refptr<ExternalConstantsOverrider> verifier =
@@ -191,11 +213,16 @@
 
   EXPECT_EQ(verifier->InitialDelay(), 123.4);
   EXPECT_EQ(verifier->ServerKeepAliveSeconds(), 3);
+  EXPECT_EQ(verifier->GroupPolicies().size(), 2U);
+
+  base::Value::DictStorage group_policies2;
+  group_policies2["b"] = base::Value(2);
 
   // But now we can use the builder again:
   EXPECT_TRUE(builder.SetInitialDelay(92.3)
                   .SetServerKeepAliveSeconds(4)
                   .ClearUpdateURL()
+                  .SetGroupPolicies(group_policies2)
                   .Overwrite());
 
   // We need a new overrider to verify because it only loads once.
@@ -212,6 +239,62 @@
   EXPECT_EQ(verifier2->InitialDelay(),
             92.3);  // Updated; update should be seen.
   EXPECT_EQ(verifier2->ServerKeepAliveSeconds(), 4);
+  EXPECT_EQ(verifier2->GroupPolicies().size(), 1U);
+}
+
+TEST_F(ExternalConstantsBuilderTests, TestModify) {
+  ExternalConstantsBuilder builder;
+
+  base::Value::DictStorage group_policies;
+  group_policies["a"] = base::Value(1);
+  group_policies["b"] = base::Value(2);
+
+  EXPECT_TRUE(
+      builder.SetUpdateURL(std::vector<std::string>{"https://www.google.com"})
+          .SetUseCUP(false)
+          .SetInitialDelay(123.4)
+          .SetServerKeepAliveSeconds(3)
+          .SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
+          .SetGroupPolicies(group_policies)
+          .Overwrite());
+
+  scoped_refptr<ExternalConstantsOverrider> verifier =
+      ExternalConstantsOverrider::FromDefaultJSONFile(
+          CreateDefaultExternalConstants());
+
+  EXPECT_FALSE(verifier->UseCUP());
+
+  std::vector<GURL> urls = verifier->UpdateURL();
+  ASSERT_EQ(urls.size(), 1ul);
+  EXPECT_EQ(urls[0], GURL("https://www.example.com"));
+
+  EXPECT_EQ(verifier->InitialDelay(), 123.4);
+  EXPECT_EQ(verifier->ServerKeepAliveSeconds(), 3);
+  EXPECT_EQ(verifier->GroupPolicies().size(), 2U);
+
+  // Now we use a new builder to modify just the group policies.
+  ExternalConstantsBuilder builder2;
+
+  base::Value::DictStorage group_policies2;
+  group_policies2["b"] = base::Value(2);
+
+  EXPECT_TRUE(builder2.SetGroupPolicies(group_policies2).Modify());
+
+  // We need a new overrider to verify because it only loads once.
+  scoped_refptr<ExternalConstantsOverrider> verifier2 =
+      ExternalConstantsOverrider::FromDefaultJSONFile(
+          CreateDefaultExternalConstants());
+
+  // Only the group policies are different.
+  EXPECT_EQ(verifier2->GroupPolicies().size(), 1U);
+
+  // All the values below are unchanged.
+  EXPECT_FALSE(verifier2->UseCUP());
+  urls = verifier2->UpdateURL();
+  ASSERT_EQ(urls.size(), 1ul);
+  EXPECT_EQ(urls[0], GURL("https://www.example.com"));
+  EXPECT_EQ(verifier2->InitialDelay(), 123.4);
+  EXPECT_EQ(verifier2->ServerKeepAliveSeconds(), 3);
 }
 
 }  // namespace updater
diff --git a/chrome/updater/external_constants_default.cc b/chrome/updater/external_constants_default.cc
index 2fa257f..428426f1 100644
--- a/chrome/updater/external_constants_default.cc
+++ b/chrome/updater/external_constants_default.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/external_constants_default.h"
 
 #include "base/memory/scoped_refptr.h"
+#include "base/values.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/external_constants.h"
 #include "chrome/updater/updater_branding.h"
@@ -35,6 +36,8 @@
     return crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF;
   }
 
+  base::Value::DictStorage GroupPolicies() const override { return {}; }
+
  private:
   ~DefaultExternalConstants() override = default;
 };
diff --git a/chrome/updater/external_constants_override.cc b/chrome/updater/external_constants_override.cc
index 99929d69..41d5fbe 100644
--- a/chrome/updater/external_constants_override.cc
+++ b/chrome/updater/external_constants_override.cc
@@ -132,6 +132,19 @@
       crx_format_verifier_value.GetInt());
 }
 
+base::Value::DictStorage ExternalConstantsOverrider::GroupPolicies() const {
+  if (!override_values_.contains(kDevOverrideKeyGroupPolicies)) {
+    return next_provider_->GroupPolicies();
+  }
+
+  const base::Value& group_policies_value =
+      override_values_.at(kDevOverrideKeyGroupPolicies);
+  CHECK(group_policies_value.is_dict())
+      << "Unexpected type of override[" << kDevOverrideKeyGroupPolicies
+      << "]: " << base::Value::GetTypeName(group_policies_value.type());
+  return group_policies_value.Clone().TakeDictDeprecated();
+}
+
 // static
 scoped_refptr<ExternalConstantsOverrider>
 ExternalConstantsOverrider::FromDefaultJSONFile(
diff --git a/chrome/updater/external_constants_override.h b/chrome/updater/external_constants_override.h
index bcfa156a..d9907ea 100644
--- a/chrome/updater/external_constants_override.h
+++ b/chrome/updater/external_constants_override.h
@@ -11,6 +11,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/values.h"
 #include "chrome/updater/external_constants.h"
 
 class GURL;
@@ -45,6 +46,7 @@
   double InitialDelay() const override;
   int ServerKeepAliveSeconds() const override;
   crx_file::VerifierFormat CrxVerifierFormat() const override;
+  base::Value::DictStorage GroupPolicies() const override;
 
  private:
   const base::flat_map<std::string, base::Value> override_values_;
diff --git a/chrome/updater/external_constants_override_unittest.cc b/chrome/updater/external_constants_override_unittest.cc
index 21d23e9..1b826914 100644
--- a/chrome/updater/external_constants_override_unittest.cc
+++ b/chrome/updater/external_constants_override_unittest.cc
@@ -33,6 +33,7 @@
 
   EXPECT_EQ(overrider->InitialDelay(), kInitialDelay);
   EXPECT_EQ(overrider->ServerKeepAliveSeconds(), kServerKeepAliveSeconds);
+  EXPECT_EQ(overrider->GroupPolicies().size(), 0U);
 }
 
 TEST_F(ExternalConstantsOverriderTest, TestFullOverrides) {
@@ -40,10 +41,15 @@
   base::Value::ListStorage url_list;
   url_list.push_back(base::Value("https://www.example.com"));
   url_list.push_back(base::Value("https://www.google.com"));
+  base::Value::DictStorage group_policies;
+  group_policies["a"] = base::Value(1);
+  group_policies["b"] = base::Value(2);
+
   overrides[kDevOverrideKeyUseCUP] = base::Value(false);
   overrides[kDevOverrideKeyUrl] = base::Value(std::move(url_list));
   overrides[kDevOverrideKeyInitialDelay] = base::Value(137.1);
   overrides[kDevOverrideKeyServerKeepAliveSeconds] = base::Value(1);
+  overrides[kDevOverrideKeyGroupPolicies] = base::Value(group_policies);
   auto overrider = base::MakeRefCounted<ExternalConstantsOverrider>(
       std::move(overrides), CreateDefaultExternalConstants());
 
@@ -58,6 +64,7 @@
 
   EXPECT_EQ(overrider->InitialDelay(), 137.1);
   EXPECT_EQ(overrider->ServerKeepAliveSeconds(), 1);
+  EXPECT_EQ(overrider->GroupPolicies().size(), 2U);
 }
 
 TEST_F(ExternalConstantsOverriderTest, TestOverrideUnwrappedURL) {
@@ -75,6 +82,7 @@
   EXPECT_TRUE(overrider->UseCUP());
   EXPECT_EQ(overrider->InitialDelay(), kInitialDelay);
   EXPECT_EQ(overrider->ServerKeepAliveSeconds(), kServerKeepAliveSeconds);
+  EXPECT_EQ(overrider->GroupPolicies().size(), 0U);
 }
 
 }  // namespace updater
diff --git a/chrome/updater/policy/service.cc b/chrome/updater/policy/service.cc
index f8b1e2f..35fcc9d 100644
--- a/chrome/updater/policy/service.cc
+++ b/chrome/updater/policy/service.cc
@@ -10,8 +10,10 @@
 #include "base/callback.h"
 #include "base/check.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
+#include "chrome/updater/external_constants.h"
 
 #include "chrome/updater/policy/dm_policy_manager.h"
 #if BUILDFLAG(IS_WIN)
@@ -208,10 +210,11 @@
   return true;
 }
 
-scoped_refptr<PolicyService> PolicyService::Create() {
+scoped_refptr<PolicyService> PolicyService::Create(
+    scoped_refptr<ExternalConstants> external_constants) {
   PolicyManagerVector managers;
 #if BUILDFLAG(IS_WIN)
-  managers.push_back(std::make_unique<GroupPolicyManager>());
+  managers.push_back(std::make_unique<GroupPolicyManager>(external_constants));
 #endif
   std::unique_ptr<PolicyManagerInterface> dm_policy_manager =
       CreateDMPolicyManager();
diff --git a/chrome/updater/policy/service.h b/chrome/updater/policy/service.h
index 9f56a05..38d50b57 100644
--- a/chrome/updater/policy/service.h
+++ b/chrome/updater/policy/service.h
@@ -11,6 +11,8 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "chrome/updater/external_constants.h"
 #include "chrome/updater/policy/manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -106,7 +108,8 @@
                       std::string* proxy_server) const;
 
   // Creates an instance that takes a snapshot of policies from all providers.
-  static scoped_refptr<PolicyService> Create();
+  static scoped_refptr<PolicyService> Create(
+      scoped_refptr<ExternalConstants> external_constants);
 
  protected:
   virtual ~PolicyService();
diff --git a/chrome/updater/policy/win/group_policy_manager.cc b/chrome/updater/policy/win/group_policy_manager.cc
index 30d1ebd..42d28e24 100644
--- a/chrome/updater/policy/win/group_policy_manager.cc
+++ b/chrome/updater/policy/win/group_policy_manager.cc
@@ -11,10 +11,12 @@
 #include <userenv.h>
 
 #include "base/enterprise_util.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/scoped_generic.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
 #include "base/win/registry.h"
+#include "chrome/updater/external_constants.h"
 #include "chrome/updater/policy/manager.h"
 #include "chrome/updater/win/win_constants.h"
 
@@ -75,14 +77,21 @@
 
 }  // namespace
 
-GroupPolicyManager::GroupPolicyManager() {
+GroupPolicyManager::GroupPolicyManager(
+    scoped_refptr<ExternalConstants> external_constants)
+    : external_constants_group_policies_(external_constants->GroupPolicies()) {
   LoadAllPolicies();
 }
 
 GroupPolicyManager::~GroupPolicyManager() = default;
 
 bool GroupPolicyManager::IsManaged() const {
-  return policies_.DictSize() > 0 && base::IsManagedDevice();
+  return !policies_.DictEmpty() && IsManagedInternal();
+}
+
+bool GroupPolicyManager::IsManagedInternal() const {
+  return !external_constants_group_policies_.DictEmpty() ||
+         base::IsManagedDevice();
 }
 
 std::string GroupPolicyManager::source() const {
@@ -204,7 +213,7 @@
 void GroupPolicyManager::LoadAllPolicies() {
   scoped_hpolicy policy_lock;
 
-  if (base::IsManagedDevice()) {
+  if (IsManagedInternal()) {
     // GPO rules mandate a call to EnterCriticalPolicySection() before reading
     // policies (and a matching LeaveCriticalPolicySection() call after read).
     // Acquire the lock for managed machines because group policies are
@@ -214,6 +223,11 @@
     CHECK(policy_lock.is_valid()) << "Failed to get policy lock.";
   }
 
+  if (!external_constants_group_policies_.DictEmpty()) {
+    policies_ = external_constants_group_policies_.Clone();
+    return;
+  }
+
   base::Value::DictStorage policy_storage;
 
   for (base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE,
diff --git a/chrome/updater/policy/win/group_policy_manager.h b/chrome/updater/policy/win/group_policy_manager.h
index 0913e37..713b8066 100644
--- a/chrome/updater/policy/win/group_policy_manager.h
+++ b/chrome/updater/policy/win/group_policy_manager.h
@@ -7,7 +7,9 @@
 
 #include <string>
 
+#include "base/memory/scoped_refptr.h"
 #include "base/values.h"
+#include "chrome/updater/external_constants.h"
 #include "chrome/updater/policy/manager.h"
 
 namespace updater {
@@ -15,7 +17,8 @@
 // The GroupPolicyManager returns policies for domain-joined machines.
 class GroupPolicyManager : public PolicyManagerInterface {
  public:
-  GroupPolicyManager();
+  explicit GroupPolicyManager(
+      scoped_refptr<ExternalConstants> external_constants);
   GroupPolicyManager(const GroupPolicyManager&) = delete;
   GroupPolicyManager& operator=(const GroupPolicyManager&) = delete;
   ~GroupPolicyManager() override;
@@ -49,11 +52,13 @@
   bool GetProxyServer(std::string* proxy_server) const override;
 
  private:
+  bool IsManagedInternal() const;
   void LoadAllPolicies();
   bool GetIntPolicy(const std::string& key, int* value) const;
   bool GetStringPolicy(const std::string& key, std::string* value) const;
 
   base::Value policies_;
+  const base::Value external_constants_group_policies_;
 };
 
 }  // namespace updater
diff --git a/chrome/updater/policy/win/group_policy_manager_unittest.cc b/chrome/updater/policy/win/group_policy_manager_unittest.cc
index 0191681..7f9dcea 100644
--- a/chrome/updater/policy/win/group_policy_manager_unittest.cc
+++ b/chrome/updater/policy/win/group_policy_manager_unittest.cc
@@ -48,7 +48,7 @@
 
 TEST_F(GroupPolicyManagerTests, NoPolicySet) {
   std::unique_ptr<PolicyManagerInterface> policy_manager =
-      std::make_unique<GroupPolicyManager>();
+      std::make_unique<GroupPolicyManager>(CreateExternalConstants());
   EXPECT_FALSE(policy_manager->IsManaged());
 
   EXPECT_EQ(policy_manager->source(), "GroupPolicy");
@@ -142,7 +142,7 @@
             key.WriteValue(L"RollbackToTargetVersion" TEST_APP_ID, 1));
 
   std::unique_ptr<PolicyManagerInterface> policy_manager =
-      std::make_unique<GroupPolicyManager>();
+      std::make_unique<GroupPolicyManager>(CreateExternalConstants());
   EXPECT_EQ(policy_manager->IsManaged(), base::win::IsEnrolledToDomain());
 
   int check_period = 0;
@@ -250,7 +250,7 @@
             key.WriteValue(L"RollbackToTargetVersion" TEST_APP_ID, L"1"));
 
   std::unique_ptr<PolicyManagerInterface> policy_manager =
-      std::make_unique<GroupPolicyManager>();
+      std::make_unique<GroupPolicyManager>(CreateExternalConstants());
 
   int check_period = 0;
   EXPECT_FALSE(policy_manager->GetLastCheckPeriodMinutes(&check_period));
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h
index a512682..39a797bb 100644
--- a/chrome/updater/test/integration_test_commands.h
+++ b/chrome/updater/test/integration_test_commands.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/memory/ref_counted.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/updater/test/integration_tests_impl.h"
 #include "chrome/updater/update_service.h"
@@ -28,6 +29,8 @@
     : public base::RefCountedThreadSafe<IntegrationTestCommands> {
  public:
   virtual void EnterTestMode(const GURL& url) const = 0;
+  virtual void SetGroupPolicies(
+      const base::Value::DictStorage& values) const = 0;
   virtual void Clean() const = 0;
   virtual void ExpectClean() const = 0;
   virtual void ExpectInstalled() const = 0;
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc
index 66ea329..8a22b04 100644
--- a/chrome/updater/test/integration_test_commands_system.cc
+++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -11,10 +11,12 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/json/json_writer.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/notreached.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/values.h"
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
@@ -37,6 +39,16 @@
 namespace updater {
 namespace test {
 
+namespace {
+
+std::string StringFromDictStorage(const base::Value::DictStorage& values) {
+  std::string values_string;
+  EXPECT_TRUE(base::JSONWriter::Write(base::Value(values), &values_string));
+  return values_string;
+}
+
+}  // namespace
+
 class IntegrationTestCommandsSystem : public IntegrationTestCommands {
  public:
   IntegrationTestCommandsSystem() = default;
@@ -68,6 +80,11 @@
     RunCommand("enter_test_mode", {Param("url", url.spec())});
   }
 
+  void SetGroupPolicies(const base::Value::DictStorage& values) const override {
+    RunCommand("set_group_policies",
+               {Param("values", StringFromDictStorage(values))});
+  }
+
   void ExpectSelfUpdateSequence(ScopedServer* test_server) const override {
     updater::test::ExpectSelfUpdateSequence(updater_scope_, test_server);
   }
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc
index 44ebaae..421e5f1 100644
--- a/chrome/updater/test/integration_test_commands_user.cc
+++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -65,6 +65,10 @@
     updater::test::ExpectSelfUpdateSequence(updater_scope_, test_server);
   }
 
+  void SetGroupPolicies(const base::Value::DictStorage& values) const override {
+    updater::test::SetGroupPolicies(values);
+  }
+
   void ExpectUpdateSequence(ScopedServer* test_server,
                             const std::string& app_id,
                             const std::string& install_data_index,
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index acfa3d9..7cca93b 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -20,6 +20,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_timeouts.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "base/version.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
@@ -134,6 +135,10 @@
 
   void EnterTestMode(const GURL& url) { test_commands_->EnterTestMode(url); }
 
+  void SetGroupPolicies(const base::Value::DictStorage& values) {
+    test_commands_->SetGroupPolicies(values);
+  }
+
   void ExpectVersionActive(const std::string& version) {
     test_commands_->ExpectVersionActive(version);
   }
@@ -486,35 +491,23 @@
   ExpectNoUpdateSequence(&test_server, kAppId);
   ExpectLegacyUpdate3WebSucceeds(kAppId, STATE_NO_UPDATE, S_OK);
 
+  base::Value::DictStorage group_policies;
+  group_policies["Updatetest1"] = base::Value(kPolicyAutomaticUpdatesOnly);
+  SetGroupPolicies(group_policies);
+  ExpectLegacyUpdate3WebSucceeds(
+      kAppId, STATE_ERROR, GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL);
+
+  group_policies["Updatetest1"] = base::Value(kPolicyDisabled);
+  SetGroupPolicies(group_policies);
+  ExpectLegacyUpdate3WebSucceeds(kAppId, STATE_ERROR,
+                                 GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY);
+
+  group_policies.clear();
+  SetGroupPolicies(group_policies);
   ExpectUpdateSequence(&test_server, kAppId, "", base::Version("0.1"),
                        base::Version("0.2"));
   ExpectLegacyUpdate3WebSucceeds(kAppId, STATE_INSTALL_COMPLETE, S_OK);
 
-  // TODO(crbug.com/1272853) - Need administrative access to be able to write
-  // under the policies key.
-  if (::IsUserAnAdmin()) {
-    base::win::RegKey key(HKEY_LOCAL_MACHINE, UPDATER_POLICIES_KEY,
-                          Wow6432(KEY_ALL_ACCESS));
-
-    EXPECT_EQ(ERROR_SUCCESS,
-              key.WriteValue(
-                  base::StrCat({L"Update", base::UTF8ToWide(kAppId)}).c_str(),
-                  kPolicyAutomaticUpdatesOnly));
-    ExpectLegacyUpdate3WebSucceeds(
-        kAppId, STATE_ERROR, GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL);
-
-    EXPECT_EQ(ERROR_SUCCESS,
-              key.WriteValue(
-                  base::StrCat({L"Update", base::UTF8ToWide(kAppId)}).c_str(),
-                  static_cast<DWORD>(kPolicyDisabled)));
-    ExpectLegacyUpdate3WebSucceeds(kAppId, STATE_ERROR,
-                                   GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY);
-
-    EXPECT_EQ(ERROR_SUCCESS,
-              base::win::RegKey(HKEY_LOCAL_MACHINE, L"", Wow6432(DELETE))
-                  .DeleteKey(UPDATER_POLICIES_KEY));
-  }
-
   Uninstall();
 }
 
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index 45f0a734..7b318c3 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/check.h"
 #include "base/command_line.h"
+#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -23,6 +24,7 @@
 #include "base/test/test_suite.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
@@ -31,6 +33,7 @@
 #include "chrome/updater/test/integration_tests_impl.h"
 #include "chrome/updater/updater_scope.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -54,6 +57,12 @@
 constexpr int kUnknownSwitch = 101;
 constexpr int kBadCommand = 102;
 
+base::Value::DictStorage DictStorageFromString(const std::string& values) {
+  absl::optional<base::Value> results_value = base::JSONReader::Read(values);
+  EXPECT_TRUE(results_value);
+  return results_value->Clone().TakeDictDeprecated();
+}
+
 template <typename... Args>
 base::RepeatingCallback<bool(Args...)> WithSwitch(
     const std::string& flag,
@@ -138,6 +147,19 @@
       }));
 }
 
+// Overload for base::Value::DictStorage switches.
+template <typename... Args>
+base::RepeatingCallback<bool(Args...)> WithSwitch(
+    const std::string& flag,
+    base::RepeatingCallback<bool(const base::Value::DictStorage&, Args...)>
+        callback) {
+  return WithSwitch(
+      flag,
+      base::BindLambdaForTesting([=](const std::string& flag, Args... args) {
+        return callback.Run(DictStorageFromString(flag), std::move(args)...);
+      }));
+}
+
 template <typename Arg, typename... RemainingArgs>
 base::RepeatingCallback<bool(RemainingArgs...)> WithArg(
     Arg arg,
@@ -198,6 +220,7 @@
     // then use the With* helper functions to provide its arguments.
     {"clean", WithSystemScope(Wrap(&Clean))},
     {"enter_test_mode", WithSwitch("url", Wrap(&EnterTestMode))},
+    {"set_group_policies", WithSwitch("values", Wrap(&SetGroupPolicies))},
     {"expect_active_updater", WithSystemScope(Wrap(&ExpectActiveUpdater))},
     {"expect_registered",
      WithSwitch("app_id", WithSystemScope(Wrap(&ExpectRegistered)))},
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index 5982806..99b674d 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -42,6 +42,7 @@
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/updater/constants.h"
+#include "chrome/updater/external_constants_builder.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/registration_data.h"
@@ -188,6 +189,10 @@
   loop.Run();
 }
 
+void SetGroupPolicies(const base::Value::DictStorage& values) {
+  ASSERT_TRUE(ExternalConstantsBuilder().SetGroupPolicies(values).Modify());
+}
+
 void ExpectVersionActive(UpdaterScope scope, const std::string& version) {
   scoped_refptr<GlobalPrefs> prefs = CreateGlobalPrefs(scope);
   ASSERT_NE(prefs, nullptr) << "Failed to acquire GlobalPrefs.";
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index d9d0bdaa..29d6a7f 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ref_counted.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -51,6 +52,9 @@
 // CUP).
 void EnterTestMode(const GURL& url);
 
+// Sets the external constants for group policies.
+void SetGroupPolicies(const base::Value::DictStorage& values);
+
 // Copies the logs to a location where they can be retrieved by ResultDB.
 void CopyLog(const base::FilePath& src_dir);
 
diff --git a/chrome/updater/test/integration_tests_mac.mm b/chrome/updater/test/integration_tests_mac.mm
index c6b3a6f7..f8e5212 100644
--- a/chrome/updater/test/integration_tests_mac.mm
+++ b/chrome/updater/test/integration_tests_mac.mm
@@ -120,7 +120,7 @@
                   .SetInitialDelay(0.1)
                   .SetServerKeepAliveSeconds(1)
                   .SetCrxVerifierFormat(crx_file::VerifierFormat::CRX3)
-                  .Overwrite());
+                  .Modify());
 }
 
 absl::optional<base::FilePath> GetDataDirPath(UpdaterScope scope) {
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc
index 4c12f1b..0f38438 100644
--- a/chrome/updater/test/integration_tests_win.cc
+++ b/chrome/updater/test/integration_tests_win.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <shlobj.h>
 #include <wrl/client.h>
 
 #include <regstr.h>
@@ -245,10 +246,12 @@
                         .HasSwitch(kWakeSwitch));
       }
     } else {
-      for (const wchar_t* key :
-           {kRegKeyCompanyCloudManagement, kRegKeyCompanyEnrollment,
-            UPDATER_POLICIES_KEY}) {
-        EXPECT_FALSE(RegKeyExists(HKEY_LOCAL_MACHINE, key));
+      if (::IsUserAnAdmin()) {
+        for (const wchar_t* key :
+             {kRegKeyCompanyCloudManagement, kRegKeyCompanyEnrollment,
+              UPDATER_POLICIES_KEY}) {
+          EXPECT_FALSE(RegKeyExists(HKEY_LOCAL_MACHINE, key));
+        }
       }
 
       EXPECT_FALSE(RegKeyExists(root, UPDATER_KEY));
@@ -428,9 +431,13 @@
   for (const wchar_t* key : {CLIENT_STATE_KEY, CLIENTS_KEY, UPDATER_KEY}) {
     EXPECT_TRUE(DeleteRegKey(root, key));
   }
-  for (const wchar_t* key : {kRegKeyCompanyCloudManagement,
-                             kRegKeyCompanyEnrollment, UPDATER_POLICIES_KEY}) {
-    EXPECT_TRUE(DeleteRegKey(HKEY_LOCAL_MACHINE, key));
+
+  if (::IsUserAnAdmin()) {
+    for (const wchar_t* key :
+         {kRegKeyCompanyCloudManagement, kRegKeyCompanyEnrollment,
+          UPDATER_POLICIES_KEY}) {
+      EXPECT_TRUE(DeleteRegKey(HKEY_LOCAL_MACHINE, key));
+    }
   }
 
   for (const CLSID& clsid :
@@ -487,7 +494,7 @@
                   .SetUseCUP(false)
                   .SetInitialDelay(0.1)
                   .SetCrxVerifierFormat(crx_file::VerifierFormat::CRX3)
-                  .Overwrite());
+                  .Modify());
 }
 
 void ExpectInstalled(UpdaterScope scope) {
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index 0fa1d4be..2ebce49c 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -1247,15 +1247,15 @@
         request_->ParseResponse(*message_value);
       }
 
-      if (base::LowerCaseEqualsASCII(error_api_error_reason,
-                                     "virtual_card_temporary_error")) {
+      if (base::EqualsCaseInsensitiveASCII(error_api_error_reason,
+                                           "virtual_card_temporary_error")) {
         result =
             AutofillClient::PaymentsRpcResult::kVcnRetrievalTryAgainFailure;
-      } else if (base::LowerCaseEqualsASCII(error_api_error_reason,
-                                            "virtual_card_permanent_error")) {
+      } else if (base::EqualsCaseInsensitiveASCII(
+                     error_api_error_reason, "virtual_card_permanent_error")) {
         result =
             AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure;
-      } else if (base::LowerCaseEqualsASCII(error_code, "internal")) {
+      } else if (base::EqualsCaseInsensitiveASCII(error_code, "internal")) {
         result = AutofillClient::PaymentsRpcResult::kTryAgainFailure;
       } else if (!error_code.empty() || !request_->IsResponseComplete()) {
         result = AutofillClient::PaymentsRpcResult::kPermanentFailure;
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index 476fcad..ce8a8fc4 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -80,7 +80,7 @@
 // preflight call.
 const base::Feature kAutofillEnableSendingBcnInGetUploadDetails{
     "AutofillEnableSendingBcnInGetUploadDetails",
-    base::FEATURE_ENABLED_BY_DEFAULT};
+    base::FEATURE_DISABLED_BY_DEFAULT};
 
 // When enabled, if the user interacts with the manual fallback bottom sheet
 // on Android, it'll remain sticky until the user dismisses it.
diff --git a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
index fd574b4..58c74d62 100644
--- a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
+++ b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
@@ -11,7 +11,7 @@
 #include "base/containers/flat_map.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
-#include "base/substring_set_matcher/string_pattern.h"
+#include "base/substring_set_matcher/matcher_string_pattern.h"
 #include "components/feed/core/proto/v2/store.pb.h"
 #include "components/feed/core/proto/v2/wire/web_feed_matcher.pb.h"
 #include "components/feed/core/v2/feedstore_util.h"
@@ -168,8 +168,8 @@
 
   // Members that just hold on to memory used in matchers.
   url_matcher::URLMatcherConditionFactory condition_factory_;
-  std::vector<base::StringPattern> host_match_patterns_;
-  std::vector<base::StringPattern> path_match_patterns_;
+  std::vector<base::MatcherStringPattern> host_match_patterns_;
+  std::vector<base::MatcherStringPattern> path_match_patterns_;
 
   // List of `MultiConditionSet`. Each one knows how to aggregate match IDs
   // reported from matchers below into a WebFeed match.
@@ -363,9 +363,9 @@
     return true;
   }
 
-  static std::vector<const base::StringPattern*> MakeVectorOfPointers(
-      const std::vector<base::StringPattern>& patterns) {
-    std::vector<const base::StringPattern*> result(patterns.size());
+  static std::vector<const base::MatcherStringPattern*> MakeVectorOfPointers(
+      const std::vector<base::MatcherStringPattern>& patterns) {
+    std::vector<const base::MatcherStringPattern*> result(patterns.size());
     for (size_t i = 0; i < patterns.size(); ++i) {
       result[i] = &patterns[i];
     }
diff --git a/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc b/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
index 089633a4..3700f19d 100644
--- a/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
+++ b/components/policy/core/common/cloud/encrypted_reporting_job_configuration.cc
@@ -281,7 +281,8 @@
   auto* const state = AccessState(priority_, generation_id_, sequence_id_);
   if (net_error != ::net::OK) {
     // Network error
-  } else if (response_code >= 400 && response_code <= 499) {
+  } else if (response_code >= 400 && response_code <= 499 &&
+             response_code != 409 /* Overlapping seq_id ranges detected */) {
     // Permanent error code returned by server, impose artificial 24h backoff.
     state->backoff_entry->SetCustomReleaseTime(
         state->backoff_entry->GetTimeTicksNow() + base::Days(1));
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index baa208e4..4157fc05 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -99,6 +99,8 @@
     "scheduler/model_execution_scheduler.h",
     "scheduler/model_execution_scheduler_impl.cc",
     "scheduler/model_execution_scheduler_impl.h",
+    "segment_id_convertor.cc",
+    "segment_id_convertor.h",
     "segmentation_platform_service_impl.cc",
     "segmentation_platform_service_impl.h",
     "segmentation_ukm_helper.cc",
@@ -150,6 +152,7 @@
     "//components/prefs",
     "//components/segmentation_platform/internal/proto",
     "//components/segmentation_platform/public",
+    "//components/segmentation_platform/public/proto",
     "//components/ukm:ukm_recorder",
     "//services/metrics/public/cpp:metrics_cpp",
     "//services/metrics/public/cpp:ukm_builders",
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
index 435f317..f63cf16 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
@@ -55,7 +55,7 @@
 
 // Find the segmentation key from the configs that contains the segment ID.
 std::string GetSegmentationKey(std::vector<std::unique_ptr<Config>>* configs,
-                               OptimizationTarget segment_id) {
+                               SegmentId segment_id) {
   if (!configs)
     return std::string();
 
@@ -107,7 +107,7 @@
   histogram_signal_handler_->AddObserver(this);
 
   DCHECK(segments);
-  const base::flat_set<OptimizationTarget>& allowed_ids =
+  const base::flat_set<SegmentId>& allowed_ids =
       SegmentationUkmHelper::GetInstance()->allowed_segment_ids();
   for (const auto& segment : *segments) {
     // Skip the segment if it is not in allowed list.
@@ -156,13 +156,12 @@
   // Report training data for all models that are interested in
   // |histogram_name| as output.
   if (it != immediate_collection_histograms_.end()) {
-    std::vector<OptimizationTarget> optimization_targets(it->second.begin(),
-                                                         it->second.end());
+    std::vector<SegmentId> segment_ids(it->second.begin(), it->second.end());
     auto param = absl::make_optional<ImmediaCollectionParam>();
     param->output_metric_hash = hash;
     param->output_value = static_cast<float>(sample);
     segment_info_database_->GetSegmentInfoForSegments(
-        optimization_targets,
+        segment_ids,
         base::BindOnce(&TrainingDataCollectorImpl::ReportForSegmentsInfoList,
                        weak_ptr_factory_.GetWeakPtr(), std::move(param)));
   }
@@ -336,8 +335,8 @@
   base::Time next_collection_time = GetNextReportTime(last_collection_time);
   if (clock_->Now() >= next_collection_time) {
     segment_info_database_->GetSegmentInfoForSegments(
-        std::vector<OptimizationTarget>(continuous_collection_segments_.begin(),
-                                        continuous_collection_segments_.end()),
+        std::vector<SegmentId>(continuous_collection_segments_.begin(),
+                               continuous_collection_segments_.end()),
         base::BindOnce(&TrainingDataCollectorImpl::ReportForSegmentsInfoList,
                        weak_ptr_factory_.GetWeakPtr(), absl::nullopt));
   }
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
index b1bfc95..6a7000e3 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
@@ -13,16 +13,16 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_base.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
+using proto::SegmentId;
+
 struct Config;
 class SegmentationResultPrefs;
 
@@ -88,12 +88,11 @@
   // Hash of histograms for immediate training data collection. When any
   // histogram hash contained in the map is recorded, a UKM message is reported
   // right away.
-  base::flat_map<uint64_t,
-                 base::flat_set<optimization_guide::proto::OptimizationTarget>>
+  base::flat_map<uint64_t, base::flat_set<proto::SegmentId>>
       immediate_collection_histograms_;
 
   // A list of segment IDs that needs to report metrics continuously.
-  std::set<OptimizationTarget> continuous_collection_segments_;
+  std::set<SegmentId> continuous_collection_segments_;
 
   base::WeakPtrFactory<TrainingDataCollectorImpl> weak_ptr_factory_{this};
 };
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
index 1e8ace2ab..9daecb3 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
@@ -30,6 +30,9 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace segmentation_platform {
+namespace {
+
 using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::NiceMock;
@@ -38,18 +41,15 @@
     ::ukm::builders::Segmentation_ModelExecution;
 
 constexpr auto kTestOptimizationTarget0 =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
 constexpr auto kTestOptimizationTarget1 =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
 constexpr char kHistogramName0[] = "histogram0";
 constexpr char kHistogramName1[] = "histogram1";
 constexpr char kSegmentationKey[] = "test_key";
 constexpr int64_t kModelVersion = 123;
 constexpr int kSample = 1;
 
-namespace segmentation_platform {
-namespace {
-
 class TrainingDataCollectorImplTest : public ::testing::Test {
  public:
   TrainingDataCollectorImplTest() = default;
@@ -61,6 +61,10 @@
     LocalStateHelper::GetInstance().Initialize(&prefs_);
     LocalStateHelper::GetInstance().SetPrefTime(
         kSegmentationLastCollectionTimePref, base::Time::Now());
+    // Set UKM allowed 30 days ago
+    LocalStateHelper::GetInstance().SetPrefTime(
+        kSegmentationUkmMostRecentAllowedTimeKey,
+        base::Time::Now() - base::Days(30));
     clock_.SetNow(base::Time::Now());
     test_recorder_.Purge();
 
@@ -82,13 +86,13 @@
     configs_.emplace_back(std::make_unique<Config>());
     configs_[0]->segmentation_key = kSegmentationKey;
     configs_[0]->segment_ids.push_back(
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
     configs_[0]->segment_ids.push_back(
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
 
     SegmentationResultPrefs result_prefs(&prefs_);
     SelectedSegment selected_segment(
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
     selected_segment.selection_time = base::Time::Now() - base::Days(1);
     result_prefs.SaveSegmentationResultToPref(kSegmentationKey,
                                               selected_segment);
@@ -125,9 +129,8 @@
     return segment_info;
   }
 
-  proto::SegmentInfo* CreateSegment(OptimizationTarget optimization_target) {
-    auto* segment_info =
-        test_segment_db()->FindOrCreateSegment(optimization_target);
+  proto::SegmentInfo* CreateSegment(SegmentId segment_id) {
+    auto* segment_info = test_segment_db()->FindOrCreateSegment(segment_id);
     auto* model_metadata = segment_info->mutable_model_metadata();
     model_metadata->set_time_unit(proto::TimeUnit::DAY);
     model_metadata->set_signal_storage_length(7);
@@ -341,7 +344,7 @@
       {kTestOptimizationTarget0, kModelVersion,
        SegmentationUkmHelper::FloatToInt64(1.f),
        SegmentationUkmHelper::FloatToInt64(0.6f),
-       OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+       SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
        base::Days(1).InSeconds(), SegmentationUkmHelper::FloatToInt64(2.f),
        SegmentationUkmHelper::FloatToInt64(3.f)});
 }
@@ -371,5 +374,20 @@
   ExpectUkmCount(1u);
 }
 
+// Tests that if UKM allowed timestamp is not set in local state, data
+// collection won't happen.
+TEST_F(TrainingDataCollectorImplTest, NoDataCollectionIfUkmAllowedPrefNotSet) {
+  ON_CALL(*feature_list_processor(), ProcessFeatureList(_, _, _, _, _, _))
+      .WillByDefault(RunOnceCallback<5>(true, std::vector<float>{1.f},
+                                        std::vector<float>{2.f, 3.f}));
+  LocalStateHelper::GetInstance().SetPrefTime(
+      kSegmentationUkmMostRecentAllowedTimeKey, base::Time());
+  CreateSegmentInfo();
+  Init();
+  collector()->ReportCollectedContinuousTrainingData();
+  task_environment()->RunUntilIdle();
+  ExpectUkmCount(0u);
+}
+
 }  // namespace
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/database/database_maintenance_impl.cc b/components/segmentation_platform/internal/database/database_maintenance_impl.cc
index aa35a1de..52b2dfbe 100644
--- a/components/segmentation_platform/internal/database/database_maintenance_impl.cc
+++ b/components/segmentation_platform/internal/database/database_maintenance_impl.cc
@@ -90,7 +90,7 @@
 };
 
 DatabaseMaintenanceImpl::DatabaseMaintenanceImpl(
-    const base::flat_set<OptimizationTarget>& segment_ids,
+    const base::flat_set<SegmentId>& segment_ids,
     base::Clock* clock,
     SegmentInfoDatabase* segment_info_database,
     SignalDatabase* signal_database,
@@ -106,8 +106,7 @@
 DatabaseMaintenanceImpl::~DatabaseMaintenanceImpl() = default;
 
 void DatabaseMaintenanceImpl::ExecuteMaintenanceTasks() {
-  std::vector<OptimizationTarget> segment_ids(segment_ids_.begin(),
-                                              segment_ids_.end());
+  std::vector<SegmentId> segment_ids(segment_ids_.begin(), segment_ids_.end());
   default_model_manager_->GetAllSegmentInfoFromBothModels(
       segment_ids, segment_info_database_,
       base::BindOnce(&DatabaseMaintenanceImpl::OnSegmentInfoCallback,
diff --git a/components/segmentation_platform/internal/database/database_maintenance_impl.h b/components/segmentation_platform/internal/database/database_maintenance_impl.h
index bb6d2f77..d8d67db5a 100644
--- a/components/segmentation_platform/internal/database/database_maintenance_impl.h
+++ b/components/segmentation_platform/internal/database/database_maintenance_impl.h
@@ -15,19 +15,19 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/database_maintenance.h"
 #include "components/segmentation_platform/internal/execution/default_model_manager.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace base {
 class Clock;
 class Time;
 }  // namespace base
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
+using proto::SegmentId;
+
 class DefaultModelManager;
 class SegmentInfoDatabase;
 class SignalDatabase;
@@ -40,13 +40,12 @@
   using SignalIdentifier = std::pair<uint64_t, proto::SignalType>;
   using CleanupItem = std::tuple<uint64_t, proto::SignalType, base::Time>;
 
-  explicit DatabaseMaintenanceImpl(
-      const base::flat_set<OptimizationTarget>& segment_ids,
-      base::Clock* clock,
-      SegmentInfoDatabase* segment_info_database,
-      SignalDatabase* signal_database,
-      SignalStorageConfig* signal_storage_config,
-      DefaultModelManager* default_model_manager);
+  explicit DatabaseMaintenanceImpl(const base::flat_set<SegmentId>& segment_ids,
+                                   base::Clock* clock,
+                                   SegmentInfoDatabase* segment_info_database,
+                                   SignalDatabase* signal_database,
+                                   SignalStorageConfig* signal_storage_config,
+                                   DefaultModelManager* default_model_manager);
   ~DatabaseMaintenanceImpl() override;
 
   // DatabaseMaintenance overrides.
@@ -88,7 +87,7 @@
   void CompactSamplesDone(base::OnceClosure next_action);
 
   // Input.
-  base::flat_set<OptimizationTarget> segment_ids_;
+  base::flat_set<SegmentId> segment_ids_;
   raw_ptr<base::Clock> clock_;
 
   // Databases.
diff --git a/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc b/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc
index 8c7bc74..ce73d34 100644
--- a/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc
+++ b/components/segmentation_platform/internal/database/database_maintenance_impl_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.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/mock_signal_storage_config.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
@@ -24,6 +23,7 @@
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
 #include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -31,9 +31,9 @@
 using ::testing::_;
 using ::testing::SetArgReferee;
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
+
+using SegmentId = proto::SegmentId;
 using SignalType = proto::SignalType;
 using Aggregation = proto::Aggregation;
 using SignalIdentifier = std::pair<uint64_t, SignalType>;
@@ -46,7 +46,7 @@
 std::string kTestSegmentationKey = "some_key";
 
 struct SignalData {
-  OptimizationTarget target;
+  SegmentId target;
   proto::SignalType signal_type;
   std::string name;
   uint64_t name_hash;
@@ -64,11 +64,11 @@
 class TestDefaultModelManager : public DefaultModelManager {
  public:
   TestDefaultModelManager()
-      : DefaultModelManager(nullptr, std::vector<OptimizationTarget>()) {}
+      : DefaultModelManager(nullptr, std::vector<SegmentId>()) {}
   ~TestDefaultModelManager() override = default;
 
   void GetAllSegmentInfoFromDefaultModel(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       MultipleSegmentInfoCallback callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback),
@@ -76,7 +76,7 @@
   }
 
   void GetAllSegmentInfoFromBothModels(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       SegmentInfoDatabase* segment_database,
       MultipleSegmentInfoCallback callback) override {
     segment_database->GetSegmentInfoForSegments(
@@ -107,9 +107,9 @@
     segment_info_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
     signal_database_ = std::make_unique<MockSignalDatabase>();
     signal_storage_config_ = std::make_unique<MockSignalStorageConfig>();
-    base::flat_set<OptimizationTarget> segment_ids = {
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
+    base::flat_set<SegmentId> segment_ids = {
+        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
     default_model_manager_ = std::make_unique<TestDefaultModelManager>();
     database_maintenance_ = std::make_unique<DatabaseMaintenanceImpl>(
         segment_ids, &clock_, segment_info_database_.get(),
@@ -181,13 +181,13 @@
 
 TEST_F(DatabaseMaintenanceImplTest, ExecuteMaintenanceTasks) {
   std::vector<SignalData> signal_datas = {
-      {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+      {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
        SignalType::HISTOGRAM_VALUE, "Foo", base::HashMetricName("Foo"), 44, 1,
        Aggregation::COUNT, clock_.Now() - base::Days(10), true},
-      {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+      {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
        SignalType::HISTOGRAM_ENUM, "Bar", base::HashMetricName("Bar"), 33, 1,
        Aggregation::COUNT, clock_.Now() - base::Days(5), true},
-      {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+      {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
        SignalType::USER_ACTION, "Failed", base::HashMetricName("Failed"), 22, 1,
        Aggregation::COUNT, clock_.Now() - base::Days(1), false},
   };
diff --git a/components/segmentation_platform/internal/database/segment_info_database.cc b/components/segmentation_platform/internal/database/segment_info_database.cc
index 12a3f46..3cad35a 100644
--- a/components/segmentation_platform/internal/database/segment_info_database.cc
+++ b/components/segmentation_platform/internal/database/segment_info_database.cc
@@ -12,7 +12,7 @@
 
 namespace {
 
-std::string ToString(OptimizationTarget segment_id) {
+std::string ToString(SegmentId segment_id) {
   return base::NumberToString(static_cast<int>(segment_id));
 }
 
@@ -53,10 +53,10 @@
 }
 
 void SegmentInfoDatabase::GetSegmentInfoForSegments(
-    const std::vector<OptimizationTarget>& segment_ids,
+    const std::vector<SegmentId>& segment_ids,
     MultipleSegmentInfoCallback callback) {
   std::vector<std::string> keys;
-  for (OptimizationTarget target : segment_ids)
+  for (SegmentId target : segment_ids)
     keys.emplace_back(ToString(target));
 
   database_->LoadEntriesWithFilter(
@@ -69,7 +69,7 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void SegmentInfoDatabase::GetSegmentInfo(OptimizationTarget segment_id,
+void SegmentInfoDatabase::GetSegmentInfo(SegmentId segment_id,
                                          SegmentInfoCallback callback) {
   database_->GetEntry(
       ToString(segment_id),
@@ -86,7 +86,7 @@
 }
 
 void SegmentInfoDatabase::UpdateSegment(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     absl::optional<proto::SegmentInfo> segment_info,
     SuccessCallback callback) {
   auto entries_to_save = std::make_unique<
@@ -104,7 +104,7 @@
 }
 
 void SegmentInfoDatabase::SaveSegmentResult(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     absl::optional<proto::PredictionResult> result,
     SuccessCallback callback) {
   GetSegmentInfo(
diff --git a/components/segmentation_platform/internal/database/segment_info_database.h b/components/segmentation_platform/internal/database/segment_info_database.h
index 2c4ab12..41dc38a 100644
--- a/components/segmentation_platform/internal/database/segment_info_database.h
+++ b/components/segmentation_platform/internal/database/segment_info_database.h
@@ -10,15 +10,15 @@
 
 #include "base/callback.h"
 #include "components/leveldb_proto/public/proto_database.h"
-#include "components/optimization_guide/proto/models.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/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 namespace proto {
 class SegmentInfo;
 class PredictionResult;
@@ -29,8 +29,7 @@
 class SegmentInfoDatabase {
  public:
   using SuccessCallback = base::OnceCallback<void(bool)>;
-  using SegmentInfoList =
-      std::vector<std::pair<OptimizationTarget, proto::SegmentInfo>>;
+  using SegmentInfoList = std::vector<std::pair<SegmentId, proto::SegmentInfo>>;
   using MultipleSegmentInfoCallback =
       base::OnceCallback<void(std::unique_ptr<SegmentInfoList>)>;
   using SegmentInfoCallback =
@@ -52,24 +51,24 @@
 
   // Called to get metadata for a given list of segments.
   virtual void GetSegmentInfoForSegments(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       MultipleSegmentInfoCallback callback);
 
   // Called to get the metadata for a given segment.
-  virtual void GetSegmentInfo(OptimizationTarget segment_id,
+  virtual void GetSegmentInfo(SegmentId segment_id,
                               SegmentInfoCallback callback);
 
   // Called to save or update metadata for a segment. The previous data is
   // overwritten. If |segment_info| is empty, the segment will be deleted.
   // TODO(shaktisahu): How does the client know if a segment is to be deleted?
-  virtual void UpdateSegment(OptimizationTarget segment_id,
+  virtual void UpdateSegment(SegmentId segment_id,
                              absl::optional<proto::SegmentInfo> segment_info,
                              SuccessCallback callback);
 
   // Called to write the model execution results for a given segment. It will
   // first read the currently stored result, and then overwrite it with
   // |result|. If |result| is null, the existing result will be deleted.
-  virtual void SaveSegmentResult(OptimizationTarget segment_id,
+  virtual void SaveSegmentResult(SegmentId segment_id,
                                  absl::optional<proto::PredictionResult> result,
                                  SuccessCallback callback);
 
diff --git a/components/segmentation_platform/internal/database/segment_info_database_unittest.cc b/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
index 9d492345..1dbe9e16 100644
--- a/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
+++ b/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
@@ -18,16 +18,15 @@
 namespace {
 
 // Test Ids.
-const OptimizationTarget kSegmentId =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
-const OptimizationTarget kSegmentId2 =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+const SegmentId kSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const SegmentId kSegmentId2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
 
-std::string ToString(OptimizationTarget segment_id) {
+std::string ToString(SegmentId segment_id) {
   return base::NumberToString(static_cast<int>(segment_id));
 }
 
-proto::SegmentInfo CreateSegment(OptimizationTarget segment_id,
+proto::SegmentInfo CreateSegment(SegmentId segment_id,
                                  absl::optional<int> result = absl::nullopt) {
   proto::SegmentInfo info;
   info.set_segment_id(segment_id);
@@ -71,14 +70,13 @@
     segment_db_.reset();
   }
 
-  void VerifyDb(std::vector<OptimizationTarget> expected_ids) {
+  void VerifyDb(std::vector<SegmentId> expected_ids) {
     EXPECT_EQ(expected_ids.size(), db_entries_.size());
     for (auto segment_id : expected_ids)
       EXPECT_TRUE(db_entries_.find(ToString(segment_id)) != db_entries_.end());
   }
 
-  void WriteResult(OptimizationTarget segment_id,
-                   absl::optional<float> result) {
+  void WriteResult(SegmentId segment_id, absl::optional<float> result) {
     proto::PredictionResult prediction_result;
     if (result.has_value())
       prediction_result.set_result(result.value());
@@ -92,8 +90,7 @@
     db_->UpdateCallback(true);
   }
 
-  void VerifyResult(OptimizationTarget segment_id,
-                    absl::optional<float> result) {
+  void VerifyResult(SegmentId segment_id, absl::optional<float> result) {
     segment_db_->GetSegmentInfo(
         segment_id, base::BindOnce(&SegmentInfoDatabaseTest::OnGetSegment,
                                    base::Unretained(this)));
diff --git a/components/segmentation_platform/internal/database/storage_service.cc b/components/segmentation_platform/internal/database/storage_service.cc
index e4618f8..90052cfa 100644
--- a/components/segmentation_platform/internal/database/storage_service.cc
+++ b/components/segmentation_platform/internal/database/storage_service.cc
@@ -31,8 +31,7 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     base::Clock* clock,
     UkmDataManager* ukm_data_manager,
-    base::flat_set<optimization_guide::proto::OptimizationTarget>
-        all_segment_ids,
+    base::flat_set<proto::SegmentId> all_segment_ids,
     ModelProviderFactory* model_provider_factory)
     : StorageService(
           db_provider->GetDB<proto::SegmentInfo>(
@@ -60,13 +59,12 @@
         signal_storage_config_db,
     base::Clock* clock,
     UkmDataManager* ukm_data_manager,
-    base::flat_set<optimization_guide::proto::OptimizationTarget>
-        all_segment_ids,
+    base::flat_set<proto::SegmentId> all_segment_ids,
     ModelProviderFactory* model_provider_factory)
     : default_model_manager_(std::make_unique<DefaultModelManager>(
           model_provider_factory,
-          std::vector<OptimizationTarget>(all_segment_ids.begin(),
-                                          all_segment_ids.end()))),
+          std::vector<SegmentId>(all_segment_ids.begin(),
+                                 all_segment_ids.end()))),
       segment_info_database_(
           std::make_unique<SegmentInfoDatabase>(std::move(segment_db))),
       signal_database_(
diff --git a/components/segmentation_platform/internal/database/storage_service.h b/components/segmentation_platform/internal/database/storage_service.h
index d5dbd1a1..258bc0dc 100644
--- a/components/segmentation_platform/internal/database/storage_service.h
+++ b/components/segmentation_platform/internal/database/storage_service.h
@@ -12,7 +12,7 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/raw_ptr.h"
 #include "components/leveldb_proto/public/proto_database.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
@@ -65,8 +65,7 @@
                  scoped_refptr<base::SequencedTaskRunner> task_runner,
                  base::Clock* clock,
                  UkmDataManager* ukm_data_manager,
-                 base::flat_set<optimization_guide::proto::OptimizationTarget>
-                     all_segment_ids,
+                 base::flat_set<proto::SegmentId> all_segment_ids,
                  ModelProviderFactory* model_provider_factory);
 
   // For tests:
@@ -79,8 +78,7 @@
           signal_storage_config_db,
       base::Clock* clock,
       UkmDataManager* ukm_data_manager,
-      base::flat_set<optimization_guide::proto::OptimizationTarget>
-          all_segment_ids,
+      base::flat_set<proto::SegmentId> all_segment_ids,
       ModelProviderFactory* model_provider_factory);
 
   // For tests:
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.cc b/components/segmentation_platform/internal/database/test_segment_info_database.cc
index 3fbd7c3..1457adb 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.cc
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.cc
@@ -8,11 +8,11 @@
 
 #include "base/containers/contains.h"
 #include "base/metrics/metrics_hashes.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/constants.h"
 #include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform::test {
@@ -33,7 +33,7 @@
 }
 
 void TestSegmentInfoDatabase::GetSegmentInfoForSegments(
-    const std::vector<OptimizationTarget>& segment_ids,
+    const std::vector<SegmentId>& segment_ids,
     MultipleSegmentInfoCallback callback) {
   auto result = std::make_unique<SegmentInfoDatabase::SegmentInfoList>();
   for (const auto& pair : segment_infos_) {
@@ -43,13 +43,13 @@
   std::move(callback).Run(std::move(result));
 }
 
-void TestSegmentInfoDatabase::GetSegmentInfo(OptimizationTarget segment_id,
+void TestSegmentInfoDatabase::GetSegmentInfo(SegmentId segment_id,
                                              SegmentInfoCallback callback) {
-  auto result = std::find_if(
-      segment_infos_.begin(), segment_infos_.end(),
-      [segment_id](std::pair<OptimizationTarget, proto::SegmentInfo> pair) {
-        return pair.first == segment_id;
-      });
+  auto result =
+      std::find_if(segment_infos_.begin(), segment_infos_.end(),
+                   [segment_id](std::pair<SegmentId, proto::SegmentInfo> pair) {
+                     return pair.first == segment_id;
+                   });
 
   std::move(callback).Run(result == segment_infos_.end()
                               ? absl::nullopt
@@ -57,7 +57,7 @@
 }
 
 void TestSegmentInfoDatabase::UpdateSegment(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     absl::optional<proto::SegmentInfo> segment_info,
     SuccessCallback callback) {
   if (segment_info.has_value()) {
@@ -67,8 +67,7 @@
     // Delete the segment.
     auto new_end = std::remove_if(
         segment_infos_.begin(), segment_infos_.end(),
-        [segment_id](
-            const std::pair<OptimizationTarget, proto::SegmentInfo>& pair) {
+        [segment_id](const std::pair<SegmentId, proto::SegmentInfo>& pair) {
           return pair.first == segment_id;
         });
     segment_infos_.erase(new_end, segment_infos_.end());
@@ -77,7 +76,7 @@
 }
 
 void TestSegmentInfoDatabase::SaveSegmentResult(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     absl::optional<proto::PredictionResult> result,
     SuccessCallback callback) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -92,7 +91,7 @@
 }
 
 void TestSegmentInfoDatabase::AddUserActionFeature(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const std::string& name,
     uint64_t bucket_count,
     uint64_t tensor_length,
@@ -111,7 +110,7 @@
 }
 
 void TestSegmentInfoDatabase::AddHistogramValueFeature(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const std::string& name,
     uint64_t bucket_count,
     uint64_t tensor_length,
@@ -130,7 +129,7 @@
 }
 
 void TestSegmentInfoDatabase::AddHistogramEnumFeature(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const std::string& name,
     uint64_t bucket_count,
     uint64_t tensor_length,
@@ -151,7 +150,7 @@
 }
 
 void TestSegmentInfoDatabase::AddSqlFeature(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const MetadataWriter::SqlFeature& feature) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
   MetadataWriter writer(info->mutable_model_metadata());
@@ -159,7 +158,7 @@
   writer.AddSqlFeatures(features, 1);
 }
 
-void TestSegmentInfoDatabase::AddPredictionResult(OptimizationTarget segment_id,
+void TestSegmentInfoDatabase::AddPredictionResult(SegmentId segment_id,
                                                   float score,
                                                   base::Time timestamp) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -170,7 +169,7 @@
 }
 
 void TestSegmentInfoDatabase::AddDiscreteMapping(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const float mappings[][2],
     int num_pairs,
     const std::string& discrete_mapping_key) {
@@ -186,7 +185,7 @@
   }
 }
 
-void TestSegmentInfoDatabase::SetBucketDuration(OptimizationTarget segment_id,
+void TestSegmentInfoDatabase::SetBucketDuration(SegmentId segment_id,
                                                 uint64_t bucket_duration,
                                                 proto::TimeUnit time_unit) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -195,7 +194,7 @@
 }
 
 proto::SegmentInfo* TestSegmentInfoDatabase::FindOrCreateSegment(
-    OptimizationTarget segment_id) {
+    SegmentId segment_id) {
   proto::SegmentInfo* info = nullptr;
   for (auto& pair : segment_infos_) {
     if (pair.first == segment_id) {
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.h b/components/segmentation_platform/internal/database/test_segment_info_database.h
index b50d9c41..3b6d388 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.h
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.h
@@ -27,53 +27,52 @@
   // SegmentInfoDatabase overrides.
   void Initialize(SuccessCallback callback) override;
   void GetAllSegmentInfo(MultipleSegmentInfoCallback callback) override;
-  void GetSegmentInfoForSegments(
-      const std::vector<OptimizationTarget>& segment_ids,
-      MultipleSegmentInfoCallback callback) override;
-  void GetSegmentInfo(OptimizationTarget segment_id,
+  void GetSegmentInfoForSegments(const std::vector<SegmentId>& segment_ids,
+                                 MultipleSegmentInfoCallback callback) override;
+  void GetSegmentInfo(SegmentId segment_id,
                       SegmentInfoCallback callback) override;
-  void UpdateSegment(OptimizationTarget segment_id,
+  void UpdateSegment(SegmentId segment_id,
                      absl::optional<proto::SegmentInfo> segment_info,
                      SuccessCallback callback) override;
-  void SaveSegmentResult(OptimizationTarget segment_id,
+  void SaveSegmentResult(SegmentId segment_id,
                          absl::optional<proto::PredictionResult> result,
                          SuccessCallback callback) override;
 
   // Test helper methods.
-  void AddUserActionFeature(OptimizationTarget segment_id,
+  void AddUserActionFeature(SegmentId segment_id,
                             const std::string& user_action,
                             uint64_t bucket_count,
                             uint64_t tensor_length,
                             proto::Aggregation aggregation);
-  void AddHistogramValueFeature(OptimizationTarget segment_id,
+  void AddHistogramValueFeature(SegmentId segment_id,
                                 const std::string& histogram,
                                 uint64_t bucket_count,
                                 uint64_t tensor_length,
                                 proto::Aggregation aggregation);
-  void AddHistogramEnumFeature(OptimizationTarget segment_id,
+  void AddHistogramEnumFeature(SegmentId segment_id,
                                const std::string& histogram_name,
                                uint64_t bucket_count,
                                uint64_t tensor_length,
                                proto::Aggregation aggregation,
                                const std::vector<int32_t>& accepted_enum_ids);
-  void AddSqlFeature(OptimizationTarget segment_id,
+  void AddSqlFeature(SegmentId segment_id,
                      const MetadataWriter::SqlFeature& feature);
-  void AddPredictionResult(OptimizationTarget segment_id,
+  void AddPredictionResult(SegmentId segment_id,
                            float score,
                            base::Time timestamp);
-  void AddDiscreteMapping(OptimizationTarget segment_id,
+  void AddDiscreteMapping(SegmentId segment_id,
                           const float mappings[][2],
                           int num_pairs,
                           const std::string& discrete_mapping_key);
-  void SetBucketDuration(OptimizationTarget segment_id,
+  void SetBucketDuration(SegmentId segment_id,
                          uint64_t bucket_duration,
                          proto::TimeUnit time_unit);
 
   // Finds a segment with given |segment_id|. Creates one if it doesn't exist.
-  proto::SegmentInfo* FindOrCreateSegment(OptimizationTarget segment_id);
+  proto::SegmentInfo* FindOrCreateSegment(SegmentId segment_id);
 
  private:
-  std::vector<std::pair<OptimizationTarget, proto::SegmentInfo>> segment_infos_;
+  std::vector<std::pair<SegmentId, proto::SegmentInfo>> segment_infos_;
 };
 
 }  // namespace segmentation_platform::test
diff --git a/components/segmentation_platform/internal/execution/default_model_manager.cc b/components/segmentation_platform/internal/execution/default_model_manager.cc
index c12c96bd..460668c 100644
--- a/components/segmentation_platform/internal/execution/default_model_manager.cc
+++ b/components/segmentation_platform/internal/execution/default_model_manager.cc
@@ -14,9 +14,9 @@
 
 DefaultModelManager::DefaultModelManager(
     ModelProviderFactory* model_provider_factory,
-    const std::vector<OptimizationTarget>& segment_ids)
+    const std::vector<SegmentId>& segment_ids)
     : model_provider_factory_(model_provider_factory) {
-  for (OptimizationTarget segment_id : segment_ids) {
+  for (SegmentId segment_id : segment_ids) {
     std::unique_ptr<ModelProvider> provider =
         model_provider_factory->CreateDefaultProvider(segment_id);
     if (!provider)
@@ -28,8 +28,7 @@
 
 DefaultModelManager::~DefaultModelManager() = default;
 
-ModelProvider* DefaultModelManager::GetDefaultProvider(
-    OptimizationTarget segment_id) {
+ModelProvider* DefaultModelManager::GetDefaultProvider(SegmentId segment_id) {
   auto it = default_model_providers_.find(segment_id);
   if (it != default_model_providers_.end())
     return it->second.get();
@@ -37,21 +36,20 @@
 }
 
 void DefaultModelManager::GetAllSegmentInfoFromDefaultModel(
-    const std::vector<OptimizationTarget>& segment_ids,
+    const std::vector<SegmentId>& segment_ids,
     MultipleSegmentInfoCallback callback) {
   auto result = std::make_unique<SegmentInfoList>();
-  std::deque<OptimizationTarget> remaining_segment_ids(segment_ids.begin(),
-                                                       segment_ids.end());
+  std::deque<SegmentId> remaining_segment_ids(segment_ids.begin(),
+                                              segment_ids.end());
   GetNextSegmentInfoFromDefaultModel(
       std::move(result), std::move(remaining_segment_ids), std::move(callback));
 }
 
 void DefaultModelManager::GetNextSegmentInfoFromDefaultModel(
     std::unique_ptr<SegmentInfoList> result,
-    std::deque<OptimizationTarget> remaining_segment_ids,
+    std::deque<SegmentId> remaining_segment_ids,
     MultipleSegmentInfoCallback callback) {
-  OptimizationTarget segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+  SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
   ModelProvider* default_provider = nullptr;
 
   // Find the next available default provider.
@@ -78,9 +76,9 @@
 
 void DefaultModelManager::OnFetchDefaultModel(
     std::unique_ptr<SegmentInfoList> result,
-    std::deque<OptimizationTarget> remaining_segment_ids,
+    std::deque<SegmentId> remaining_segment_ids,
     MultipleSegmentInfoCallback callback,
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     proto::SegmentationModelMetadata metadata,
     int64_t model_version) {
   auto info = std::make_unique<SegmentInfoWrapper>();
@@ -95,7 +93,7 @@
 }
 
 void DefaultModelManager::GetAllSegmentInfoFromBothModels(
-    const std::vector<OptimizationTarget>& segment_ids,
+    const std::vector<SegmentId>& segment_ids,
     SegmentInfoDatabase* segment_database,
     MultipleSegmentInfoCallback callback) {
   segment_database->GetSegmentInfoForSegments(
@@ -106,7 +104,7 @@
 }
 
 void DefaultModelManager::OnGetAllSegmentInfoFromDatabase(
-    const std::vector<OptimizationTarget>& segment_ids,
+    const std::vector<SegmentId>& segment_ids,
     MultipleSegmentInfoCallback callback,
     std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segment_infos) {
   GetAllSegmentInfoFromDefaultModel(
@@ -137,7 +135,7 @@
 }
 
 void DefaultModelManager::SetDefaultProvidersForTesting(
-    std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>&& providers) {
+    std::map<SegmentId, std::unique_ptr<ModelProvider>>&& providers) {
   default_model_providers_ = std::move(providers);
 }
 
diff --git a/components/segmentation_platform/internal/execution/default_model_manager.h b/components/segmentation_platform/internal/execution/default_model_manager.h
index 911a31cc..e0ca96b5 100644
--- a/components/segmentation_platform/internal/execution/default_model_manager.h
+++ b/components/segmentation_platform/internal/execution/default_model_manager.h
@@ -20,9 +20,9 @@
 #include "components/segmentation_platform/public/model_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
+using proto::SegmentId;
+
 class SegmentInfoDatabase;
 
 // DefaultModelManager provides support to query all default models available.
@@ -31,7 +31,7 @@
 class DefaultModelManager {
  public:
   DefaultModelManager(ModelProviderFactory* model_provider_factory,
-                      const std::vector<OptimizationTarget>& segment_ids);
+                      const std::vector<SegmentId>& segment_ids);
   virtual ~DefaultModelManager();
 
   // Disallow copy/assign.
@@ -60,37 +60,37 @@
   // default model for a given set of segment IDs. The result can contain
   // the same segment ID multiple times.
   virtual void GetAllSegmentInfoFromBothModels(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       SegmentInfoDatabase* segment_database,
       MultipleSegmentInfoCallback callback);
 
   // Called to get the segment info from the default model for a given set of
   // segment IDs.
   virtual void GetAllSegmentInfoFromDefaultModel(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       MultipleSegmentInfoCallback callback);
 
   // Returns the default provider or `nulllptr` when unavailable.
-  ModelProvider* GetDefaultProvider(OptimizationTarget segment_id);
+  ModelProvider* GetDefaultProvider(SegmentId segment_id);
 
   void SetDefaultProvidersForTesting(
-      std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>&& providers);
+      std::map<SegmentId, std::unique_ptr<ModelProvider>>&& providers);
 
  private:
   void GetNextSegmentInfoFromDefaultModel(
       std::unique_ptr<SegmentInfoList> result,
-      std::deque<OptimizationTarget> remaining_segment_ids,
+      std::deque<SegmentId> remaining_segment_ids,
       MultipleSegmentInfoCallback callback);
 
   void OnFetchDefaultModel(std::unique_ptr<SegmentInfoList> result,
-                           std::deque<OptimizationTarget> remaining_segment_ids,
+                           std::deque<SegmentId> remaining_segment_ids,
                            MultipleSegmentInfoCallback callback,
-                           OptimizationTarget segment_id,
+                           SegmentId segment_id,
                            proto::SegmentationModelMetadata metadata,
                            int64_t model_version);
 
   void OnGetAllSegmentInfoFromDatabase(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       MultipleSegmentInfoCallback callback,
       std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segment_infos);
 
@@ -101,8 +101,7 @@
       SegmentInfoList segment_infos_from_default_model);
 
   // Default model providers.
-  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>>
-      default_model_providers_;
+  std::map<SegmentId, std::unique_ptr<ModelProvider>> default_model_providers_;
   const raw_ptr<ModelProviderFactory> model_provider_factory_;
 
   base::WeakPtrFactory<DefaultModelManager> weak_ptr_factory_{this};
diff --git a/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc b/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc
index c7979cc4..32abfcc 100644
--- a/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc
+++ b/components/segmentation_platform/internal/execution/default_model_manager_unittest.cc
@@ -18,18 +18,18 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 using base::test::RunOnceCallback;
-using optimization_guide::proto::OptimizationTarget;
 using testing::_;
 
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 class DefaultModelManagerTest : public testing::Test {
  public:
   DefaultModelManagerTest() : model_provider_factory_(&model_provider_data_) {}
   ~DefaultModelManagerTest() override = default;
 
-  MockModelProvider& FindHandler(
-      optimization_guide::proto::OptimizationTarget segment_id) {
+  MockModelProvider& FindHandler(proto::SegmentId segment_id) {
     return *(*model_provider_data_.default_model_providers.find(segment_id))
                 .second;
   }
@@ -52,14 +52,11 @@
 };
 
 TEST_F(DefaultModelManagerTest, BasicTest) {
-  const auto segment_1 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
-  const auto segment_2 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
-  const auto segment_3 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
+  const auto segment_1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  const auto segment_2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  const auto segment_3 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
   const auto segment_4 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES;
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES;
 
   // Set some model versions.
   const int model_version_db = 4;
diff --git a/components/segmentation_platform/internal/execution/mock_model_provider.cc b/components/segmentation_platform/internal/execution/mock_model_provider.cc
index 7c16a16..d9915c05 100644
--- a/components/segmentation_platform/internal/execution/mock_model_provider.cc
+++ b/components/segmentation_platform/internal/execution/mock_model_provider.cc
@@ -18,7 +18,7 @@
 
 // Stores the client callbacks to |data|.
 void StoreClientCallback(
-    optimization_guide::proto::OptimizationTarget segment_id,
+    proto::SegmentId segment_id,
     TestModelProviderFactory::Data* data,
     const ModelProvider::ModelUpdatedCallback& model_updated_callback) {
   data->model_providers_callbacks.emplace(
@@ -28,7 +28,7 @@
 }  // namespace
 
 MockModelProvider::MockModelProvider(
-    optimization_guide::proto::OptimizationTarget segment_id,
+    proto::SegmentId segment_id,
     base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)>
         get_client_callback)
     : ModelProvider(segment_id), get_client_callback_(get_client_callback) {
@@ -44,7 +44,7 @@
 TestModelProviderFactory::Data::~Data() = default;
 
 std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateProvider(
-    optimization_guide::proto::OptimizationTarget segment_id) {
+    proto::SegmentId 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()));
@@ -52,7 +52,7 @@
 }
 
 std::unique_ptr<ModelProvider> TestModelProviderFactory::CreateDefaultProvider(
-    optimization_guide::proto::OptimizationTarget segment_id) {
+    proto::SegmentId segment_id) {
   if (!base::Contains(data_->segments_supporting_default_model, segment_id))
     return nullptr;
 
diff --git a/components/segmentation_platform/internal/execution/mock_model_provider.h b/components/segmentation_platform/internal/execution/mock_model_provider.h
index a1c679d4..b8dc7619 100644
--- a/components/segmentation_platform/internal/execution/mock_model_provider.h
+++ b/components/segmentation_platform/internal/execution/mock_model_provider.h
@@ -14,15 +14,15 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 // Mock model provider for testing, to be used with TestModelProviderFactory.
 class MockModelProvider : public ModelProvider {
  public:
   MockModelProvider(
-      optimization_guide::proto::OptimizationTarget segment_id,
+      proto::SegmentId segment_id,
       base::RepeatingCallback<void(const ModelProvider::ModelUpdatedCallback&)>
           get_client_callback);
   ~MockModelProvider() override;
@@ -56,21 +56,18 @@
 
     // 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;
+    std::map<proto::SegmentId, MockModelProvider*> model_providers;
 
     // Map of targets to default model providers, added when provider is
     // created. The list is not cleared when providers are destroyed.
-    std::map<optimization_guide::proto::OptimizationTarget, MockModelProvider*>
-        default_model_providers;
+    std::map<proto::SegmentId, MockModelProvider*> default_model_providers;
 
     // Map from target to updated callback, recorded when InitAndFetchModel()
     // was called on any provider.
-    std::map<optimization_guide::proto::OptimizationTarget,
-             ModelProvider::ModelUpdatedCallback>
+    std::map<proto::SegmentId, ModelProvider::ModelUpdatedCallback>
         model_providers_callbacks;
 
-    std::vector<OptimizationTarget> segments_supporting_default_model;
+    std::vector<SegmentId> segments_supporting_default_model;
   };
 
   // Records requests to `data`. `data` is not owned, and the caller must ensure
@@ -81,10 +78,10 @@
   // 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;
+      proto::SegmentId segment_id) override;
 
   std::unique_ptr<ModelProvider> CreateDefaultProvider(
-      optimization_guide::proto::OptimizationTarget) override;
+      proto::SegmentId) override;
 
  private:
   raw_ptr<Data> data_;
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager.h b/components/segmentation_platform/internal/execution/model_execution_manager.h
index db967a4..3e87570 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager.h
+++ b/components/segmentation_platform/internal/execution/model_execution_manager.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_EXECUTION_MODEL_EXECUTION_MANAGER_H_
 
 #include "base/callback_forward.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 namespace proto {
@@ -31,8 +31,7 @@
   using SegmentationModelUpdatedCallback =
       base::RepeatingCallback<void(proto::SegmentInfo)>;
 
-  virtual ModelProvider* GetProvider(
-      optimization_guide::proto::OptimizationTarget segment_id) = 0;
+  virtual ModelProvider* GetProvider(proto::SegmentId segment_id) = 0;
 
  protected:
   ModelExecutionManager() = default;
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 0c58d556..66ee95f 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
@@ -16,24 +16,23 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "base/trace_event/typed_macros.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/metadata/metadata_utils.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/stats.h"
 #include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace optimization_guide {
 class OptimizationGuideModelProvider;
-using proto::OptimizationTarget;
 }  // namespace optimization_guide
 
 namespace segmentation_platform {
 
 ModelExecutionManagerImpl::ModelExecutionManagerImpl(
-    const base::flat_set<OptimizationTarget>& segment_ids,
+    const base::flat_set<SegmentId>& segment_ids,
     ModelProviderFactory* model_provider_factory,
     base::Clock* clock,
     SegmentInfoDatabase* segment_database,
@@ -41,7 +40,7 @@
     : clock_(clock),
       segment_database_(segment_database),
       model_updated_callback_(model_updated_callback) {
-  for (OptimizationTarget segment_id : segment_ids) {
+  for (SegmentId segment_id : segment_ids) {
     std::unique_ptr<ModelProvider> provider =
         model_provider_factory->CreateProvider(segment_id);
     provider->InitAndFetchModel(base::BindRepeating(
@@ -54,21 +53,20 @@
 ModelExecutionManagerImpl::~ModelExecutionManagerImpl() = default;
 
 ModelProvider* ModelExecutionManagerImpl::GetProvider(
-    optimization_guide::proto::OptimizationTarget segment_id) {
+    proto::SegmentId segment_id) {
   auto it = model_providers_.find(segment_id);
   DCHECK(it != model_providers_.end());
   return it->second.get();
 }
 
 void ModelExecutionManagerImpl::OnSegmentationModelUpdated(
-    optimization_guide::proto::OptimizationTarget segment_id,
+    proto::SegmentId segment_id,
     proto::SegmentationModelMetadata metadata,
     int64_t model_version) {
   TRACE_EVENT("segmentation_platform",
               "ModelExecutionManagerImpl::OnSegmentationModelUpdated");
   stats::RecordModelDeliveryReceived(segment_id);
-  if (segment_id == optimization_guide::proto::OptimizationTarget::
-                        OPTIMIZATION_TARGET_UNKNOWN) {
+  if (segment_id == proto::SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     return;
   }
 
@@ -91,7 +89,7 @@
 }
 
 void ModelExecutionManagerImpl::OnSegmentInfoFetchedForModelUpdate(
-    optimization_guide::proto::OptimizationTarget segment_id,
+    proto::SegmentId segment_id,
     proto::SegmentationModelMetadata metadata,
     int64_t model_version,
     absl::optional<proto::SegmentInfo> old_segment_info) {
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 8969651..e3445835 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.h
@@ -13,9 +13,9 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
@@ -38,7 +38,7 @@
 class ModelExecutionManagerImpl : public ModelExecutionManager {
  public:
   ModelExecutionManagerImpl(
-      const base::flat_set<OptimizationTarget>& segment_ids,
+      const base::flat_set<SegmentId>& segment_ids,
       ModelProviderFactory* model_provider_factory,
       base::Clock* clock,
       SegmentInfoDatabase* segment_database,
@@ -51,8 +51,7 @@
       delete;
 
   // ModelExecutionManager override:
-  ModelProvider* GetProvider(
-      optimization_guide::proto::OptimizationTarget segment_id) override;
+  ModelProvider* GetProvider(proto::SegmentId segment_id) override;
 
  private:
   friend class SegmentationPlatformServiceImplTest;
@@ -61,10 +60,9 @@
   // Callback for whenever a SegmentationModelHandler is informed that the
   // underlying ML model file has been updated. If there is an available
   // model, this will be called at least once per session.
-  void OnSegmentationModelUpdated(
-      optimization_guide::proto::OptimizationTarget segment_id,
-      proto::SegmentationModelMetadata metadata,
-      int64_t model_version);
+  void OnSegmentationModelUpdated(proto::SegmentId segment_id,
+                                  proto::SegmentationModelMetadata metadata,
+                                  int64_t model_version);
 
   // Callback after fetching the current SegmentInfo from the
   // SegmentInfoDatabase. This is part of the flow for informing the
@@ -72,7 +70,7 @@
   // Merges the PredictionResult from the previously stored SegmentInfo with
   // the newly updated one, and stores the new version in the DB.
   void OnSegmentInfoFetchedForModelUpdate(
-      optimization_guide::proto::OptimizationTarget segment_id,
+      proto::SegmentId segment_id,
       proto::SegmentationModelMetadata metadata,
       int64_t model_version,
       absl::optional<proto::SegmentInfo> segment_info);
@@ -83,7 +81,7 @@
                                   bool success);
 
   // All the relevant handlers for each of the segments.
-  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> model_providers_;
+  std::map<SegmentId, 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 882d042..b25c150 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,7 +18,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.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"
@@ -31,6 +30,7 @@
 #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 "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -57,22 +57,22 @@
               (override));
   MOCK_METHOD(void,
               GetSegmentInfoForSegments,
-              (const std::vector<OptimizationTarget>& segment_ids,
+              (const std::vector<SegmentId>& segment_ids,
                MultipleSegmentInfoCallback callback),
               (override));
   MOCK_METHOD(void,
               GetSegmentInfo,
-              (OptimizationTarget segment_id, SegmentInfoCallback callback),
+              (SegmentId segment_id, SegmentInfoCallback callback),
               (override));
   MOCK_METHOD(void,
               UpdateSegment,
-              (OptimizationTarget segment_id,
+              (SegmentId segment_id,
                absl::optional<proto::SegmentInfo> segment_info,
                SuccessCallback callback),
               (override));
   MOCK_METHOD(void,
               SaveSegmentResult,
-              (OptimizationTarget segment_id,
+              (SegmentId segment_id,
                absl::optional<proto::PredictionResult> result,
                SuccessCallback callback),
               (override));
@@ -100,7 +100,7 @@
   }
 
   void CreateModelExecutionManager(
-      std::vector<OptimizationTarget> segment_ids,
+      std::vector<SegmentId> segment_ids,
       const ModelExecutionManager::SegmentationModelUpdatedCallback& callback) {
     model_execution_manager_ = std::make_unique<ModelExecutionManagerImpl>(
         segment_ids, &model_provider_factory_, &clock_, segment_database_.get(),
@@ -109,8 +109,7 @@
 
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
-  MockModelProvider& FindHandler(
-      optimization_guide::proto::OptimizationTarget segment_id) {
+  MockModelProvider& FindHandler(proto::SegmentId segment_id) {
     return *(*model_provider_data_.model_providers.find(segment_id)).second;
   }
 
@@ -137,8 +136,7 @@
   // Construct the ModelExecutionManager.
   base::MockCallback<ModelExecutionManager::SegmentationModelUpdatedCallback>
       callback;
-  auto segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  auto segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   CreateModelExecutionManager({segment_id}, callback.Get());
 
   // Create invalid metadata, which should be ignored.
@@ -157,8 +155,7 @@
 TEST_F(ModelExecutionManagerTest, OnSegmentationModelUpdatedNoOldMetadata) {
   base::MockCallback<ModelExecutionManager::SegmentationModelUpdatedCallback>
       callback;
-  auto segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  auto segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   CreateModelExecutionManager({segment_id}, callback.Get());
 
   proto::SegmentInfo segment_info;
@@ -195,8 +192,7 @@
        OnSegmentationModelUpdatedWithPreviousMetadataAndPredictionResult) {
   base::MockCallback<ModelExecutionManager::SegmentationModelUpdatedCallback>
       callback;
-  auto segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  auto segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   CreateModelExecutionManager({segment_id}, callback.Get());
 
   // Fill in old data in the SegmentInfo database.
diff --git a/components/segmentation_platform/internal/execution/model_executor_impl.cc b/components/segmentation_platform/internal/execution/model_executor_impl.cc
index bbb6c13..b752e72 100644
--- a/components/segmentation_platform/internal/execution/model_executor_impl.cc
+++ b/components/segmentation_platform/internal/execution/model_executor_impl.cc
@@ -9,19 +9,19 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "base/trace_event/typed_macros.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/execution/execution_request.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.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 "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/perfetto/include/perfetto/tracing/track.h"
 
 namespace segmentation_platform {
 namespace {
-using optimization_guide::proto::OptimizationTarget;
 using processing::FeatureListQueryProcessor;
-}
+using proto::SegmentId;
+}  // namespace
 
 struct ModelExecutorImpl::ModelExecutionTraceEvent {
   ModelExecutionTraceEvent(const char* event_name,
@@ -55,7 +55,7 @@
   // https://crbug.com/1021571.
   std::unique_ptr<ModelExecutionTraceEvent> trace_event;
 
-  OptimizationTarget segment_id;
+  SegmentId segment_id;
   int64_t model_version = 0;
   raw_ptr<ModelProvider> model_provider = nullptr;
   bool record_metrics_for_default = false;
@@ -90,7 +90,7 @@
 void ModelExecutorImpl::ExecuteModel(
     std::unique_ptr<ExecutionRequest> request) {
   const proto::SegmentInfo& segment_info = *request->segment_info;
-  OptimizationTarget segment_id = segment_info.segment_id();
+  SegmentId segment_id = segment_info.segment_id();
 
   // Create an ExecutionState that will stay with this request until it has been
   // fully processed.
@@ -157,9 +157,7 @@
     for (unsigned i = 0; i < state->input_tensor.size(); ++i)
       log_input << " feature " << i << ": " << state->input_tensor[i];
     VLOG(1) << "Segmentation model input: " << log_input.str()
-            << " for segment "
-            << optimization_guide::proto::OptimizationTarget_Name(
-                   state->segment_id);
+            << " for segment " << proto::SegmentId_Name(state->segment_id);
   }
   const std::vector<float>& const_input_tensor = std::move(state->input_tensor);
   stats::RecordModelExecutionZeroValuePercent(state->segment_id,
@@ -182,8 +180,7 @@
       clock_->Now() - state->model_execution_start_time);
   if (result.has_value()) {
     VLOG(1) << "Segmentation model result: " << *result << " for segment "
-            << optimization_guide::proto::OptimizationTarget_Name(
-                   state->segment_id);
+            << proto::SegmentId_Name(state->segment_id);
     stats::RecordModelExecutionResult(state->segment_id, result.value());
     if (state->model_version && SegmentationUkmHelper::AllowedToUploadData(
                                     state->signal_storage_length, clock_)) {
@@ -195,8 +192,7 @@
                               ModelExecutionStatus::kSuccess);
   } else {
     VLOG(1) << "Segmentation model returned no result for segment "
-            << optimization_guide::proto::OptimizationTarget_Name(
-                   state->segment_id);
+            << proto::SegmentId_Name(state->segment_id);
     RunModelExecutionCallback(std::move(state), 0,
                               ModelExecutionStatus::kExecutionError);
   }
diff --git a/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
index 450963b9..a5ce702 100644
--- a/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.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"
@@ -32,6 +31,7 @@
 #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 "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,8 +45,8 @@
 
 namespace segmentation_platform {
 
-const OptimizationTarget kSegmentId =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const SegmentId kSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
 
 class ModelExecutorTest : public testing::Test {
  public:
@@ -150,7 +150,7 @@
 
   // Initialize with required metadata.
   test::TestSegmentInfoDatabase metadata_writer;
-  const OptimizationTarget segment_id = kSegmentId;
+  const SegmentId segment_id = kSegmentId;
   metadata_writer.SetBucketDuration(segment_id, 3, proto::TimeUnit::HOUR);
   std::string user_action_name = "some_user_action";
   metadata_writer.AddUserActionFeature(segment_id, user_action_name, 3, 3,
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
index 3fd50ee..44205cf 100644
--- a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.cc
@@ -12,6 +12,7 @@
 #include "components/optimization_guide/proto/models.pb.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/segment_id_convertor.h"
 #include "components/segmentation_platform/internal/stats.h"
 
 namespace segmentation_platform {
@@ -20,7 +21,7 @@
     OptimizationGuideSegmentationModelHandler(
         optimization_guide::OptimizationGuideModelProvider* model_provider,
         scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-        optimization_guide::proto::OptimizationTarget optimization_target,
+        optimization_guide::proto::OptimizationTarget segment_id,
         const ModelUpdatedCallback& model_updated_callback,
         absl::optional<optimization_guide::proto::Any>&& model_metadata)
     : optimization_guide::ModelHandler<float, const std::vector<float>&>(
@@ -28,11 +29,11 @@
           background_task_runner,
           std::make_unique<SegmentationModelExecutor>(),
           /*model_inference_timeout=*/absl::nullopt,
-          optimization_target,
+          segment_id,
           model_metadata),
       model_updated_callback_(model_updated_callback) {
   stats::RecordModelAvailability(
-      optimization_target,
+      OptimizationTargetToSegmentId(segment_id),
       stats::SegmentationModelAvailability::kModelHandlerCreated);
 }
 
@@ -40,12 +41,11 @@
     ~OptimizationGuideSegmentationModelHandler() = default;
 
 void OptimizationGuideSegmentationModelHandler::OnModelUpdated(
-    optimization_guide::proto::OptimizationTarget optimization_target,
+    optimization_guide::proto::OptimizationTarget segment_id,
     const optimization_guide::ModelInfo& model_info) {
   // First invoke parent to update internal status.
   optimization_guide::ModelHandler<
-      float, const std::vector<float>&>::OnModelUpdated(optimization_target,
-                                                        model_info);
+      float, const std::vector<float>&>::OnModelUpdated(segment_id, model_info);
   // The parent class should always set the model availability to true after
   // having received an updated model.
   DCHECK(ModelAvailable());
@@ -55,22 +55,23 @@
   absl::optional<proto::SegmentationModelMetadata> segmentation_model_metadata =
       ParsedSupportedFeaturesForLoadedModel<proto::SegmentationModelMetadata>();
   stats::RecordModelDeliveryHasMetadata(
-      optimization_target, segmentation_model_metadata.has_value());
+      OptimizationTargetToSegmentId(segment_id),
+      segmentation_model_metadata.has_value());
   if (!segmentation_model_metadata.has_value()) {
     // This is not expected to happen, since the optimization guide server is
     // expected to pass this along. Either something failed horribly on the way,
     // we failed to read the metadata, or the server side configuration is
     // wrong.
     stats::RecordModelAvailability(
-        optimization_target,
+        OptimizationTargetToSegmentId(segment_id),
         stats::SegmentationModelAvailability::kMetadataInvalid);
     return;
   }
   stats::RecordModelAvailability(
-      optimization_target,
+      OptimizationTargetToSegmentId(segment_id),
       stats::SegmentationModelAvailability::kModelAvailable);
 
-  model_updated_callback_.Run(optimization_target,
+  model_updated_callback_.Run(OptimizationTargetToSegmentId(segment_id),
                               std::move(*segmentation_model_metadata),
                               model_info.GetVersion());
 }
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
index 4eb60ff..a619f53 100644
--- a/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
+++ b/components/segmentation_platform/internal/execution/optimization_guide/optimization_guide_segmentation_model_handler.h
@@ -10,6 +10,7 @@
 
 #include "components/optimization_guide/core/model_handler.h"
 #include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace optimization_guide {
 class OptimizationGuideModelProvider;
@@ -29,15 +30,13 @@
     : public optimization_guide::ModelHandler<float,
                                               const std::vector<float>&> {
  public:
-  using ModelUpdatedCallback = base::RepeatingCallback<void(
-      optimization_guide::proto::OptimizationTarget,
-      proto::SegmentationModelMetadata,
-      int64_t)>;
+  using ModelUpdatedCallback = base::RepeatingCallback<
+      void(proto::SegmentId, proto::SegmentationModelMetadata, int64_t)>;
 
   explicit OptimizationGuideSegmentationModelHandler(
       optimization_guide::OptimizationGuideModelProvider* model_provider,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      optimization_guide::proto::OptimizationTarget optimization_target,
+      optimization_guide::proto::OptimizationTarget segment_id,
       const ModelUpdatedCallback& model_updated_callback,
       absl::optional<optimization_guide::proto::Any>&& model_metadata);
 
@@ -50,9 +49,8 @@
       const OptimizationGuideSegmentationModelHandler&) = delete;
 
   // optimization_guide::ModelHandler overrides.
-  void OnModelUpdated(
-      optimization_guide::proto::OptimizationTarget optimization_target,
-      const optimization_guide::ModelInfo& model_info) override;
+  void OnModelUpdated(optimization_guide::proto::OptimizationTarget segment_id,
+                      const optimization_guide::ModelInfo& model_info) override;
 
  private:
   // Callback to invoke whenever the model file has been updated. If there is
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
index be5e59da..512614b 100644
--- 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
@@ -10,11 +10,12 @@
 #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/segment_id_convertor.h"
 #include "components/segmentation_platform/internal/stats.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 
@@ -43,8 +44,8 @@
     OptimizationGuideSegmentationModelProvider(
         optimization_guide::OptimizationGuideModelProvider* model_provider,
         scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-        optimization_guide::proto::OptimizationTarget optimization_target)
-    : ModelProvider(optimization_target),
+        proto::SegmentId segment_id)
+    : ModelProvider(segment_id),
       model_provider_(model_provider),
       background_task_runner_(background_task_runner) {}
 
@@ -55,8 +56,9 @@
     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());
+      model_provider_, background_task_runner_,
+      SegmentIdToOptimizationTarget(segment_id_), model_updated_callback,
+      GetModelFetchConfig());
 }
 
 void OptimizationGuideSegmentationModelProvider::ExecuteModelWithInput(
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
index 2e7bdee..c1bbe2a 100644
--- 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
@@ -9,8 +9,8 @@
 #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"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace optimization_guide {
 class OptimizationGuideSegmentationModelProvider;
@@ -27,7 +27,7 @@
   OptimizationGuideSegmentationModelProvider(
       optimization_guide::OptimizationGuideModelProvider* model_provider,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner,
-      optimization_guide::proto::OptimizationTarget optimization_target);
+      proto::SegmentId segment_id);
 
   ~OptimizationGuideSegmentationModelProvider() override;
 
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
index 5586081..13bec7e 100644
--- 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
@@ -25,8 +25,7 @@
     registered_model_metadata_.insert_or_assign(target, model_metadata);
   }
 
-  bool DidRegisterForTarget(
-      optimization_guide::proto::OptimizationTarget target) const {
+  bool DidRegisterForTarget(proto::SegmentId target) const {
     auto it = registered_model_metadata_.find(target);
     if (it == registered_model_metadata_.end())
       return false;
@@ -67,7 +66,7 @@
   }
 
   std::unique_ptr<OptimizationGuideSegmentationModelProvider>
-  CreateModelProvider(optimization_guide::proto::OptimizationTarget target) {
+  CreateModelProvider(proto::SegmentId target) {
     return std::make_unique<OptimizationGuideSegmentationModelProvider>(
         model_observer_tracker_.get(), task_runner_, target);
   }
@@ -81,46 +80,41 @@
 
 TEST_F(OptimizationGuideSegmentationModelProviderTest, InitAndFetchModel) {
   std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
-      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
-                              OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+      CreateModelProvider(
+          proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
 
   // Not initialized yet.
   EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+      proto::SegmentId::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));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
 
   // Different target does not register yet.
   EXPECT_FALSE(model_observer_tracker_->DidRegisterForTarget(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
 
   // Initialize voice provider.
   std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider2 =
-      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
-                              OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+      CreateModelProvider(
+          proto::SegmentId::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));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
 
   EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
 }
 
 TEST_F(OptimizationGuideSegmentationModelProviderTest,
        ExecuteModelWithoutFetch) {
   std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
-      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
-                              OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+      CreateModelProvider(
+          proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
 
   base::RunLoop run_loop;
   std::vector<float> input = {4, 5};
@@ -137,12 +131,11 @@
 
 TEST_F(OptimizationGuideSegmentationModelProviderTest, ExecuteModelWithFetch) {
   std::unique_ptr<OptimizationGuideSegmentationModelProvider> provider =
-      CreateModelProvider(optimization_guide::proto::OptimizationTarget::
-                              OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+      CreateModelProvider(
+          proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
   provider->InitAndFetchModel(base::DoNothing());
   EXPECT_TRUE(model_observer_tracker_->DidRegisterForTarget(
-      optimization_guide::proto::OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
 
   base::RunLoop run_loop;
   std::vector<float> input = {4, 5};
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
index 02cbdcd..3077e9e4 100644
--- a/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
+++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "components/optimization_guide/core/base_model_executor.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 struct TfLiteTensor;
 
diff --git a/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
index 87b8409..f943af4 100644
--- a/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
+++ b/components/segmentation_platform/internal/execution/optimization_guide/segmentation_model_executor_unittest.cc
@@ -19,24 +19,25 @@
 #include "components/optimization_guide/core/test_model_info_builder.h"
 #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/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/internal/segment_id_convertor.h"
 #include "components/segmentation_platform/public/model_provider.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
 
+namespace segmentation_platform {
 namespace {
-const auto kOptimizationTarget = optimization_guide::proto::OptimizationTarget::
-    OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const auto kSegmentId =
+    proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
 const int64_t kModelVersion = 123;
 }  // namespace
 
-namespace segmentation_platform {
 bool AreEqual(const proto::SegmentationModelMetadata& a,
               const proto::SegmentationModelMetadata& b) {
   // Serializing two protos and comparing them is unsafe, in particular if they
@@ -75,7 +76,7 @@
     opt_guide_model_provider_ =
         std::make_unique<OptimizationGuideSegmentationModelProvider>(
             optimization_guide_segmentation_model_provider_.get(),
-            task_environment_.GetMainThreadTaskRunner(), kOptimizationTarget);
+            task_environment_.GetMainThreadTaskRunner(), kSegmentId);
     opt_guide_model_provider_->InitAndFetchModel(callback);
   }
 
@@ -110,8 +111,8 @@
                               .SetModelFilePath(model_file_path_)
                               .SetVersion(kModelVersion)
                               .Build();
-    opt_guide_model_handler().OnModelUpdated(kOptimizationTarget,
-                                             *model_metadata);
+    opt_guide_model_handler().OnModelUpdated(
+        SegmentIdToOptimizationTarget(kSegmentId), *model_metadata);
     RunUntilIdle();
   }
 
@@ -141,11 +142,11 @@
   CreateModelExecutor(base::BindRepeating(
       [](base::RunLoop* run_loop,
          proto::SegmentationModelMetadata original_metadata,
-         optimization_guide::proto::OptimizationTarget optimization_target,
+         proto::SegmentId segment_id,
          proto::SegmentationModelMetadata actual_metadata,
          int64_t model_version) {
         // Verify that the callback is invoked with the correct data.
-        EXPECT_EQ(kOptimizationTarget, optimization_target);
+        EXPECT_EQ(kSegmentId, segment_id);
         EXPECT_TRUE(AreEqual(original_metadata, actual_metadata));
         EXPECT_EQ(kModelVersion, model_version);
         run_loop->Quit();
diff --git a/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc b/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc
index 99e4607..70aa3cc 100644
--- a/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc
+++ b/components/segmentation_platform/internal/execution/processing/custom_input_processor_unittest.cc
@@ -10,10 +10,10 @@
 #include "base/run_loop.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
 #include "components/segmentation_platform/internal/execution/processing/query_processor.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace segmentation_platform::processing {
diff --git a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
index 0ae5f8c..3dd3185 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
+++ b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
@@ -38,7 +38,7 @@
 void FeatureListQueryProcessor::ProcessFeatureList(
     const proto::SegmentationModelMetadata& model_metadata,
     scoped_refptr<InputContext> input_context,
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     base::Time prediction_time,
     ProcessOption process_option,
     FeatureProcessorCallback callback) {
diff --git a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
index b748c3302..608a4fd 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
+++ b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h
@@ -10,13 +10,13 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/ukm_database.h"
 #include "components/segmentation_platform/internal/execution/processing/custom_input_processor.h"
 #include "components/segmentation_platform/internal/execution/processing/query_processor.h"
 #include "components/segmentation_platform/internal/execution/processing/uma_feature_processor.h"
 #include "components/segmentation_platform/internal/input_context.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 class StorageService;
@@ -26,7 +26,7 @@
 class FeatureAggregator;
 class FeatureProcessorState;
 
-using optimization_guide::proto::OptimizationTarget;
+using proto::SegmentId;
 
 // FeatureListQueryProcessor takes a segmentation model's metadata, processes
 // each feature in the metadata's feature list in order and computes an input
@@ -60,7 +60,7 @@
   virtual void ProcessFeatureList(
       const proto::SegmentationModelMetadata& model_metadata,
       scoped_refptr<InputContext> input_context,
-      OptimizationTarget segment_id,
+      SegmentId segment_id,
       base::Time prediction_time,
       ProcessOption process_option,
       FeatureProcessorCallback callback);
diff --git a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
index 9f6646c..e3fe6488 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
+++ b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor_unittest.cc
@@ -45,7 +45,7 @@
         std::make_unique<StorageService>(nullptr, std::move(moved_signal_db),
                                          nullptr, nullptr, &ukm_data_manager_);
     clock_.SetNow(base::Time::Now());
-    segment_id_ = OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+    segment_id_ = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   }
 
   void TearDown() override {
@@ -199,7 +199,7 @@
 
   base::SimpleTestClock clock_;
   base::test::TaskEnvironment task_environment_;
-  OptimizationTarget segment_id_;
+  SegmentId segment_id_;
   proto::SegmentationModelMetadata model_metadata;
   MockUkmDataManager ukm_data_manager_;
   std::unique_ptr<StorageService> storage_service_;
diff --git a/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc b/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc
index 4710198..a8ea6da 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc
+++ b/components/segmentation_platform/internal/execution/processing/feature_processor_state.cc
@@ -32,12 +32,12 @@
 FeatureProcessorState::FeatureProcessorState()
     : prediction_time_(base::Time::Now()),
       bucket_duration_(base::TimeDelta()),
-      segment_id_(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {}
+      segment_id_(SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {}
 
 FeatureProcessorState::FeatureProcessorState(
     base::Time prediction_time,
     base::TimeDelta bucket_duration,
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     std::deque<Data> data,
     scoped_refptr<InputContext> input_context,
     FeatureListQueryProcessor::FeatureProcessorCallback callback)
diff --git a/components/segmentation_platform/internal/execution/processing/feature_processor_state.h b/components/segmentation_platform/internal/execution/processing/feature_processor_state.h
index 129c225..68e0d96 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_processor_state.h
+++ b/components/segmentation_platform/internal/execution/processing/feature_processor_state.h
@@ -11,16 +11,16 @@
 
 #include "base/time/clock.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
 #include "components/segmentation_platform/internal/input_context.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform::processing {
 
-using optimization_guide::proto::OptimizationTarget;
+using proto::SegmentId;
 
 // FeatureProcessorState is responsible for storing all necessary state during
 // the processing of a model's metadata.
@@ -44,7 +44,7 @@
   FeatureProcessorState(
       base::Time prediction_time,
       base::TimeDelta bucket_duration,
-      OptimizationTarget segment_id,
+      SegmentId segment_id,
       std::deque<Data> data,
       scoped_refptr<InputContext> input_context,
       FeatureListQueryProcessor::FeatureProcessorCallback callback);
@@ -59,7 +59,7 @@
 
   base::Time prediction_time() const { return prediction_time_; }
 
-  OptimizationTarget segment_id() const { return segment_id_; }
+  SegmentId segment_id() const { return segment_id_; }
 
   bool error() const { return error_; }
 
@@ -87,7 +87,7 @@
  private:
   const base::Time prediction_time_;
   const base::TimeDelta bucket_duration_;
-  const OptimizationTarget segment_id_;
+  const SegmentId segment_id_;
   std::deque<Data> data_;
   scoped_refptr<InputContext> input_context_;
 
diff --git a/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h b/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h
index 64c9da0..990bac2 100644
--- a/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h
+++ b/components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h
@@ -20,7 +20,7 @@
               ProcessFeatureList,
               (const proto::SegmentationModelMetadata&,
                scoped_refptr<InputContext> input_context,
-               optimization_guide::proto::OptimizationTarget,
+               proto::SegmentId,
                base::Time,
                FeatureListQueryProcessor::ProcessOption,
                FeatureProcessorCallback),
diff --git a/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc b/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc
index 6616a6a..1c9f117 100644
--- a/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc
+++ b/components/segmentation_platform/internal/execution/processing/sql_feature_processor_unittest.cc
@@ -10,11 +10,11 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/mock_ukm_database.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::test::RunOnceCallback;
diff --git a/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc b/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
index e3541c1..ad2ff48 100644
--- a/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
+++ b/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
@@ -23,7 +23,7 @@
     FeatureAggregator* feature_aggregator,
     const base::Time prediction_time,
     const base::TimeDelta bucket_duration,
-    const OptimizationTarget segment_id)
+    const SegmentId segment_id)
     : uma_features_(std::move(uma_features)),
       signal_database_(signal_database),
       feature_aggregator_(feature_aggregator),
diff --git a/components/segmentation_platform/internal/execution/processing/uma_feature_processor.h b/components/segmentation_platform/internal/execution/processing/uma_feature_processor.h
index cec66c01..664900b 100644
--- a/components/segmentation_platform/internal/execution/processing/uma_feature_processor.h
+++ b/components/segmentation_platform/internal/execution/processing/uma_feature_processor.h
@@ -10,11 +10,11 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_aggregator.h"
 #include "components/segmentation_platform/internal/execution/processing/query_processor.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform::processing {
 class FeatureProcessorState;
@@ -30,7 +30,7 @@
       FeatureAggregator* feature_aggregator,
       const base::Time prediction_time,
       const base::TimeDelta bucket_duration,
-      const optimization_guide::proto::OptimizationTarget segment_id);
+      const proto::SegmentId segment_id);
 
   ~UmaFeatureProcessor() override;
 
@@ -69,7 +69,7 @@
   // Data needed for the processing of uma features.
   const base::Time prediction_time_;
   const base::TimeDelta bucket_duration_;
-  const optimization_guide::proto::OptimizationTarget segment_id_;
+  const proto::SegmentId segment_id_;
 
   // Temporary storage of the processing state object.
   // TODO(haileywang): Remove dependency to the state object once error check is
diff --git a/components/segmentation_platform/internal/metadata/metadata_utils.cc b/components/segmentation_platform/internal/metadata/metadata_utils.cc
index 7cf303ed..1863b27 100644
--- a/components/segmentation_platform/internal/metadata/metadata_utils.cc
+++ b/components/segmentation_platform/internal/metadata/metadata_utils.cc
@@ -11,13 +11,13 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/signal_key.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/public/features.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform {
@@ -76,8 +76,7 @@
 }  // namespace
 
 ValidationResult ValidateSegmentInfo(const proto::SegmentInfo& segment_info) {
-  if (segment_info.segment_id() ==
-      optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN) {
+  if (segment_info.segment_id() == proto::OPTIMIZATION_TARGET_UNKNOWN) {
     return ValidationResult::kSegmentIDNotFound;
   }
 
diff --git a/components/segmentation_platform/internal/metadata/metadata_utils.h b/components/segmentation_platform/internal/metadata/metadata_utils.h
index 85ff2bb..ff22148 100644
--- a/components/segmentation_platform/internal/metadata/metadata_utils.h
+++ b/components/segmentation_platform/internal/metadata/metadata_utils.h
@@ -6,17 +6,17 @@
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_UTILS_H_
 
 #include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/signal_key.h"
 #include "components/segmentation_platform/internal/execution/processing/query_processor.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/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
+using proto::SegmentId;
+
 namespace metadata_utils {
 
 // Keep up to date with SegmentationPlatformValidationResult in
diff --git a/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc b/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
index 962e8799..336e790 100644
--- a/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
+++ b/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
@@ -5,11 +5,11 @@
 #include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 
 #include "base/metrics/metrics_hashes.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/processing/query_processor.h"
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace segmentation_platform {
@@ -41,8 +41,8 @@
   EXPECT_EQ(metadata_utils::ValidationResult::kSegmentIDNotFound,
             metadata_utils::ValidateSegmentInfo(segment_info));
 
-  segment_info.set_segment_id(optimization_guide::proto::OptimizationTarget::
-                                  OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  segment_info.set_segment_id(
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   EXPECT_EQ(metadata_utils::ValidationResult::kMetadataNotFound,
             metadata_utils::ValidateSegmentInfo(segment_info));
 
@@ -385,8 +385,8 @@
       metadata_utils::ValidationResult::kSegmentIDNotFound,
       metadata_utils::ValidateSegmentInfoMetadataAndFeatures(segment_info));
 
-  segment_info.set_segment_id(optimization_guide::proto::OptimizationTarget::
-                                  OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  segment_info.set_segment_id(
+      proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   EXPECT_EQ(
       metadata_utils::ValidationResult::kMetadataNotFound,
       metadata_utils::ValidateSegmentInfoMetadataAndFeatures(segment_info));
diff --git a/components/segmentation_platform/internal/metric_filter_utils.cc b/components/segmentation_platform/internal/metric_filter_utils.cc
index fbc015f8..b6c18495 100644
--- a/components/segmentation_platform/internal/metric_filter_utils.cc
+++ b/components/segmentation_platform/internal/metric_filter_utils.cc
@@ -9,12 +9,11 @@
 
 namespace segmentation_platform::stats {
 namespace {
-using optimization_guide::proto::OptimizationTarget;
+using proto::SegmentId;
 
 }  // namespace
 
-std::string OptimizationTargetToSegmentGroupName(
-    OptimizationTarget segment_id) {
+std::string OptimizationTargetToSegmentGroupName(SegmentId segment_id) {
   return OptimizationTargetToHistogramVariant(segment_id);
 }
 
@@ -25,7 +24,7 @@
 
 std::string SegmentationKeyToSubsegmentTrialName(
     const std::string& segmentation_key,
-    optimization_guide::proto::OptimizationTarget segment_id) {
+    proto::SegmentId segment_id) {
   return base::StrCat({"Segmentation_",
                        SegmentationKeyToUmaName(segmentation_key), "_",
                        OptimizationTargetToHistogramVariant(segment_id)});
diff --git a/components/segmentation_platform/internal/metric_filter_utils.h b/components/segmentation_platform/internal/metric_filter_utils.h
index 4ef5f80..f027cf6 100644
--- a/components/segmentation_platform/internal/metric_filter_utils.h
+++ b/components/segmentation_platform/internal/metric_filter_utils.h
@@ -9,15 +9,14 @@
 #include <string>
 #include <vector>
 
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform::stats {
 
 // Returns a name to be used in UMA dashboard as segment group for the given
 // `segment_id`.
-std::string OptimizationTargetToSegmentGroupName(
-    optimization_guide::proto::OptimizationTarget segment_id);
+std::string OptimizationTargetToSegmentGroupName(proto::SegmentId segment_id);
 
 // Returns a name to be used in UMA dashboard as segmentation type for the given
 // `segmentation_key`.
@@ -27,7 +26,7 @@
 // the given `segmentation_key` and `segment_id`.
 std::string SegmentationKeyToSubsegmentTrialName(
     const std::string& segmentation_key,
-    optimization_guide::proto::OptimizationTarget segment_id);
+    proto::SegmentId segment_id);
 
 }  // namespace segmentation_platform::stats
 
diff --git a/components/segmentation_platform/internal/proto/BUILD.gn b/components/segmentation_platform/internal/proto/BUILD.gn
index 09c721d1..2308e6cc 100644
--- a/components/segmentation_platform/internal/proto/BUILD.gn
+++ b/components/segmentation_platform/internal/proto/BUILD.gn
@@ -15,6 +15,5 @@
     "types.proto",
   ]
 
-  link_deps =
-      [ "//components/optimization_guide/proto:optimization_guide_proto" ]
+  link_deps = [ "//components/segmentation_platform/public/proto" ]
 }
diff --git a/components/segmentation_platform/internal/proto/model_prediction.proto b/components/segmentation_platform/internal/proto/model_prediction.proto
index c6ab5785..a8375a1 100644
--- a/components/segmentation_platform/internal/proto/model_prediction.proto
+++ b/components/segmentation_platform/internal/proto/model_prediction.proto
@@ -8,7 +8,7 @@
 package segmentation_platform.proto;
 
 import "components/segmentation_platform/internal/proto/model_metadata.proto";
-import "components/optimization_guide/proto/models.proto";
+import "components/segmentation_platform/public/proto/segmentation_platform.proto";
 
 // Result from the model evaluation for a given segment.
 message PredictionResult {
@@ -25,7 +25,7 @@
 // Next tag: 6
 message SegmentInfo {
   // Segment target.
-  optional optimization_guide.proto.OptimizationTarget segment_id = 1;
+  optional SegmentId segment_id = 1;
 
   // Cached copy of the segment metadata which is important in case the metadata
   // is temporarily not available in the future. It also contains the relevant
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.cc b/components/segmentation_platform/internal/scheduler/execution_service.cc
index 6955be3..26b092c 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.cc
+++ b/components/segmentation_platform/internal/scheduler/execution_service.cc
@@ -40,7 +40,7 @@
     base::Clock* clock,
     ModelExecutionManager::SegmentationModelUpdatedCallback callback,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
-    const base::flat_set<OptimizationTarget>& all_segment_ids,
+    const base::flat_set<SegmentId>& all_segment_ids,
     ModelProviderFactory* model_provider_factory,
     std::vector<ModelExecutionScheduler::Observer*>&& observers,
     const PlatformOptions& platform_options,
@@ -77,8 +77,7 @@
   model_execution_scheduler_->OnNewModelInfoReady(segment_info);
 }
 
-ModelProvider* ExecutionService::GetModelProvider(
-    OptimizationTarget segment_id) {
+ModelProvider* ExecutionService::GetModelProvider(SegmentId segment_id) {
   return model_execution_manager_->GetProvider(segment_id);
 }
 
@@ -105,7 +104,7 @@
 }
 
 void ExecutionService::OverwriteModelExecutionResult(
-    optimization_guide::proto::OptimizationTarget segment_id,
+    proto::SegmentId segment_id,
     const std::pair<float, ModelExecutionStatus>& result) {
   model_execution_scheduler_->OnModelExecutionCompleted(segment_id, result);
 }
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.h b/components/segmentation_platform/internal/scheduler/execution_service.h
index ce2a8c38..58ec883 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.h
+++ b/components/segmentation_platform/internal/scheduler/execution_service.h
@@ -11,11 +11,11 @@
 #include "base/containers/flat_set.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/clock.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/execution/execution_request.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
 #include "components/segmentation_platform/internal/input_context.h"
 #include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 class PrefService;
 
@@ -53,7 +53,7 @@
       base::Clock* clock,
       ModelExecutionManager::SegmentationModelUpdatedCallback callback,
       scoped_refptr<base::SequencedTaskRunner> task_runner,
-      const base::flat_set<OptimizationTarget>& all_segment_ids,
+      const base::flat_set<SegmentId>& all_segment_ids,
       ModelProviderFactory* model_provider_factory,
       std::vector<ModelExecutionScheduler::Observer*>&& observers,
       const PlatformOptions& platform_options,
@@ -65,12 +65,12 @@
   void OnNewModelInfoReady(const proto::SegmentInfo& segment_info);
 
   // Gets the model provider for execution.
-  ModelProvider* GetModelProvider(OptimizationTarget segment_id);
+  ModelProvider* GetModelProvider(SegmentId segment_id);
 
   void RequestModelExecution(std::unique_ptr<ExecutionRequest> request);
 
   void OverwriteModelExecutionResult(
-      optimization_guide::proto::OptimizationTarget segment_id,
+      proto::SegmentId segment_id,
       const std::pair<float, ModelExecutionStatus>& result);
 
   // Refreshes model results for all eligible models.
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h b/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h
index 641b9806..a0ccfca3 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler.h
@@ -5,16 +5,16 @@
 #ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SCHEDULER_MODEL_EXECUTION_SCHEDULER_H_
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SCHEDULER_MODEL_EXECUTION_SCHEDULER_H_
 
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 namespace proto {
 class SegmentInfo;
 }  // namespace proto
 
+using proto::SegmentId;
+
 // Central class responsible for scheduling model execution. Determines which
 // models are eligible for execution based on various criteria e.g. cached
 // results, TTL etc. Invoked from multiple classes such as segment
@@ -25,7 +25,7 @@
   class Observer {
    public:
     // Called whenever a model execution completes.
-    virtual void OnModelExecutionCompleted(OptimizationTarget segment_id) = 0;
+    virtual void OnModelExecutionCompleted(SegmentId segment_id) = 0;
   };
 
   virtual ~ModelExecutionScheduler() = default;
@@ -54,7 +54,7 @@
   // TODO(shaktisahu): Do we want to store that failure reason in the DB
   // instead? We might treat different failures differently next time.
   virtual void OnModelExecutionCompleted(
-      OptimizationTarget segment_id,
+      SegmentId segment_id,
       const std::pair<float, ModelExecutionStatus>& result) = 0;
 };
 
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
index 99b8c94..790cb79 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
@@ -26,7 +26,7 @@
     SignalStorageConfig* signal_storage_config,
     ModelExecutionManager* model_execution_manager,
     ModelExecutor* model_executor,
-    base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids,
+    base::flat_set<proto::SegmentId> segment_ids,
     base::Clock* clock,
     const PlatformOptions& platform_options)
     : observers_(observers),
@@ -59,8 +59,8 @@
 
 void ModelExecutionSchedulerImpl::RequestModelExecutionForEligibleSegments(
     bool expired_only) {
-  std::vector<OptimizationTarget> segment_ids(all_segment_ids_.begin(),
-                                              all_segment_ids_.end());
+  std::vector<SegmentId> segment_ids(all_segment_ids_.begin(),
+                                     all_segment_ids_.end());
   segment_database_->GetSegmentInfoForSegments(
       segment_ids,
       base::BindOnce(&ModelExecutionSchedulerImpl::FilterEligibleSegments,
@@ -69,7 +69,7 @@
 
 void ModelExecutionSchedulerImpl::RequestModelExecution(
     const proto::SegmentInfo& segment_info) {
-  OptimizationTarget segment_id = segment_info.segment_id();
+  SegmentId segment_id = segment_info.segment_id();
   CancelOutstandingExecutionRequests(segment_id);
   outstanding_requests_.insert(std::make_pair(
       segment_id,
@@ -86,7 +86,7 @@
 }
 
 void ModelExecutionSchedulerImpl::OnModelExecutionCompleted(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     const std::pair<float, ModelExecutionStatus>& result) {
   // TODO(shaktisahu): Check ModelExecutionStatus and handle failure cases.
   // Should we save it to DB?
@@ -110,11 +110,11 @@
     std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments) {
   std::vector<const proto::SegmentInfo*> models_to_run;
   for (const auto& pair : *all_segments) {
-    OptimizationTarget segment_id = pair.first;
+    SegmentId segment_id = pair.first;
     const proto::SegmentInfo& segment_info = pair.second;
     if (!ShouldExecuteSegment(expired_only, segment_info)) {
       VLOG(1) << "Segmentation scheduler: Skipped executed segment "
-              << optimization_guide::proto::OptimizationTarget_Name(segment_id);
+              << proto::SegmentId_Name(segment_id);
       continue;
     }
 
@@ -168,7 +168,7 @@
 }
 
 void ModelExecutionSchedulerImpl::CancelOutstandingExecutionRequests(
-    OptimizationTarget segment_id) {
+    SegmentId segment_id) {
   const auto& iter = outstanding_requests_.find(segment_id);
   if (iter != outstanding_requests_.end()) {
     iter->second.Cancel();
@@ -176,7 +176,7 @@
   }
 }
 
-void ModelExecutionSchedulerImpl::OnResultSaved(OptimizationTarget segment_id,
+void ModelExecutionSchedulerImpl::OnResultSaved(SegmentId segment_id,
                                                 bool success) {
   stats::RecordModelExecutionSaveResult(segment_id, success);
   if (!success) {
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
index 1b8954e..cbe2b1a 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
@@ -11,11 +11,11 @@
 #include "base/cancelable_callback.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "components/segmentation_platform/internal/execution/model_executor.h"
 #include "components/segmentation_platform/internal/platform_options.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace base {
 class Clock;
@@ -32,15 +32,14 @@
 
 class ModelExecutionSchedulerImpl : public ModelExecutionScheduler {
  public:
-  ModelExecutionSchedulerImpl(
-      std::vector<Observer*>&& observers,
-      SegmentInfoDatabase* segment_database,
-      SignalStorageConfig* signal_storage_config,
-      ModelExecutionManager* model_execution_manager,
-      ModelExecutor* model_executor,
-      base::flat_set<optimization_guide::proto::OptimizationTarget> segment_ids,
-      base::Clock* clock,
-      const PlatformOptions& platform_options);
+  ModelExecutionSchedulerImpl(std::vector<Observer*>&& observers,
+                              SegmentInfoDatabase* segment_database,
+                              SignalStorageConfig* signal_storage_config,
+                              ModelExecutionManager* model_execution_manager,
+                              ModelExecutor* model_executor,
+                              base::flat_set<proto::SegmentId> segment_ids,
+                              base::Clock* clock,
+                              const PlatformOptions& platform_options);
   ~ModelExecutionSchedulerImpl() override;
 
   // Disallow copy/assign.
@@ -53,7 +52,7 @@
   void RequestModelExecutionForEligibleSegments(bool expired_only) override;
   void RequestModelExecution(const proto::SegmentInfo& segment_info) override;
   void OnModelExecutionCompleted(
-      OptimizationTarget segment_id,
+      SegmentId segment_id,
       const std::pair<float, ModelExecutionStatus>& score) override;
 
  private:
@@ -62,9 +61,9 @@
       std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments);
   bool ShouldExecuteSegment(bool expired_only,
                             const proto::SegmentInfo& segment_info);
-  void CancelOutstandingExecutionRequests(OptimizationTarget segment_id);
+  void CancelOutstandingExecutionRequests(SegmentId segment_id);
 
-  void OnResultSaved(OptimizationTarget segment_id, bool success);
+  void OnResultSaved(SegmentId segment_id, bool success);
 
   // Observers listening to model exeuction events. Required by the segment
   // selection pipeline.
@@ -81,8 +80,7 @@
   const raw_ptr<ModelExecutor> model_executor_;
 
   // The set of all known segments.
-  base::flat_set<optimization_guide::proto::OptimizationTarget>
-      all_segment_ids_;
+  base::flat_set<proto::SegmentId> all_segment_ids_;
 
   // The time provider.
   raw_ptr<base::Clock> clock_;
@@ -91,7 +89,7 @@
 
   // In-flight model execution requests. Will be killed if we get a model
   // update.
-  std::map<OptimizationTarget,
+  std::map<SegmentId,
            base::CancelableOnceCallback<
                ModelExecutor::ModelExecutionCallback::RunType>>
       outstanding_requests_;
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
index a5560aa..d9cd92fe 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_unittest.cc
@@ -30,23 +30,21 @@
 using CleanupItem = std::tuple<uint64_t, SignalType, base::Time>;
 
 namespace {
-constexpr auto kTestOptimizationTarget =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+constexpr auto kTestSegmentId =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
 constexpr auto kTestOptimizationTarget2 =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY;
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY;
 }  // namespace
 
 class MockModelExecutionObserver : public ModelExecutionScheduler::Observer {
  public:
   MockModelExecutionObserver() = default;
-  MOCK_METHOD(void, OnModelExecutionCompleted, (OptimizationTarget));
+  MOCK_METHOD(void, OnModelExecutionCompleted, (SegmentId));
 };
 
 class MockModelExecutionManager : public ModelExecutionManager {
  public:
-  MOCK_METHOD(ModelProvider*,
-              GetProvider,
-              (optimization_guide::proto::OptimizationTarget segment_id));
+  MOCK_METHOD(ModelProvider*, GetProvider, (proto::SegmentId segment_id));
 };
 
 class MockModelExecutor : public ModelExecutor {
@@ -65,8 +63,8 @@
     std::vector<ModelExecutionScheduler::Observer*> observers = {&observer1_,
                                                                  &observer2_};
     segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
-    base::flat_set<OptimizationTarget> segment_ids;
-    segment_ids.insert(kTestOptimizationTarget);
+    base::flat_set<SegmentId> segment_ids;
+    segment_ids.insert(kTestSegmentId);
     model_execution_scheduler_ = std::make_unique<ModelExecutionSchedulerImpl>(
         std::move(observers), segment_database_.get(), &signal_storage_config_,
         &model_execution_manager_, &model_executor_, segment_ids, &clock_,
@@ -89,13 +87,12 @@
 }
 
 TEST_F(ModelExecutionSchedulerTest, OnNewModelInfoReady) {
-  auto* segment_info =
-      segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
-  segment_info->set_segment_id(kTestOptimizationTarget);
+  auto* segment_info = segment_database_->FindOrCreateSegment(kTestSegmentId);
+  segment_info->set_segment_id(kTestSegmentId);
   auto* metadata = segment_info->mutable_model_metadata();
   metadata->set_result_time_to_live(1);
   metadata->set_time_unit(proto::TimeUnit::DAY);
-  MockModelProvider provider(kTestOptimizationTarget, base::DoNothing());
+  MockModelProvider provider(kTestSegmentId, base::DoNothing());
 
   // If the metadata DOES NOT meet the signal requirement, we SHOULD NOT try to
   // execute the model.
@@ -106,10 +103,9 @@
 
   // If the metadata DOES meet the signal requirement, and we have no old,
   // PredictionResult we SHOULD try to execute the model.
-  EXPECT_CALL(model_execution_manager_, GetProvider(kTestOptimizationTarget))
+  EXPECT_CALL(model_execution_manager_, GetProvider(kTestSegmentId))
       .WillOnce(Return(&provider));
-  EXPECT_CALL(model_executor_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget)))
+  EXPECT_CALL(model_executor_, ExecuteModel(IsForTarget(kTestSegmentId)))
       .Times(1);
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillOnce(Return(true));
@@ -141,26 +137,24 @@
   prediction_result->set_result(0.9);
   prediction_result->set_timestamp_us(
       just_expired_timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds());
-  EXPECT_CALL(model_execution_manager_, GetProvider(kTestOptimizationTarget))
+  EXPECT_CALL(model_execution_manager_, GetProvider(kTestSegmentId))
       .WillOnce(Return(&provider));
-  EXPECT_CALL(model_executor_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget)))
+  EXPECT_CALL(model_executor_, ExecuteModel(IsForTarget(kTestSegmentId)))
       .Times(1);
   model_execution_scheduler_->OnNewModelInfoReady(*segment_info);
 }
 
 TEST_F(ModelExecutionSchedulerTest, RequestModelExecutionForEligibleSegments) {
-  MockModelProvider provider(kTestOptimizationTarget, base::DoNothing());
-  segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
+  MockModelProvider provider(kTestSegmentId, base::DoNothing());
+  segment_database_->FindOrCreateSegment(kTestSegmentId);
   segment_database_->FindOrCreateSegment(kTestOptimizationTarget2);
 
   // TODO(shaktisahu): Add tests for expired segments, freshly computed segments
   // etc.
 
-  EXPECT_CALL(model_execution_manager_, GetProvider(kTestOptimizationTarget))
+  EXPECT_CALL(model_execution_manager_, GetProvider(kTestSegmentId))
       .WillOnce(Return(&provider));
-  EXPECT_CALL(model_executor_,
-              ExecuteModel(IsForTarget(kTestOptimizationTarget)))
+  EXPECT_CALL(model_executor_, ExecuteModel(IsForTarget(kTestSegmentId)))
       .Times(1);
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillRepeatedly(Return(true));
@@ -174,21 +168,17 @@
 
 TEST_F(ModelExecutionSchedulerTest, OnModelExecutionCompleted) {
   proto::SegmentInfo* segment_info =
-      segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
+      segment_database_->FindOrCreateSegment(kTestSegmentId);
 
   // TODO(shaktisahu): Add tests for model failure.
-  EXPECT_CALL(observer2_, OnModelExecutionCompleted(kTestOptimizationTarget))
-      .Times(1);
-  EXPECT_CALL(observer1_, OnModelExecutionCompleted(kTestOptimizationTarget))
-      .Times(1);
+  EXPECT_CALL(observer2_, OnModelExecutionCompleted(kTestSegmentId)).Times(1);
+  EXPECT_CALL(observer1_, OnModelExecutionCompleted(kTestSegmentId)).Times(1);
   float score = 0.4;
   model_execution_scheduler_->OnModelExecutionCompleted(
-      kTestOptimizationTarget,
-      std::make_pair(score, ModelExecutionStatus::kSuccess));
+      kTestSegmentId, std::make_pair(score, ModelExecutionStatus::kSuccess));
 
   // Verify that the results are written to the DB.
-  segment_info =
-      segment_database_->FindOrCreateSegment(kTestOptimizationTarget);
+  segment_info = segment_database_->FindOrCreateSegment(kTestSegmentId);
   ASSERT_TRUE(segment_info->has_prediction_result());
   ASSERT_EQ(score, segment_info->prediction_result().result());
 }
diff --git a/components/segmentation_platform/internal/segment_id_convertor.cc b/components/segmentation_platform/internal/segment_id_convertor.cc
new file mode 100644
index 0000000..a60bb91
--- /dev/null
+++ b/components/segmentation_platform/internal/segment_id_convertor.cc
@@ -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.
+
+#include "components/segmentation_platform/internal/segment_id_convertor.h"
+
+namespace segmentation_platform {
+
+optimization_guide::proto::OptimizationTarget SegmentIdToOptimizationTarget(
+    proto::SegmentId segment_id) {
+  return static_cast<optimization_guide::proto::OptimizationTarget>(segment_id);
+}
+
+proto::SegmentId OptimizationTargetToSegmentId(
+    optimization_guide::proto::OptimizationTarget segment_id) {
+  return static_cast<proto::SegmentId>(segment_id);
+}
+
+}  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/segment_id_convertor.h b/components/segmentation_platform/internal/segment_id_convertor.h
new file mode 100644
index 0000000..e0e70e6
--- /dev/null
+++ b/components/segmentation_platform/internal/segment_id_convertor.h
@@ -0,0 +1,23 @@
+// 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_SEGMENT_ID_CONVERTOR_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SEGMENT_ID_CONVERTOR_H_
+
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
+
+namespace segmentation_platform {
+
+// Conversion functions between OptimizationTarget and SegmentId.
+optimization_guide::proto::OptimizationTarget SegmentIdToOptimizationTarget(
+    proto::SegmentId segment_id);
+
+// Conversion functions between OptimizationTarget and SegmentId.
+proto::SegmentId OptimizationTargetToSegmentId(
+    optimization_guide::proto::OptimizationTarget segment_id);
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SEGMENT_ID_CONVERTOR_H_
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index 44ceb10..09022c0 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -30,15 +30,14 @@
 #include "components/segmentation_platform/public/field_trial_register.h"
 #include "components/segmentation_platform/public/model_provider.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
-
 namespace {
 
-base::flat_set<OptimizationTarget> GetAllSegmentIds(
+using proto::SegmentId;
+
+base::flat_set<SegmentId> GetAllSegmentIds(
     const std::vector<std::unique_ptr<Config>>& configs) {
-  base::flat_set<OptimizationTarget> all_segment_ids;
+  base::flat_set<SegmentId> all_segment_ids;
   for (const auto& config : configs) {
     for (const auto& segment_id : config->segment_ids)
       all_segment_ids.insert(segment_id);
@@ -83,8 +82,8 @@
         model_provider_factory_.get());
   }
 
-  std::vector<OptimizationTarget> segment_id_vec(all_segment_ids_.begin(),
-                                                 all_segment_ids_.end());
+  std::vector<SegmentId> segment_id_vec(all_segment_ids_.begin(),
+                                        all_segment_ids_.end());
 
   // Construct signal processors.
   signal_handler_.Initialize(
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
index 84d487c5..b7aa877 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.h
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
@@ -14,13 +14,13 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/storage_service.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/platform_options.h"
 #include "components/segmentation_platform/internal/scheduler/execution_service.h"
 #include "components/segmentation_platform/internal/service_proxy_impl.h"
 #include "components/segmentation_platform/internal/signals/signal_handler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "components/segmentation_platform/public/segmentation_platform_service.h"
 
 namespace base {
@@ -123,8 +123,7 @@
 
   // Config.
   std::vector<std::unique_ptr<Config>> configs_;
-  base::flat_set<optimization_guide::proto::OptimizationTarget>
-      all_segment_ids_;
+  base::flat_set<proto::SegmentId> all_segment_ids_;
   std::unique_ptr<FieldTrialRegister> field_trial_register_;
 
   std::unique_ptr<StorageService> storage_service_;
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 e9451cf..2387e054 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
@@ -19,6 +19,7 @@
 #include "components/segmentation_platform/internal/database/mock_ukm_database.h"
 #include "components/segmentation_platform/internal/dummy_ukm_data_manager.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/segment_id_convertor.h"
 #include "components/segmentation_platform/internal/segmentation_platform_service_test_base.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
 #include "components/segmentation_platform/internal/signals/ukm_observer.h"
@@ -67,7 +68,7 @@
     LocalStateHelper::GetInstance().Initialize(&prefs_);
     ukm_data_manager_ = std::make_unique<UkmDataManagerImpl>();
     ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
-    ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get(), true);
+    ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get());
     auto ukm_database = std::make_unique<MockUkmDatabase>();
     static_cast<UkmDataManagerImpl*>(ukm_data_manager_.get())
         ->InitializeForTesting(std::move(ukm_database), ukm_observer_.get());
@@ -101,12 +102,11 @@
   void AssertSelectedSegment(
       const std::string& segmentation_key,
       bool is_ready,
-      OptimizationTarget expected =
-          OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+      SegmentId expected = SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     SegmentSelectionResult result;
     result.is_ready = is_ready;
     if (is_ready)
-      result.segment = expected;
+      result.segment = SegmentIdToOptimizationTarget(expected);
     base::RunLoop loop;
     segmentation_platform_service_impl_->GetSelectedSegment(
         segmentation_key,
@@ -119,12 +119,11 @@
   void AssertCachedSegment(
       const std::string& segmentation_key,
       bool is_ready,
-      OptimizationTarget expected =
-          OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+      SegmentId expected = SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     SegmentSelectionResult result;
     result.is_ready = is_ready;
     if (is_ready)
-      result.segment = expected;
+      result.segment = SegmentIdToOptimizationTarget(expected);
     ASSERT_EQ(result,
               segmentation_platform_service_impl_->GetCachedSegmentResult(
                   segmentation_key));
@@ -166,12 +165,12 @@
     // from the database, and then write the merged result of the old and new to
     // the database.
     ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count(
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
     model_provider_data_
         .model_providers_callbacks
-            [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE]
-        .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-             metadata, kModelVersion);
+            [SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE]
+        .Run(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, metadata,
+             kModelVersion);
     segment_db_->GetCallback(true);
     segment_db_->UpdateCallback(true);
 
@@ -185,14 +184,12 @@
         histogram_tester.GetBucketCount(
             "SegmentationPlatform.Signals.ListeningCount.HistogramValue", 1));
 
-    AssertSelectedSegment(
-        kTestSegmentationKey1, true,
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+    AssertSelectedSegment(kTestSegmentationKey1, true,
+                          SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
     AssertSelectedSegment(kTestSegmentationKey2, false);
     AssertSelectedSegment(kTestSegmentationKey3, false);
-    AssertCachedSegment(
-        kTestSegmentationKey1, true,
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+    AssertCachedSegment(kTestSegmentationKey1, true,
+                        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
     AssertCachedSegment(kTestSegmentationKey2, false);
     AssertCachedSegment(kTestSegmentationKey3, false);
 
@@ -202,12 +199,12 @@
     segment_db_->LoadCallback(true);
 
     ASSERT_TRUE(model_provider_data_.model_providers_callbacks.count(
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
     model_provider_data_
         .model_providers_callbacks
-            [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE]
-        .Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
-             metadata, kModelVersion);
+            [SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE]
+        .Run(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE, metadata,
+             kModelVersion);
     segment_db_->GetCallback(true);
     segment_db_->UpdateCallback(true);
 
@@ -232,14 +229,12 @@
     task_environment_.FastForwardBy(base::Hours(1));
     segment_db_->LoadCallback(true);
 
-    AssertSelectedSegment(
-        kTestSegmentationKey1, true,
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+    AssertSelectedSegment(kTestSegmentationKey1, true,
+                          SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
     AssertSelectedSegment(kTestSegmentationKey2, false);
     AssertSelectedSegment(kTestSegmentationKey3, false);
-    AssertCachedSegment(
-        kTestSegmentationKey1, true,
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+    AssertCachedSegment(kTestSegmentationKey1, true,
+                        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
     AssertCachedSegment(kTestSegmentationKey2, false);
     AssertCachedSegment(kTestSegmentationKey3, false);
   }
@@ -299,14 +294,12 @@
 
     base::Value segmentation_result(base::Value::Type::DICTIONARY);
     segmentation_result.SetIntKey(
-        "segment_id",
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+        "segment_id", SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
     dictionary->SetKey(kTestSegmentationKey1, std::move(segmentation_result));
 
     base::Value segmentation_result2(base::Value::Type::DICTIONARY);
     segmentation_result2.SetIntKey(
-        "segment_id",
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+        "segment_id", SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
     dictionary->SetKey(kTestSegmentationKey2, std::move(segmentation_result2));
   }
 };
@@ -324,19 +317,15 @@
   // querying segment db.
   segment_db_->LoadCallback(true);
 
-  AssertSelectedSegment(
-      kTestSegmentationKey1, true,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
-  AssertSelectedSegment(
-      kTestSegmentationKey2, true,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+  AssertSelectedSegment(kTestSegmentationKey1, true,
+                        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+  AssertSelectedSegment(kTestSegmentationKey2, true,
+                        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
   AssertSelectedSegment(kTestSegmentationKey3, false);
-  AssertCachedSegment(
-      kTestSegmentationKey1, true,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
-  AssertCachedSegment(
-      kTestSegmentationKey2, true,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
+  AssertCachedSegment(kTestSegmentationKey1, true,
+                      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+  AssertCachedSegment(kTestSegmentationKey2, true,
+                      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE);
   AssertCachedSegment(kTestSegmentationKey3, false);
 }
 
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc b/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc
index 18ea67b..8fa92fb 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_test_base.cc
@@ -27,7 +27,7 @@
                     base::StringPiece group_name));
   MOCK_METHOD3(RegisterSubsegmentFieldTrialIfNeeded,
                void(base::StringPiece trial_name,
-                    optimization_guide::proto::OptimizationTarget segment_id,
+                    proto::SegmentId segment_id,
                     int subsegment_rank));
 };
 
@@ -37,26 +37,23 @@
     std::unique_ptr<Config> config = std::make_unique<Config>();
     config->segmentation_key = kTestSegmentationKey1;
     config->segment_selection_ttl = base::Days(28);
-    config->segment_ids = {
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
+    config->segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+                           SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
     configs.push_back(std::move(config));
   }
   {
     std::unique_ptr<Config> config = std::make_unique<Config>();
     config->segmentation_key = kTestSegmentationKey2;
     config->segment_selection_ttl = base::Days(10);
-    config->segment_ids = {
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE};
+    config->segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+                           SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE};
     configs.push_back(std::move(config));
   }
   {
     std::unique_ptr<Config> config = std::make_unique<Config>();
     config->segmentation_key = kTestSegmentationKey3;
     config->segment_selection_ttl = base::Days(14);
-    config->segment_ids = {
-        OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB};
+    config->segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB};
     configs.push_back(std::move(config));
   }
   {
@@ -104,7 +101,7 @@
   SetUpPrefs();
 
   std::vector<std::unique_ptr<Config>> configs = CreateTestConfigs();
-  base::flat_set<OptimizationTarget> all_segment_ids;
+  base::flat_set<SegmentId> all_segment_ids;
   for (const auto& config : configs) {
     for (const auto& segment_id : config->segment_ids)
       all_segment_ids.insert(segment_id);
@@ -141,7 +138,7 @@
 
   base::Value segmentation_result(base::Value::Type::DICTIONARY);
   segmentation_result.SetIntKey(
-      "segment_id", OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+      "segment_id", SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
   dictionary->SetKey(kTestSegmentationKey1, std::move(segmentation_result));
 }
 
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper.cc b/components/segmentation_platform/internal/segmentation_ukm_helper.cc
index 24bc080..ab51818 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper.cc
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper.cc
@@ -21,6 +21,7 @@
 #define CALL_MEMBER_FN(obj, func) ((obj).*(func))
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x)[0])
 
+using segmentation_platform::proto::SegmentId;
 using ukm::builders::Segmentation_ModelExecution;
 
 namespace {
@@ -67,7 +68,7 @@
     &Segmentation_ModelExecution::SetActualResult5,
     &Segmentation_ModelExecution::SetActualResult6};
 
-base::flat_set<OptimizationTarget> GetSegmentIdsAllowedForReporting() {
+base::flat_set<SegmentId> GetSegmentIdsAllowedForReporting() {
   std::vector<std::string> segment_ids = base::SplitString(
       base::GetFieldTrialParamValueByFeature(
           segmentation_platform::features::
@@ -75,11 +76,11 @@
           segmentation_platform::kSegmentIdsAllowedForReportingKey),
       ",;", base::WhitespaceHandling::TRIM_WHITESPACE,
       base::SplitResult::SPLIT_WANT_NONEMPTY);
-  base::flat_set<OptimizationTarget> result;
+  base::flat_set<SegmentId> result;
   for (const auto& id : segment_ids) {
     int segment_id;
     if (base::StringToInt(id, &segment_id))
-      result.emplace(static_cast<OptimizationTarget>(segment_id));
+      result.emplace(static_cast<SegmentId>(segment_id));
   }
   return result;
 }
@@ -105,7 +106,7 @@
 }
 
 ukm::SourceId SegmentationUkmHelper::RecordModelExecutionResult(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     int64_t model_version,
     const std::vector<float>& input_tensor,
     float result) {
@@ -124,7 +125,7 @@
 }
 
 ukm::SourceId SegmentationUkmHelper::RecordTrainingData(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     int64_t model_version,
     const std::vector<float>& input_tensor,
     const std::vector<float>& outputs,
@@ -158,7 +159,7 @@
 
 bool SegmentationUkmHelper::AddInputsToUkm(
     ukm::builders::Segmentation_ModelExecution* ukm_builder,
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     int64_t model_version,
     const std::vector<float>& input_tensor) {
   if (!allowed_segment_ids_.contains(static_cast<int>(segment_id)))
@@ -211,10 +212,14 @@
 bool SegmentationUkmHelper::AllowedToUploadData(
     base::TimeDelta signal_storage_length,
     base::Clock* clock) {
-  return LocalStateHelper::GetInstance().GetPrefTime(
-             kSegmentationUkmMostRecentAllowedTimeKey) +
-             signal_storage_length <
-         clock->Now();
+  base::Time most_recent_allowed = LocalStateHelper::GetInstance().GetPrefTime(
+      kSegmentationUkmMostRecentAllowedTimeKey);
+  // If the local state is never set, return false.
+  if (most_recent_allowed.is_null() ||
+      most_recent_allowed == base::Time::Max()) {
+    return false;
+  }
+  return most_recent_allowed + signal_storage_length < clock->Now();
 }
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper.h b/components/segmentation_platform/internal/segmentation_ukm_helper.h
index 4ceb6e5..80b2021 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper.h
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper.h
@@ -8,13 +8,11 @@
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace base {
 class Clock;
 }
@@ -24,6 +22,8 @@
 }  // namespace ukm::builders
 
 namespace segmentation_platform {
+
+using proto::SegmentId;
 struct SelectedSegment;
 
 // A helper class to record segmentation model execution results in UKM.
@@ -36,7 +36,7 @@
   // Record segmentation model information and input/output after the
   // executing the model, and return the UKM source ID.
   ukm::SourceId RecordModelExecutionResult(
-      OptimizationTarget segment_id,
+      SegmentId segment_id,
       int64_t model_version,
       const std::vector<float>& input_tensor,
       float result);
@@ -51,7 +51,7 @@
   // tied to the ML model.
   // Return the UKM source ID.
   ukm::SourceId RecordTrainingData(
-      OptimizationTarget segment_id,
+      SegmentId segment_id,
       int64_t model_version,
       const std::vector<float>& input_tensors,
       const std::vector<float>& outputs,
@@ -68,13 +68,13 @@
                                   base::Clock* clock);
 
   // Gets a set of segment IDs that are allowed to upload metrics.
-  const base::flat_set<OptimizationTarget>& allowed_segment_ids() {
+  const base::flat_set<SegmentId>& allowed_segment_ids() {
     return allowed_segment_ids_;
   }
 
  private:
   bool AddInputsToUkm(ukm::builders::Segmentation_ModelExecution* ukm_builder,
-                      OptimizationTarget segment_id,
+                      SegmentId segment_id,
                       int64_t model_version,
                       const std::vector<float>& input_tensor);
 
@@ -89,7 +89,7 @@
 
   void Initialize();
 
-  base::flat_set<OptimizationTarget> allowed_segment_ids_;
+  base::flat_set<SegmentId> allowed_segment_ids_;
 };
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc b/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
index 8a3d59ff..faf59f9 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
@@ -24,6 +24,8 @@
 
 using Segmentation_ModelExecution = ukm::builders::Segmentation_ModelExecution;
 
+namespace segmentation_platform {
+
 namespace {
 
 // Round errors allowed during conversion.
@@ -42,17 +44,14 @@
       kRoundingError);
 }
 
-absl::optional<segmentation_platform::proto::PredictionResult>
-GetPredictionResult() {
-  segmentation_platform::proto::PredictionResult result;
+absl::optional<proto::PredictionResult> GetPredictionResult() {
+  proto::PredictionResult result;
   result.set_result(0.5);
   return result;
 }
 
 }  // namespace
 
-namespace segmentation_platform {
-
 class SegmentationUkmHelperTest : public testing::Test {
  public:
   SegmentationUkmHelperTest() = default;
@@ -109,26 +108,24 @@
   InitializeAllowedSegmentIds("4");
   std::vector<float> input_tensors = {0.1, 0.7, 0.8, 0.5};
   SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
-      input_tensors, 0.6);
-  ExpectUkmMetrics(
-      Segmentation_ModelExecution::kEntryName,
-      {Segmentation_ModelExecution::kOptimizationTargetName,
-       Segmentation_ModelExecution::kModelVersionName,
-       Segmentation_ModelExecution::kInput0Name,
-       Segmentation_ModelExecution::kInput1Name,
-       Segmentation_ModelExecution::kInput2Name,
-       Segmentation_ModelExecution::kInput3Name,
-       Segmentation_ModelExecution::kPredictionResultName},
-      {
-          optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-          101,
-          SegmentationUkmHelper::FloatToInt64(0.1),
-          SegmentationUkmHelper::FloatToInt64(0.7),
-          SegmentationUkmHelper::FloatToInt64(0.8),
-          SegmentationUkmHelper::FloatToInt64(0.5),
-          SegmentationUkmHelper::FloatToInt64(0.6),
-      });
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors, 0.6);
+  ExpectUkmMetrics(Segmentation_ModelExecution::kEntryName,
+                   {Segmentation_ModelExecution::kOptimizationTargetName,
+                    Segmentation_ModelExecution::kModelVersionName,
+                    Segmentation_ModelExecution::kInput0Name,
+                    Segmentation_ModelExecution::kInput1Name,
+                    Segmentation_ModelExecution::kInput2Name,
+                    Segmentation_ModelExecution::kInput3Name,
+                    Segmentation_ModelExecution::kPredictionResultName},
+                   {
+                       proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+                       101,
+                       SegmentationUkmHelper::FloatToInt64(0.1),
+                       SegmentationUkmHelper::FloatToInt64(0.7),
+                       SegmentationUkmHelper::FloatToInt64(0.8),
+                       SegmentationUkmHelper::FloatToInt64(0.5),
+                       SegmentationUkmHelper::FloatToInt64(0.6),
+                   });
 }
 
 // Tests that the training data collection recording works properly.
@@ -140,32 +137,30 @@
   std::vector<int> output_indexes = {2, 3};
 
   SelectedSegment selected_segment(
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   selected_segment.selection_time = base::Time::Now() - base::Seconds(10);
   SegmentationUkmHelper::GetInstance()->RecordTrainingData(
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
-      input_tensors, outputs, output_indexes, GetPredictionResult(),
-      selected_segment);
-  ExpectUkmMetrics(
-      Segmentation_ModelExecution::kEntryName,
-      {Segmentation_ModelExecution::kOptimizationTargetName,
-       Segmentation_ModelExecution::kModelVersionName,
-       Segmentation_ModelExecution::kInput0Name,
-       Segmentation_ModelExecution::kActualResult3Name,
-       Segmentation_ModelExecution::kActualResult4Name,
-       Segmentation_ModelExecution::kPredictionResultName,
-       Segmentation_ModelExecution::kSelectionResultName,
-       Segmentation_ModelExecution::kOutputDelaySecName},
-      {
-          optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-          101,
-          SegmentationUkmHelper::FloatToInt64(0.1),
-          SegmentationUkmHelper::FloatToInt64(1.0),
-          SegmentationUkmHelper::FloatToInt64(0.0),
-          SegmentationUkmHelper::FloatToInt64(0.5),
-          optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-          10,
-      });
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+      outputs, output_indexes, GetPredictionResult(), selected_segment);
+  ExpectUkmMetrics(Segmentation_ModelExecution::kEntryName,
+                   {Segmentation_ModelExecution::kOptimizationTargetName,
+                    Segmentation_ModelExecution::kModelVersionName,
+                    Segmentation_ModelExecution::kInput0Name,
+                    Segmentation_ModelExecution::kActualResult3Name,
+                    Segmentation_ModelExecution::kActualResult4Name,
+                    Segmentation_ModelExecution::kPredictionResultName,
+                    Segmentation_ModelExecution::kSelectionResultName,
+                    Segmentation_ModelExecution::kOutputDelaySecName},
+                   {
+                       proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+                       101,
+                       SegmentationUkmHelper::FloatToInt64(0.1),
+                       SegmentationUkmHelper::FloatToInt64(1.0),
+                       SegmentationUkmHelper::FloatToInt64(0.0),
+                       SegmentationUkmHelper::FloatToInt64(0.5),
+                       proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+                       10,
+                   });
 }
 
 // Tests that recording is disabled if kSegmentationStructuredMetricsFeature
@@ -174,8 +169,7 @@
   DisableStructureMetrics();
   std::vector<float> input_tensors = {0.1, 0.7, 0.8, 0.5};
   SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
-      input_tensors, 0.6);
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors, 0.6);
   ExpectEmptyUkmMetrics(Segmentation_ModelExecution::kEntryName);
 }
 
@@ -185,8 +179,7 @@
   InitializeAllowedSegmentIds("7, 8");
   std::vector<float> input_tensors = {0.1, 0.7, 0.8, 0.5};
   SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
-      input_tensors, 0.6);
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors, 0.6);
   ExpectEmptyUkmMetrics(Segmentation_ModelExecution::kEntryName);
 }
 
@@ -221,8 +214,8 @@
   std::vector<float> input_tensors(100, 0.1);
   ukm::SourceId source_id =
       SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
-          optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-          101, input_tensors, 0.6);
+          proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+          0.6);
   ASSERT_EQ(source_id, ukm::kInvalidSourceId);
   tester.ExpectTotalCount(histogram_name, 1);
   ASSERT_EQ(tester.GetTotalSum(histogram_name), 100);
@@ -239,25 +232,22 @@
 
   ukm::SourceId source_id =
       SegmentationUkmHelper::GetInstance()->RecordTrainingData(
-          optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-          101, input_tensors, outputs, output_indexes, GetPredictionResult(),
-          absl::nullopt);
+          proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+          outputs, output_indexes, GetPredictionResult(), absl::nullopt);
   ASSERT_EQ(source_id, ukm::kInvalidSourceId);
 
   // output_indexes value too large.
   output_indexes = {100, 1000};
   source_id = SegmentationUkmHelper::GetInstance()->RecordTrainingData(
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
-      input_tensors, outputs, output_indexes, GetPredictionResult(),
-      absl::nullopt);
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+      outputs, output_indexes, GetPredictionResult(), absl::nullopt);
   ASSERT_EQ(source_id, ukm::kInvalidSourceId);
 
   // Valid outputs.
   output_indexes = {3, 0};
   source_id = SegmentationUkmHelper::GetInstance()->RecordTrainingData(
-      optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101,
-      input_tensors, outputs, output_indexes, GetPredictionResult(),
-      absl::nullopt);
+      proto::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 101, input_tensors,
+      outputs, output_indexes, GetPredictionResult(), absl::nullopt);
   ASSERT_NE(source_id, ukm::kInvalidSourceId);
 }
 
@@ -265,9 +255,13 @@
   TestingPrefServiceSimple prefs;
   SegmentationPlatformService::RegisterLocalStatePrefs(prefs.registry());
   LocalStateHelper::GetInstance().Initialize(&prefs);
-
   base::SimpleTestClock clock;
   clock.SetNow(base::Time::Now());
+
+  // If pref is not initialized, AllowedToUploadData() always return false.
+  ASSERT_FALSE(
+      SegmentationUkmHelper::AllowedToUploadData(base::Seconds(1), &clock));
+
   LocalStateHelper::GetInstance().SetPrefTime(
       kSegmentationUkmMostRecentAllowedTimeKey, clock.Now());
 
diff --git a/components/segmentation_platform/internal/selection/experimental_group_recorder.cc b/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
index 43137e5..09b226f 100644
--- a/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
+++ b/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
@@ -20,7 +20,7 @@
     SegmentInfoDatabase* segment_database,
     FieldTrialRegister* field_trial_register,
     const std::string& segmentation_key,
-    optimization_guide::proto::OptimizationTarget selected_segment)
+    proto::SegmentId selected_segment)
     : field_trial_register_(field_trial_register),
       segmentation_key_(segmentation_key) {
   segment_database->GetSegmentInfo(
diff --git a/components/segmentation_platform/internal/selection/experimental_group_recorder.h b/components/segmentation_platform/internal/selection/experimental_group_recorder.h
index bf08ae19..4bf0b70 100644
--- a/components/segmentation_platform/internal/selection/experimental_group_recorder.h
+++ b/components/segmentation_platform/internal/selection/experimental_group_recorder.h
@@ -7,8 +7,8 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform {
@@ -22,11 +22,10 @@
   // On construction, gets the model score from the database and records the
   // subsegment based on the score. This class must be kept alive till the
   // recording is complete, can be used only once.
-  ExperimentalGroupRecorder(
-      SegmentInfoDatabase* storage_service,
-      FieldTrialRegister* field_trial_register,
-      const std::string& segmentation_key,
-      optimization_guide::proto::OptimizationTarget selected_segment);
+  ExperimentalGroupRecorder(SegmentInfoDatabase* storage_service,
+                            FieldTrialRegister* field_trial_register,
+                            const std::string& segmentation_key,
+                            proto::SegmentId selected_segment);
   ~ExperimentalGroupRecorder();
 
   ExperimentalGroupRecorder(ExperimentalGroupRecorder&) = delete;
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc
index 4772680..eafba1e 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -28,7 +28,7 @@
       segmentation_key, segment_info.prediction_result().result(),
       segment_info.model_metadata());
   VLOG(1) << __func__
-          << ": segment=" << OptimizationTarget_Name(segment_info.segment_id())
+          << ": segment=" << SegmentId_Name(segment_info.segment_id())
           << ": result=" << segment_info.prediction_result().result()
           << ", rank=" << rank;
 
@@ -110,7 +110,7 @@
 };
 
 void SegmentResultProviderImpl::GetSegmentResult(GetResultOptions&& options) {
-  const OptimizationTarget segment_id = options.segment_id;
+  const SegmentId segment_id = options.segment_id;
   auto request_state = std::make_unique<RequestState>();
   request_state = std::make_unique<RequestState>();
   request_state->options = std::move(options);
@@ -135,8 +135,8 @@
   // Don't compute results if we don't have enough signals, or don't have
   // valid unexpired results for any of the segments.
   if (!db_segment_info) {
-    VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->options.segment_id)
+    VLOG(1) << __func__
+            << ": segment=" << SegmentId_Name(request_state->options.segment_id)
             << " does not have segment info.";
     TryGetScoreFromDefaultModel(std::move(request_state),
                                 ResultState::kSegmentNotAvailable,
@@ -149,8 +149,8 @@
   if (!force_refresh_results_ &&
       !signal_storage_config_->MeetsSignalCollectionRequirement(
           db_segment_info->model_metadata())) {
-    VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(db_segment_info->segment_id())
+    VLOG(1) << __func__
+            << ": segment=" << SegmentId_Name(db_segment_info->segment_id())
             << " does not meet signal collection requirements.";
     TryGetScoreFromDefaultModel(std::move(request_state),
                                 ResultState::kSignalsNotCollected,
@@ -159,8 +159,8 @@
   }
 
   if (request_state->options.ignore_db_scores) {
-    VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(db_segment_info->segment_id())
+    VLOG(1) << __func__
+            << ": segment=" << SegmentId_Name(db_segment_info->segment_id())
             << " executing model to get score";
     TryExecuteModelAndGetScore(std::move(request_state),
                                std::move(available_segments));
@@ -169,8 +169,8 @@
 
   if (metadata_utils::HasExpiredOrUnavailableResult(*db_segment_info,
                                                     clock_->Now())) {
-    VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(db_segment_info->segment_id())
+    VLOG(1) << __func__
+            << ": segment=" << SegmentId_Name(db_segment_info->segment_id())
             << " has expired or unavailable result.";
     TryGetScoreFromDefaultModel(std::move(request_state),
                                 ResultState::kDatabaseScoreNotReady,
@@ -214,8 +214,8 @@
     DefaultModelManager::SegmentInfoList available_segments) {
   if (!request_state->default_provider ||
       !request_state->default_provider->ModelAvailable()) {
-    VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->options.segment_id)
+    VLOG(1) << __func__
+            << ": segment=" << SegmentId_Name(request_state->options.segment_id)
             << " default provider not available";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(existing_state));
@@ -225,8 +225,8 @@
   proto::SegmentInfo* segment_info =
       GetSegmentInfo(available_segments, /*default_model=*/true);
   if (!segment_info) {
-    VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->options.segment_id)
+    VLOG(1) << __func__
+            << ": segment=" << SegmentId_Name(request_state->options.segment_id)
             << " default segment info not available";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(
@@ -243,8 +243,8 @@
   if (!force_refresh_results_ &&
       !signal_storage_config_->MeetsSignalCollectionRequirement(
           default_segment_info->model_metadata())) {
-    VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->options.segment_id)
+    VLOG(1) << __func__
+            << ": segment=" << SegmentId_Name(request_state->options.segment_id)
             << " signal collection not met";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.h b/components/segmentation_platform/internal/selection/segment_result_provider.h
index 2458763..883d9b20b 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.h
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.h
@@ -7,9 +7,9 @@
 
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/input_context.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
@@ -75,8 +75,7 @@
     GetResultOptions& operator=(GetResultOptions&&);
 
     // The segment ID to fetch result for.
-    OptimizationTarget segment_id =
-        OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+    SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
 
     // The key is used for recording metrics only.
     std::string segmentation_key;
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
index 2b91a51..6dc2450 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
@@ -29,10 +29,10 @@
 using ::testing::ByMove;
 using ::testing::Return;
 
-const OptimizationTarget kTestSegment =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
-const OptimizationTarget kTestSegment2 =
-    OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
+const SegmentId kTestSegment =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+const SegmentId kTestSegment2 =
+    SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
 
 constexpr float kTestScore = 0.1;
 constexpr float kDatabaseScore = 0.6;
@@ -42,14 +42,13 @@
 class TestModelProvider : public ModelProvider {
  public:
   static constexpr int64_t kVersion = 10;
-  explicit TestModelProvider(OptimizationTarget segment)
-      : ModelProvider(segment) {}
+  explicit TestModelProvider(SegmentId segment) : ModelProvider(segment) {}
 
   void InitAndFetchModel(
       const ModelUpdatedCallback& model_updated_callback) override {
     proto::SegmentationModelMetadata metadata;
     metadata.set_time_unit(proto::TimeUnit::DAY);
-    model_updated_callback.Run(optimization_target_, metadata, kVersion);
+    model_updated_callback.Run(segment_id_, metadata, kVersion);
   }
 
   void ExecuteModelWithInput(const std::vector<float>& inputs,
@@ -63,9 +62,7 @@
 
 class MockModelExecutionManager : public ModelExecutionManager {
  public:
-  MOCK_METHOD(ModelProvider*,
-              GetProvider,
-              (optimization_guide::proto::OptimizationTarget segment_id));
+  MOCK_METHOD(ModelProvider*, GetProvider, (proto::SegmentId segment_id));
 };
 
 }  // namespace
@@ -78,7 +75,7 @@
   void SetUp() override {
     default_manager_ = std::make_unique<DefaultModelManager>(
         &provider_factory_,
-        std::vector<OptimizationTarget>({kTestSegment, kTestSegment2}));
+        std::vector<SegmentId>({kTestSegment, kTestSegment2}));
     segment_database_ = std::make_unique<test::TestSegmentInfoDatabase>();
     execution_service_ = std::make_unique<ExecutionService>();
     auto query_processor =
@@ -104,7 +101,7 @@
   }
 
   void ExpectSegmentResultOnGet(
-      OptimizationTarget segment_id,
+      SegmentId segment_id,
       bool ignore_db_scores,
       SegmentResultProvider::ResultState expected_state,
       absl::optional<int> expected_rank) {
@@ -130,8 +127,7 @@
     wait_for_result.Run();
   }
 
-  void SetSegmentResult(OptimizationTarget segment,
-                        absl::optional<float> score) {
+  void SetSegmentResult(SegmentId segment, absl::optional<float> score) {
     absl::optional<proto::PredictionResult> result;
     if (score) {
       result = proto::PredictionResult();
@@ -146,7 +142,7 @@
     wait_for_save.Run();
   }
 
-  void InitializeMetadata(OptimizationTarget segment_id) {
+  void InitializeMetadata(SegmentId segment_id) {
     segment_database_->FindOrCreateSegment(segment_id)
         ->mutable_model_metadata()
         ->set_result_time_to_live(7);
@@ -274,7 +270,7 @@
 
 TEST_F(SegmentResultProviderTest, DefaultNeedsSignal) {
   SetSegmentResult(kTestSegment, absl::nullopt);
-  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
   p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
 
@@ -292,7 +288,7 @@
 
 TEST_F(SegmentResultProviderTest, DefaultModelFailedExecution) {
   SetSegmentResult(kTestSegment, absl::nullopt);
-  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
   p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
 
@@ -313,7 +309,7 @@
 
 TEST_F(SegmentResultProviderTest, GetFromDefault) {
   SetSegmentResult(kTestSegment, absl::nullopt);
-  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
   p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
 
@@ -334,7 +330,7 @@
   InitializeMetadata(kTestSegment2);
   SetSegmentResult(kTestSegment2, kDatabaseScore);
 
-  std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
+  std::map<SegmentId, std::unique_ptr<ModelProvider>> p;
   p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
   p.emplace(kTestSegment2, std::make_unique<TestModelProvider>(kTestSegment2));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
diff --git a/components/segmentation_platform/internal/selection/segment_score_provider.cc b/components/segmentation_platform/internal/selection/segment_score_provider.cc
index 6c05a9bc..df50bac 100644
--- a/components/segmentation_platform/internal/selection/segment_score_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_score_provider.cc
@@ -30,7 +30,7 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
-  void GetSegmentScore(OptimizationTarget segment_id,
+  void GetSegmentScore(SegmentId segment_id,
                        SegmentScoreCallback callback) override {
     DCHECK(initialized_);
 
@@ -49,7 +49,7 @@
       std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> all_segments) {
     // Read results from last session to memory.
     for (const auto& pair : *all_segments) {
-      OptimizationTarget id = pair.first;
+      SegmentId id = pair.first;
       const proto::SegmentInfo& info = pair.second;
       if (!info.has_prediction_result())
         continue;
@@ -67,7 +67,7 @@
 
   // Model scores that are read from db on startup and used for serving the
   // clients in the current session.
-  std::map<OptimizationTarget, float> scores_last_session_;
+  std::map<SegmentId, float> scores_last_session_;
 
   // Whether the initialization is complete through an Initialize call.
   bool initialized_{false};
diff --git a/components/segmentation_platform/internal/selection/segment_score_provider.h b/components/segmentation_platform/internal/selection/segment_score_provider.h
index dc69412..792809d 100644
--- a/components/segmentation_platform/internal/selection/segment_score_provider.h
+++ b/components/segmentation_platform/internal/selection/segment_score_provider.h
@@ -6,13 +6,13 @@
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_SELECTION_SEGMENT_SCORE_PROVIDER_H_
 
 #include "base/callback.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 class SegmentInfoDatabase;
 
 // Result of a single segment.
@@ -50,7 +50,7 @@
   // from the last session.
   // Note that there is no strong reason to keep this async, feel free to change
   // this to sync if needed.
-  virtual void GetSegmentScore(OptimizationTarget segment_id,
+  virtual void GetSegmentScore(SegmentId segment_id,
                                SegmentScoreCallback callback) = 0;
 };
 
diff --git a/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc b/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc
index 8fa0073..384ecc94 100644
--- a/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_score_provider_unittest.cc
@@ -29,7 +29,7 @@
         SegmentScoreProvider::Create(segment_database_.get());
   }
 
-  void InitializeMetadataForSegment(OptimizationTarget segment_id,
+  void InitializeMetadataForSegment(SegmentId segment_id,
                                     float mapping[][2],
                                     int num_mapping_pairs) {
     auto* metadata = segment_database_->FindOrCreateSegment(segment_id)
@@ -43,8 +43,7 @@
         segment_id, mapping, num_mapping_pairs, default_mapping_key);
   }
 
-  void GetSegmentScore(OptimizationTarget segment_id,
-                       const SegmentScore& expected) {
+  void GetSegmentScore(SegmentId segment_id, const SegmentScore& expected) {
     base::RunLoop loop;
     single_segment_manager_->GetSegmentScore(
         segment_id,
@@ -66,8 +65,7 @@
 };
 
 TEST_F(SegmentScoreProviderTest, GetSegmentScore) {
-  OptimizationTarget segment_id1 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  SegmentId segment_id1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
   InitializeMetadataForSegment(segment_id1, mapping1, 3);
   segment_database_->AddPredictionResult(segment_id1, 0.6, base::Time::Now());
@@ -86,7 +84,7 @@
   GetSegmentScore(segment_id1, expected);
 
   // Returns empty results when called on a segment with no scores.
-  GetSegmentScore(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
+  GetSegmentScore(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
                   SegmentScore());
 }
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector.h b/components/segmentation_platform/internal/selection/segment_selector.h
index e1a4d646..a601823 100644
--- a/components/segmentation_platform/internal/selection/segment_selector.h
+++ b/components/segmentation_platform/internal/selection/segment_selector.h
@@ -7,15 +7,15 @@
 
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 struct InputContext;
 struct SegmentSelectionResult;
 class ExecutionService;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index 6a53679..fa039feb 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -20,6 +20,7 @@
 #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/segment_id_convertor.h"
 #include "components/segmentation_platform/internal/selection/experimental_group_recorder.h"
 #include "components/segmentation_platform/internal/selection/segment_result_provider.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
@@ -66,7 +67,7 @@
 
 }  // namespace
 
-using optimization_guide::proto::OptimizationTarget_Name;
+using proto::SegmentId_Name;
 
 SegmentSelectorImpl::SegmentSelectorImpl(
     SegmentInfoDatabase* segment_database,
@@ -111,14 +112,15 @@
       stats::SegmentationKeyToTrialName(config_->segmentation_key);
   std::string group_name;
   if (selected_segment.has_value()) {
-    selected_segment_last_session_.segment = selected_segment->segment_id;
+    selected_segment_last_session_.segment =
+        SegmentIdToOptimizationTarget(selected_segment->segment_id);
     selected_segment_last_session_.is_ready = true;
     stats::RecordSegmentSelectionFailure(
         config_->segmentation_key,
         stats::SegmentationSelectionFailureReason::kSelectionAvailableInPrefs);
 
     group_name = stats::OptimizationTargetToSegmentGroupName(
-        *selected_segment_last_session_.segment);
+        selected_segment->segment_id);
   } else {
     stats::RecordSegmentSelectionFailure(
         config_->segmentation_key, stats::SegmentationSelectionFailureReason::
@@ -148,7 +150,7 @@
   // TODO(ssid): Store the scores in prefs so that this can be recorded earlier
   // in startup.
   if (selected_segment_last_session_.is_ready) {
-    for (const OptimizationTarget segment_id : config_->segment_ids) {
+    for (const SegmentId segment_id : config_->segment_ids) {
       experimental_group_recorder_.emplace_back(
           std::make_unique<ExperimentalGroupRecorder>(
               segment_database_, field_trial_register_,
@@ -176,8 +178,7 @@
                         std::move(callback));
 }
 
-void SegmentSelectorImpl::OnModelExecutionCompleted(
-    OptimizationTarget segment_id) {
+void SegmentSelectorImpl::OnModelExecutionCompleted(SegmentId segment_id) {
   DCHECK(segment_result_provider_);
 
   // If the |segment_id| is not in config, then skip any updates early.
@@ -196,7 +197,7 @@
       result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
   if (previous_selection.has_value()) {
     bool was_unknown_selected = previous_selection->segment_id ==
-                                OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+                                SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
     base::TimeDelta ttl_to_use = was_unknown_selected
                                      ? config_->unknown_selection_ttl
                                      : config_->segment_selection_ttl;
@@ -206,7 +207,7 @@
           config_->segmentation_key,
           stats::SegmentationSelectionFailureReason::kSelectionTtlNotExpired);
       VLOG(1) << __func__ << ": previous selection of segment="
-              << OptimizationTarget_Name(previous_selection->segment_id)
+              << SegmentId_Name(previous_selection->segment_id)
               << " has not yet expired.";
       return false;
     }
@@ -227,7 +228,7 @@
     std::unique_ptr<SegmentRanks> ranks,
     scoped_refptr<InputContext> input_context,
     SegmentSelectionCallback callback) {
-  for (OptimizationTarget needed_segment : config_->segment_ids) {
+  for (SegmentId needed_segment : config_->segment_ids) {
     if (ranks->count(needed_segment) == 0) {
       SegmentResultProvider::GetResultOptions options;
       options.segment_id = needed_segment;
@@ -244,12 +245,12 @@
   }
 
   // Finished fetching ranks for all segments.
-  OptimizationTarget selected_segment = FindBestSegment(*ranks);
+  SegmentId selected_segment = FindBestSegment(*ranks);
   if (config_->on_demand_execution) {
     DCHECK(!callback.is_null());
     SegmentSelectionResult result;
     result.is_ready = true;
-    result.segment = selected_segment;
+    result.segment = SegmentIdToOptimizationTarget(selected_segment);
     std::move(callback).Run(result);
   } else {
     DCHECK(callback.is_null());
@@ -261,7 +262,7 @@
     std::unique_ptr<SegmentRanks> ranks,
     scoped_refptr<InputContext> input_context,
     SegmentSelectionCallback callback,
-    OptimizationTarget current_segment_id,
+    SegmentId current_segment_id,
     std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
   if (!result->rank) {
     stats::RecordSegmentSelectionFailure(config_->segmentation_key,
@@ -273,15 +274,14 @@
   GetRankForNextSegment(std::move(ranks), input_context, std::move(callback));
 }
 
-OptimizationTarget SegmentSelectorImpl::FindBestSegment(
+SegmentId SegmentSelectorImpl::FindBestSegment(
     const SegmentRanks& segment_results) {
   int max_rank = 0;
-  OptimizationTarget max_rank_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+  SegmentId max_rank_id = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
   // 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;
+    SegmentId id = pair.first;
     int rank = pair.second;
     if (rank > max_rank) {
       max_rank = rank;
@@ -294,10 +294,9 @@
   return max_rank_id;
 }
 
-void SegmentSelectorImpl::UpdateSelectedSegment(
-    OptimizationTarget new_selection) {
-  VLOG(1) << __func__ << ": Updating selected segment="
-          << OptimizationTarget_Name(new_selection);
+void SegmentSelectorImpl::UpdateSelectedSegment(SegmentId new_selection) {
+  VLOG(1) << __func__
+          << ": Updating selected segment=" << SegmentId_Name(new_selection);
   const auto& previous_selection =
       result_prefs_->ReadSegmentationResultFromPref(config_->segmentation_key);
 
@@ -310,7 +309,7 @@
     skip_updating_prefs = new_selection == previous_selection->segment_id;
     skip_updating_prefs |=
         config_->unknown_selection_ttl == base::TimeDelta() &&
-        new_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+        new_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
     // TODO(shaktisahu): Use segment selection inertia.
   }
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h
index 1835f5f3..f40d020 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.h
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -62,11 +62,11 @@
 
   // Helper function to update the selected segment in the prefs. Auto-extends
   // the selection if the new result is unknown.
-  virtual void UpdateSelectedSegment(OptimizationTarget new_selection);
+  virtual void UpdateSelectedSegment(SegmentId new_selection);
 
   // Called whenever a model eval completes. Runs segment selection to find the
   // best segment, and writes it to the pref.
-  void OnModelExecutionCompleted(OptimizationTarget segment_id) override;
+  void OnModelExecutionCompleted(SegmentId segment_id) override;
 
   void set_segment_result_provider_for_testing(
       std::unique_ptr<SegmentResultProvider> result_provider) {
@@ -77,7 +77,7 @@
   // For testing.
   friend class SegmentSelectorTest;
 
-  using SegmentRanks = base::flat_map<OptimizationTarget, int>;
+  using SegmentRanks = base::flat_map<SegmentId, int>;
 
   // Determines whether segment selection can be run based on whether the
   // segment selection TTL has expired, or selection is unavailable.
@@ -98,13 +98,13 @@
       std::unique_ptr<SegmentRanks> ranks,
       scoped_refptr<InputContext> input_context,
       SegmentSelectionCallback callback,
-      OptimizationTarget current_segment_id,
+      SegmentId 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 rank.
   // Ignores the segments that have no results.
-  OptimizationTarget FindBestSegment(const SegmentRanks& segment_scores);
+  SegmentId FindBestSegment(const SegmentRanks& segment_scores);
 
   std::unique_ptr<SegmentResultProvider> segment_result_provider_;
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
index 27aa9a6..8e598b44 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/segmentation_platform/internal/execution/mock_model_provider.h"
 #include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/metric_filter_utils.h"
+#include "components/segmentation_platform/internal/segment_id_convertor.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/field_trial_register.h"
@@ -41,7 +42,7 @@
 
   MOCK_METHOD3(RegisterSubsegmentFieldTrialIfNeeded,
                void(base::StringPiece trial_name,
-                    optimization_guide::proto::OptimizationTarget segment_id,
+                    proto::SegmentId segment_id,
                     int subsegment_rank));
 };
 
@@ -50,9 +51,8 @@
   config.segmentation_key = "test_key";
   config.segment_selection_ttl = base::Days(28);
   config.unknown_selection_ttl = base::Days(14);
-  config.segment_ids = {
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
+  config.segment_ids = {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+                        SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE};
   return config;
 }
 
@@ -116,7 +116,7 @@
     std::move(closure).Run();
   }
 
-  void InitializeMetadataForSegment(OptimizationTarget segment_id,
+  void InitializeMetadataForSegment(SegmentId segment_id,
                                     float mapping[][2],
                                     int num_mapping_pairs) {
     EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
@@ -130,7 +130,7 @@
         segment_id, mapping, num_mapping_pairs, config_.segmentation_key);
   }
 
-  void CompleteModelExecution(OptimizationTarget segment_id, float score) {
+  void CompleteModelExecution(SegmentId segment_id, float score) {
     segment_database_->AddPredictionResult(segment_id, score, clock_.Now());
     segment_selector_->OnModelExecutionCompleted(segment_id);
     task_environment_.RunUntilIdle();
@@ -154,13 +154,11 @@
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillRepeatedly(Return(true));
 
-  OptimizationTarget segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   float mapping[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
   InitializeMetadataForSegment(segment_id, mapping, 3);
 
-  OptimizationTarget segment_id2 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  SegmentId segment_id2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
   float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
   InitializeMetadataForSegment(segment_id2, mapping2, 2);
 
@@ -181,13 +179,13 @@
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillRepeatedly(Return(true));
 
-  static constexpr OptimizationTarget kSegmentId =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  static constexpr SegmentId kSegmentId =
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   float mapping[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
   InitializeMetadataForSegment(kSegmentId, mapping, 3);
 
-  static constexpr OptimizationTarget kSegmentId2 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  static constexpr SegmentId kSegmentId2 =
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
   float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
   InitializeMetadataForSegment(kSegmentId2, mapping2, 2);
 
@@ -214,7 +212,8 @@
       base::BindOnce(
           [](base::OnceClosure quit, const SegmentSelectionResult& result) {
             EXPECT_TRUE(result.is_ready);
-            EXPECT_EQ(kSegmentId2, result.segment);
+            EXPECT_EQ(kSegmentId2,
+                      OptimizationTargetToSegmentId(*result.segment));
             std::move(quit).Run();
           },
           wait_for_selection.QuitClosure()));
@@ -227,13 +226,11 @@
   SetUpWithConfig(config);
 
   // Setup test with two models.
-  OptimizationTarget segment_id1 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  SegmentId segment_id1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}, {0.8, 5}};
   InitializeMetadataForSegment(segment_id1, mapping1, 4);
 
-  OptimizationTarget segment_id2 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  SegmentId segment_id2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
   float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
   InitializeMetadataForSegment(segment_id2, mapping2, 2);
 
@@ -247,13 +244,13 @@
 
   CompleteModelExecution(segment_id2, 0.1);
   ASSERT_TRUE(prefs_->selection.has_value());
-  ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
             prefs_->selection->segment_id);
 
   // Model 1 completes with a good score. Model 2 results are expired.
   clock_.Advance(config_.segment_selection_ttl * 1.2f);
   CompleteModelExecution(segment_id1, 0.6);
-  ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
             prefs_->selection->segment_id);
 
   // Model 2 gets fresh results. Now segment selection will update.
@@ -289,12 +286,11 @@
 TEST_F(SegmentSelectorTest, UnknownSegmentTtlExpiryForBooleanModel) {
   Config config = CreateTestConfig();
   config.segment_ids = {
-      OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID};
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID};
   SetUpWithConfig(config);
 
-  OptimizationTarget segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID;
+  SegmentId segment_id =
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID;
   float mapping[][2] = {{0.7, 1}};
   InitializeMetadataForSegment(segment_id, mapping, 1);
 
@@ -304,42 +300,39 @@
   // Set a value less than 1 and result should be UNKNOWN.
   CompleteModelExecution(segment_id, 0);
   ASSERT_TRUE(prefs_->selection.has_value());
-  ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
             prefs_->selection->segment_id);
 
   // Advance by less than UNKNOWN segment TTL and result should not change,
   // UNKNOWN segment TTL is less than selection TTL.
   clock_.Advance(config_.unknown_selection_ttl * 0.8f);
   CompleteModelExecution(segment_id, 0.9);
-  ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
             prefs_->selection->segment_id);
 
   // Advance clock so that the time is between UNKNOWN segment TTL and selection
   // TTL.
   clock_.Advance(config_.unknown_selection_ttl * 0.4f);
   CompleteModelExecution(segment_id, 0.9);
-  ASSERT_EQ(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
-      prefs_->selection->segment_id);
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+            prefs_->selection->segment_id);
 
   // Advance by more than UNKNOWN segment TTL and result should not change.
   clock_.Advance(config_.unknown_selection_ttl * 1.2f);
   CompleteModelExecution(segment_id, 0);
-  ASSERT_EQ(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
-      prefs_->selection->segment_id);
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+            prefs_->selection->segment_id);
 
   // Advance by segment selection TTL and result should change.
   clock_.Advance(config_.segment_selection_ttl * 1.2f);
   CompleteModelExecution(segment_id, 0);
-  ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
             prefs_->selection->segment_id);
 }
 
 TEST_F(SegmentSelectorTest, DoesNotMeetSignalCollectionRequirement) {
   SetUpWithConfig(CreateTestConfig());
-  OptimizationTarget segment_id1 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  SegmentId segment_id1 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   float mapping1[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}, {0.8, 5}};
 
   segment_database_->FindOrCreateSegment(segment_id1)
@@ -361,10 +354,8 @@
   SetUpWithConfig(CreateTestConfig());
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillRepeatedly(Return(true));
-  OptimizationTarget segment_id0 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
-  OptimizationTarget segment_id1 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  SegmentId segment_id0 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  SegmentId segment_id1 = SegmentId::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);
@@ -384,7 +375,7 @@
   segment_selector_->OnPlatformInitialized(nullptr);
 
   SegmentSelectionResult result;
-  result.segment = segment_id0;
+  result.segment = SegmentIdToOptimizationTarget(segment_id0);
   result.is_ready = true;
   GetSelectedSegment(result);
   ASSERT_EQ(result, segment_selector_->GetCachedSegmentResult());
@@ -410,13 +401,11 @@
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillRepeatedly(Return(true));
 
-  OptimizationTarget segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   float mapping[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
   InitializeMetadataForSegment(segment_id, mapping, 3);
 
-  OptimizationTarget segment_id2 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  SegmentId segment_id2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
   float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
   InitializeMetadataForSegment(segment_id2, mapping2, 2);
 
@@ -436,8 +425,8 @@
 }
 
 TEST_F(SegmentSelectorTest, SubsegmentRecording) {
-  const OptimizationTarget kSubsegmentEnabledTarget =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER;
+  const SegmentId kSubsegmentEnabledTarget =
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER;
 
   // Create config with Feed segment.
   Config config = CreateTestConfig();
@@ -450,8 +439,7 @@
   SetUpWithConfig(config);
 
   // Store model metadata, model scores and selection results.
-  OptimizationTarget segment_id0 =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  SegmentId segment_id0 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
   float mapping0[][2] = {{1.0, 0}};
   InitializeMetadataForSegment(segment_id0, mapping0, 1);
   segment_database_->AddPredictionResult(segment_id0, 0.7, clock_.Now());
@@ -495,8 +483,8 @@
               RegisterSubsegmentFieldTrialIfNeeded(
                   base::StringPiece("Segmentation_TestKey_FeedUserSegment"),
                   kSubsegmentEnabledTarget, 3))
-      .WillOnce(Invoke(
-          [&wait_for_subsegment](base::StringPiece, OptimizationTarget, int) {
+      .WillOnce(
+          Invoke([&wait_for_subsegment](base::StringPiece, SegmentId, int) {
             wait_for_subsegment.QuitClosure().Run();
           }));
 
diff --git a/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc b/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
index e5f87e296..91c669c 100644
--- a/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
+++ b/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
@@ -12,7 +12,7 @@
 
 namespace segmentation_platform {
 
-SelectedSegment::SelectedSegment(OptimizationTarget segment_id)
+SelectedSegment::SelectedSegment(SegmentId segment_id)
     : segment_id(segment_id), in_use(false) {}
 
 SelectedSegment::~SelectedSegment() = default;
@@ -57,8 +57,7 @@
   absl::optional<base::Time> selection_time =
       base::ValueToTime(segmentation_result.FindPath("selection_time"));
 
-  SelectedSegment selected_segment(
-      static_cast<OptimizationTarget>(segment_id.value()));
+  SelectedSegment selected_segment(static_cast<SegmentId>(segment_id.value()));
   if (in_use.has_value())
     selected_segment.in_use = in_use.value();
   if (selection_time.has_value())
diff --git a/components/segmentation_platform/internal/selection/segmentation_result_prefs.h b/components/segmentation_platform/internal/selection/segmentation_result_prefs.h
index e966dd7..a48ae879 100644
--- a/components/segmentation_platform/internal/selection/segmentation_result_prefs.h
+++ b/components/segmentation_platform/internal/selection/segmentation_result_prefs.h
@@ -7,24 +7,24 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 class PrefService;
 
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 // Struct containing information about the selected segment. Convenient for
 // reading and writing to prefs.
 struct SelectedSegment {
  public:
-  explicit SelectedSegment(OptimizationTarget segment_id);
+  explicit SelectedSegment(SegmentId segment_id);
   ~SelectedSegment();
 
   // The segment selection result.
-  OptimizationTarget segment_id;
+  SegmentId segment_id;
 
   // The time when the segment was selected.
   base::Time selection_time;
diff --git a/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc b/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc
index c105671..386ee18 100644
--- a/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segmentation_result_prefs_unittest.cc
@@ -39,8 +39,7 @@
   EXPECT_FALSE(current_result.has_value());
 
   // Save a result. Verify by reading the result back.
-  OptimizationTarget segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  SegmentId segment_id = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   SelectedSegment selected_segment(segment_id);
   result_prefs_->SaveSegmentationResultToPref(result_key, selected_segment);
   current_result = result_prefs_->ReadSegmentationResultFromPref(result_key);
@@ -51,7 +50,7 @@
 
   // Overwrite the result with a new segment.
   selected_segment.segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
   selected_segment.in_use = true;
   base::Time now = base::Time::Now();
   selected_segment.selection_time = now;
@@ -66,7 +65,7 @@
   // first key.
   std::string result_key2 = "some_key2";
   selected_segment.segment_id =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
   result_prefs_->SaveSegmentationResultToPref(result_key2, selected_segment);
   current_result = result_prefs_->ReadSegmentationResultFromPref(result_key2);
   EXPECT_TRUE(current_result.has_value());
@@ -74,7 +73,7 @@
 
   current_result = result_prefs_->ReadSegmentationResultFromPref(result_key);
   EXPECT_TRUE(current_result.has_value());
-  EXPECT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+  EXPECT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
             current_result->segment_id);
 
   // Save empty result. It should delete the current result.
@@ -85,7 +84,7 @@
 
 TEST_F(SegmentationResultPrefsTest, CorruptedValue) {
   std::string result_key = "some_key";
-  SelectedSegment selected_segment(static_cast<OptimizationTarget>(100));
+  SelectedSegment selected_segment(static_cast<SegmentId>(100));
   result_prefs_->SaveSegmentationResultToPref(result_key, selected_segment);
   absl::optional<SelectedSegment> current_result =
       result_prefs_->ReadSegmentationResultFromPref(result_key);
diff --git a/components/segmentation_platform/internal/service_proxy_impl.cc b/components/segmentation_platform/internal/service_proxy_impl.cc
index 93e6d829..2bcb0da 100644
--- a/components/segmentation_platform/internal/service_proxy_impl.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl.cc
@@ -14,6 +14,7 @@
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/scheduler/execution_service.h"
+#include "components/segmentation_platform/internal/segment_id_convertor.h"
 #include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
 #include "components/segmentation_platform/internal/selection/segment_selector_impl.h"
 #include "components/segmentation_platform/public/config.h"
@@ -107,9 +108,9 @@
   UpdateObservers(true /* update_service_status */);
 }
 
-void ServiceProxyImpl::ExecuteModel(OptimizationTarget segment_id) {
+void ServiceProxyImpl::ExecuteModel(SegmentId segment_id) {
   if (!execution_service ||
-      segment_id == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+      segment_id == SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     return;
   }
   segment_db_->GetSegmentInfo(
@@ -129,27 +130,26 @@
   execution_service->RequestModelExecution(std::move(request));
 }
 
-void ServiceProxyImpl::OverwriteResult(OptimizationTarget segment_id,
-                                       float result) {
+void ServiceProxyImpl::OverwriteResult(SegmentId segment_id, float result) {
   if (!execution_service)
     return;
 
   if (result < 0 || result > 1)
     return;
 
-  if (segment_id != OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+  if (segment_id != SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     execution_service->OverwriteModelExecutionResult(
         segment_id, std::make_pair(result, ModelExecutionStatus::kSuccess));
   }
 }
 
 void ServiceProxyImpl::SetSelectedSegment(const std::string& segmentation_key,
-                                          OptimizationTarget segment_id) {
+                                          SegmentId segment_id) {
   if (!segment_selectors_ ||
       segment_selectors_->find(segmentation_key) == segment_selectors_->end()) {
     return;
   }
-  if (segment_id != OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+  if (segment_id != SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     auto& selector = segment_selectors_->at(segmentation_key);
     selector->UpdateSelectedSegment(segment_id);
   }
@@ -161,31 +161,30 @@
     return;
 
   // Convert the |segment_info| vector to a map for quick lookup.
-  base::flat_map<OptimizationTarget, proto::SegmentInfo> optimization_targets;
+  base::flat_map<SegmentId, proto::SegmentInfo> segment_ids;
   for (const auto& info : *segment_info) {
-    optimization_targets[info.first] = info.second;
+    segment_ids[info.first] = info.second;
   }
 
   std::vector<ServiceProxy::ClientInfo> result;
   for (const auto& config : *configs_) {
-    OptimizationTarget selected =
-        OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+    SegmentId selected = SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
     if (segment_selectors_ &&
         segment_selectors_->find(config->segmentation_key) !=
             segment_selectors_->end()) {
-      absl::optional<optimization_guide::proto::OptimizationTarget> target =
+      absl::optional<OptimizationTarget> target =
           segment_selectors_->at(config->segmentation_key)
               ->GetCachedSegmentResult()
               .segment;
       if (target.has_value()) {
-        selected = target.value();
+        selected = OptimizationTargetToSegmentId(*target);
       }
     }
     result.emplace_back(config->segmentation_key, selected);
     for (const auto& segment_id : config->segment_ids) {
-      if (!optimization_targets.contains(segment_id))
+      if (!segment_ids.contains(segment_id))
         continue;
-      const auto& info = optimization_targets[segment_id];
+      const auto& info = segment_ids[segment_id];
       result.back().segment_status.emplace_back(
           segment_id, SegmentMetadataToString(info),
           PredictionResultToString(info),
@@ -200,8 +199,7 @@
     obs.OnClientInfoAvailable(result);
 }
 
-void ServiceProxyImpl::OnModelExecutionCompleted(
-    OptimizationTarget segment_id) {
+void ServiceProxyImpl::OnModelExecutionCompleted(SegmentId segment_id) {
   // Update the observers with the new execution results.
   UpdateObservers(false);
 }
diff --git a/components/segmentation_platform/internal/service_proxy_impl.h b/components/segmentation_platform/internal/service_proxy_impl.h
index 96d521f..1d4ea7fc 100644
--- a/components/segmentation_platform/internal/service_proxy_impl.h
+++ b/components/segmentation_platform/internal/service_proxy_impl.h
@@ -12,14 +12,14 @@
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
 #include "components/leveldb_proto/public/proto_database.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/scheduler/model_execution_scheduler.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "components/segmentation_platform/public/service_proxy.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform {
+using proto::SegmentId;
+
 struct Config;
 class SignalStorageConfig;
 class ExecutionService;
@@ -48,10 +48,10 @@
 
   // ServiceProxy impl.
   void GetServiceStatus() override;
-  void ExecuteModel(OptimizationTarget segment_id) override;
-  void OverwriteResult(OptimizationTarget segment_id, float result) override;
+  void ExecuteModel(SegmentId segment_id) override;
+  void OverwriteResult(SegmentId segment_id, float result) override;
   void SetSelectedSegment(const std::string& segmentation_key,
-                          OptimizationTarget segment_id) override;
+                          SegmentId segment_id) override;
 
   // Called when segmentation service status changed.
   void OnServiceStatusChanged(bool is_initialized, int status_flag);
@@ -70,7 +70,7 @@
       std::unique_ptr<SegmentInfoDatabase::SegmentInfoList> segment_info);
 
   // ModelExecutionScheduler::Observer overrides.
-  void OnModelExecutionCompleted(OptimizationTarget segment_id) override;
+  void OnModelExecutionCompleted(SegmentId segment_id) override;
 
   bool is_service_initialized_ = false;
   int service_status_flag_ = 0;
diff --git a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
index 9e2aeb6e..a2acb02 100644
--- a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
@@ -38,7 +38,7 @@
 proto::SegmentInfo AddSegmentInfo(
     std::map<std::string, proto::SegmentInfo>* db_entries,
     Config* config,
-    OptimizationTarget segment_id) {
+    SegmentId segment_id) {
   proto::SegmentInfo info;
   info.set_segment_id(segment_id);
   db_entries->insert(
@@ -55,8 +55,7 @@
   MOCK_METHOD(void, RequestModelExecutionForEligibleSegments, (bool));
   MOCK_METHOD(void,
               OnModelExecutionCompleted,
-              (OptimizationTarget,
-               (const std::pair<float, ModelExecutionStatus>&)));
+              (SegmentId, (const std::pair<float, ModelExecutionStatus>&)));
 };
 
 }  // namespace
@@ -75,14 +74,14 @@
                             nullptr) {}
   ~FakeSegmentSelectorImpl() override = default;
 
-  void UpdateSelectedSegment(OptimizationTarget new_selection) override {
+  void UpdateSelectedSegment(SegmentId new_selection) override {
     new_selection_ = new_selection;
   }
 
-  OptimizationTarget new_selection() const { return new_selection_; }
+  SegmentId new_selection() const { return new_selection_; }
 
  private:
-  OptimizationTarget new_selection_;
+  SegmentId new_selection_;
 };
 
 class ServiceProxyImplTest : public testing::Test,
@@ -169,9 +168,9 @@
 }
 
 TEST_F(ServiceProxyImplTest, GetSegmentationInfoFromDB) {
-  proto::SegmentInfo info = AddSegmentInfo(
-      &db_entries_, configs_.at(0).get(),
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  proto::SegmentInfo info =
+      AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+                     SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   SetUpProxy();
 
   service_proxy_impl_->OnServiceStatusChanged(true, 7);
@@ -181,23 +180,23 @@
   ASSERT_EQ(client_info_.at(0).segment_status.size(), 1u);
   ServiceProxy::SegmentStatus status = client_info_.at(0).segment_status.at(0);
   ASSERT_EQ(status.segment_id,
-            OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+            SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   ASSERT_EQ(status.can_execute_segment, false);
   ASSERT_TRUE(status.segment_metadata.empty());
   ASSERT_TRUE(status.prediction_result.empty());
 }
 
 TEST_F(ServiceProxyImplTest, ExecuteModel) {
-  proto::SegmentInfo info = AddSegmentInfo(
-      &db_entries_, configs_.at(0).get(),
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  proto::SegmentInfo info =
+      AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+                     SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   SetUpProxy();
 
   service_proxy_impl_->OnServiceStatusChanged(true, 7);
   db_->LoadCallback(true);
 
   segment_db_->UpdateSegment(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, info,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, info,
       base::DoNothing());
   db_->UpdateCallback(true);
 
@@ -210,7 +209,7 @@
   // Scheduler is not set, ExecuteModel() will do nothing.
   EXPECT_CALL(*scheduler, RequestModelExecution(_)).Times(0);
   service_proxy_impl_->ExecuteModel(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
 
   service_proxy_impl_->SetExecutionService(&execution);
   base::RunLoop wait_for_execution;
@@ -221,19 +220,18 @@
             wait_for_execution.QuitClosure().Run();
           }));
   service_proxy_impl_->ExecuteModel(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   db_->GetCallback(true);
   wait_for_execution.Run();
 
   EXPECT_CALL(*scheduler, RequestModelExecution(_)).Times(0);
-  service_proxy_impl_->ExecuteModel(
-      OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN);
+  service_proxy_impl_->ExecuteModel(SegmentId::OPTIMIZATION_TARGET_UNKNOWN);
 }
 
 TEST_F(ServiceProxyImplTest, OverwriteResult) {
-  proto::SegmentInfo info = AddSegmentInfo(
-      &db_entries_, configs_.at(0).get(),
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  proto::SegmentInfo info =
+      AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+                     SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   SetUpProxy();
 
   service_proxy_impl_->OnServiceStatusChanged(true, 7);
@@ -248,33 +246,32 @@
   // Scheduler is not set, OverwriteValue() will do nothing.
   EXPECT_CALL(*scheduler, OnModelExecutionCompleted(_, _)).Times(0);
   service_proxy_impl_->OverwriteResult(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
 
   // Test with invalid values.
   service_proxy_impl_->SetExecutionService(&execution);
   EXPECT_CALL(*scheduler, OnModelExecutionCompleted(_, _)).Times(0);
   service_proxy_impl_->OverwriteResult(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 1.1);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 1.1);
   service_proxy_impl_->OverwriteResult(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, -0.1);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, -0.1);
 
-  EXPECT_CALL(
-      *scheduler,
-      OnModelExecutionCompleted(
-          OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, _))
+  EXPECT_CALL(*scheduler,
+              OnModelExecutionCompleted(
+                  SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, _))
       .Times(1);
   service_proxy_impl_->OverwriteResult(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, 0.7);
 
   EXPECT_CALL(*scheduler, OnModelExecutionCompleted(_, _)).Times(0);
-  service_proxy_impl_->OverwriteResult(
-      OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN, 0.7);
+  service_proxy_impl_->OverwriteResult(SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
+                                       0.7);
 }
 
 TEST_F(ServiceProxyImplTest, SetSelectSegment) {
-  proto::SegmentInfo info = AddSegmentInfo(
-      &db_entries_, configs_.at(0).get(),
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  proto::SegmentInfo info =
+      AddSegmentInfo(&db_entries_, configs_.at(0).get(),
+                     SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
   SetUpProxy();
 
   service_proxy_impl_->OnServiceStatusChanged(true, 7);
@@ -282,8 +279,8 @@
 
   service_proxy_impl_->SetSelectedSegment(
       kTestSegmentationKey,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
-  ASSERT_EQ(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB);
+  ASSERT_EQ(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
             static_cast<FakeSegmentSelectorImpl*>(
                 segment_selectors_[kTestSegmentationKey].get())
                 ->new_selection());
diff --git a/components/segmentation_platform/internal/signals/history_service_observer.cc b/components/segmentation_platform/internal/signals/history_service_observer.cc
index 6996e511..4a61939 100644
--- a/components/segmentation_platform/internal/signals/history_service_observer.cc
+++ b/components/segmentation_platform/internal/signals/history_service_observer.cc
@@ -70,8 +70,7 @@
 }
 
 void HistoryServiceObserver::SetHistoryBasedSegments(
-    base::flat_set<optimization_guide::proto::OptimizationTarget>&&
-        history_based_segments) {
+    base::flat_set<proto::SegmentId>&& history_based_segments) {
   history_based_segments_ = std::move(history_based_segments);
   // If a delete is pending, clear the results now.
   if (pending_deletion_based_on_history_based_segments_) {
diff --git a/components/segmentation_platform/internal/signals/history_service_observer.h b/components/segmentation_platform/internal/signals/history_service_observer.h
index 5211e7d..75adbcc 100644
--- a/components/segmentation_platform/internal/signals/history_service_observer.h
+++ b/components/segmentation_platform/internal/signals/history_service_observer.h
@@ -12,7 +12,7 @@
 #include "base/time/time.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_service_observer.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform {
@@ -44,8 +44,7 @@
 
   // Sets the list of segment IDs that are based on history data.
   virtual void SetHistoryBasedSegments(
-      base::flat_set<optimization_guide::proto::OptimizationTarget>&&
-          history_based_segments);
+      base::flat_set<proto::SegmentId>&& history_based_segments);
 
  private:
   void DeleteResultsForHistoryBasedSegments();
@@ -55,8 +54,7 @@
 
   // List of segment IDs that depend on history data, that will be cleared when
   // history is deleted.
-  absl::optional<base::flat_set<optimization_guide::proto::OptimizationTarget>>
-      history_based_segments_;
+  absl::optional<base::flat_set<proto::SegmentId>> history_based_segments_;
   bool pending_deletion_based_on_history_based_segments_ = false;
 
   base::RepeatingClosure models_refresh_callback_;
diff --git a/components/segmentation_platform/internal/signals/signal_filter_processor.cc b/components/segmentation_platform/internal/signals/signal_filter_processor.cc
index 6ddb2c36..32262b0 100644
--- a/components/segmentation_platform/internal/signals/signal_filter_processor.cc
+++ b/components/segmentation_platform/internal/signals/signal_filter_processor.cc
@@ -42,7 +42,7 @@
   std::set<uint64_t> user_actions;
   std::set<std::pair<std::string, proto::SignalType>> histograms;
   UkmConfig ukm_config;
-  base::flat_set<OptimizationTarget> history_based_segments;
+  base::flat_set<SegmentId> history_based_segments;
 
  private:
   void AddUmaFeatures(const proto::SegmentationModelMetadata& metadata) {
@@ -95,7 +95,7 @@
     UserActionSignalHandler* user_action_signal_handler,
     HistogramSignalHandler* histogram_signal_handler,
     HistoryServiceObserver* history_observer,
-    const std::vector<OptimizationTarget>& segment_ids)
+    const std::vector<SegmentId>& segment_ids)
     : storage_service_(storage_service),
       user_action_signal_handler_(user_action_signal_handler),
       histogram_signal_handler_(histogram_signal_handler),
diff --git a/components/segmentation_platform/internal/signals/signal_filter_processor.h b/components/segmentation_platform/internal/signals/signal_filter_processor.h
index 26b4232..ff39f4c 100644
--- a/components/segmentation_platform/internal/signals/signal_filter_processor.h
+++ b/components/segmentation_platform/internal/signals/signal_filter_processor.h
@@ -7,13 +7,13 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/execution/default_model_manager.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 class HistogramSignalHandler;
 class HistoryServiceObserver;
 class StorageService;
@@ -28,7 +28,7 @@
                         UserActionSignalHandler* user_action_signal_handler,
                         HistogramSignalHandler* histogram_signal_handler,
                         HistoryServiceObserver* history_observer,
-                        const std::vector<OptimizationTarget>& segment_ids);
+                        const std::vector<SegmentId>& segment_ids);
   ~SignalFilterProcessor();
 
   // Disallow copy/assign.
@@ -53,7 +53,7 @@
   const raw_ptr<UserActionSignalHandler> user_action_signal_handler_;
   const raw_ptr<HistogramSignalHandler> histogram_signal_handler_;
   const raw_ptr<HistoryServiceObserver> history_observer_;
-  std::vector<OptimizationTarget> segment_ids_;
+  std::vector<SegmentId> segment_ids_;
 
   base::WeakPtrFactory<SignalFilterProcessor> weak_ptr_factory_{this};
 };
diff --git a/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc b/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
index d895da33..126c51c 100644
--- a/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
+++ b/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
@@ -53,21 +53,19 @@
 
 class MockHistoryObserver : public HistoryServiceObserver {
  public:
-  MOCK_METHOD1(
-      SetHistoryBasedSegments,
-      void(base::flat_set<optimization_guide::proto::OptimizationTarget>&&
-               history_based_segments));
+  MOCK_METHOD1(SetHistoryBasedSegments,
+               void(base::flat_set<proto::SegmentId>&& history_based_segments));
 };
 
 // Noop version. For database calls, just passes the calls to the DB.
 class TestDefaultModelManager : public DefaultModelManager {
  public:
   TestDefaultModelManager()
-      : DefaultModelManager(nullptr, std::vector<OptimizationTarget>()) {}
+      : DefaultModelManager(nullptr, std::vector<SegmentId>()) {}
   ~TestDefaultModelManager() override = default;
 
   void GetAllSegmentInfoFromDefaultModel(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       MultipleSegmentInfoCallback callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback),
@@ -75,7 +73,7 @@
   }
 
   void GetAllSegmentInfoFromBothModels(
-      const std::vector<OptimizationTarget>& segment_ids,
+      const std::vector<SegmentId>& segment_ids,
       SegmentInfoDatabase* segment_database,
       MultipleSegmentInfoCallback callback) override {
     segment_database->GetSegmentInfoForSegments(
@@ -106,9 +104,9 @@
     base::SetRecordActionTaskRunner(
         task_environment_.GetMainThreadTaskRunner());
 
-    std::vector<OptimizationTarget> segment_ids(
-        {OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-         OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE});
+    std::vector<SegmentId> segment_ids(
+        {SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+         SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE});
     user_action_signal_handler_ =
         std::make_unique<MockUserActionSignalHandler>();
     histogram_signal_handler_ = std::make_unique<MockHistogramSignalHandler>();
@@ -144,12 +142,12 @@
 TEST_F(SignalFilterProcessorTest, UserActionRegistrationFlow) {
   std::string kUserActionName1 = "some_action_1";
   segment_database_->AddUserActionFeature(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      kUserActionName1, 0, 0, proto::Aggregation::COUNT);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, kUserActionName1, 0,
+      0, proto::Aggregation::COUNT);
   std::string kUserActionName2 = "some_action_2";
   segment_database_->AddUserActionFeature(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-      kUserActionName2, 0, 0, proto::Aggregation::COUNT);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, kUserActionName2, 0, 0,
+      proto::Aggregation::COUNT);
 
   std::set<uint64_t> actions;
   EXPECT_CALL(*user_action_signal_handler_, SetRelevantUserActions(_))
@@ -165,16 +163,16 @@
 TEST_F(SignalFilterProcessorTest, HistogramRegistrationFlow) {
   std::string kHistogramName1 = "some_histogram_1";
   segment_database_->AddHistogramValueFeature(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      kHistogramName1, 1, 1, proto::Aggregation::COUNT);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, kHistogramName1, 1,
+      1, proto::Aggregation::COUNT);
   std::string kHistogramName2 = "some_histogram_2";
   segment_database_->AddHistogramValueFeature(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-      kHistogramName2, 1, 1, proto::Aggregation::COUNT);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, kHistogramName2, 1, 1,
+      proto::Aggregation::COUNT);
   std::string kHistogramName3 = "some_histogram_3";
   segment_database_->AddHistogramEnumFeature(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-      kHistogramName3, 1, 1, proto::Aggregation::COUNT, {3, 4});
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, kHistogramName3, 1, 1,
+      proto::Aggregation::COUNT, {3, 4});
 
   std::set<std::pair<std::string, proto::SignalType>> histograms;
   EXPECT_CALL(*histogram_signal_handler_, SetRelevantHistograms(_))
@@ -195,8 +193,8 @@
 }
 
 TEST_F(SignalFilterProcessorTest, UkmMetricsConfig) {
-  const OptimizationTarget kSegmentId =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  const SegmentId kSegmentId =
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
   const UkmEventHash kEvent1 = TestEvent(10);
   const UkmEventHash kEvent2 = TestEvent(11);
   const std::array<UkmMetricHash, 3> kMetrics1_1{
@@ -260,8 +258,7 @@
         EXPECT_EQ(actual_config, config2);
       }));
   EXPECT_CALL(*history_observer_,
-              SetHistoryBasedSegments(
-                  base::flat_set<OptimizationTarget>({kSegmentId})));
+              SetHistoryBasedSegments(base::flat_set<SegmentId>({kSegmentId})));
   EXPECT_CALL(*signal_storage_config_, OnSignalCollectionStarted(_));
   signal_filter_processor_->OnSignalListUpdated();
 }
diff --git a/components/segmentation_platform/internal/signals/signal_handler.cc b/components/segmentation_platform/internal/signals/signal_handler.cc
index 4a99703..42e9a80 100644
--- a/components/segmentation_platform/internal/signals/signal_handler.cc
+++ b/components/segmentation_platform/internal/signals/signal_handler.cc
@@ -16,12 +16,10 @@
 SignalHandler::SignalHandler() = default;
 SignalHandler::~SignalHandler() = default;
 
-void SignalHandler::Initialize(
-    StorageService* storage_service,
-    history::HistoryService* history_service,
-    const std::vector<optimization_guide::proto::OptimizationTarget>&
-        segment_ids,
-    base::RepeatingClosure models_refresh_callback) {
+void SignalHandler::Initialize(StorageService* storage_service,
+                               history::HistoryService* history_service,
+                               const std::vector<proto::SegmentId>& segment_ids,
+                               base::RepeatingClosure models_refresh_callback) {
   user_action_signal_handler_ = std::make_unique<UserActionSignalHandler>(
       storage_service->signal_database());
   histogram_signal_handler_ = std::make_unique<HistogramSignalHandler>(
diff --git a/components/segmentation_platform/internal/signals/signal_handler.h b/components/segmentation_platform/internal/signals/signal_handler.h
index 77072aa..ac05648 100644
--- a/components/segmentation_platform/internal/signals/signal_handler.h
+++ b/components/segmentation_platform/internal/signals/signal_handler.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace history {
 class HistoryService;
@@ -33,12 +33,10 @@
   SignalHandler(SignalHandler&) = delete;
   SignalHandler& operator=(SignalHandler&) = delete;
 
-  void Initialize(
-      StorageService* storage_service,
-      history::HistoryService* history_service,
-      const std::vector<optimization_guide::proto::OptimizationTarget>&
-          segment_ids,
-      base::RepeatingClosure model_refresh_callback);
+  void Initialize(StorageService* storage_service,
+                  history::HistoryService* history_service,
+                  const std::vector<proto::SegmentId>& segment_ids,
+                  base::RepeatingClosure model_refresh_callback);
 
   void TearDown();
 
diff --git a/components/segmentation_platform/internal/signals/ukm_observer.cc b/components/segmentation_platform/internal/signals/ukm_observer.cc
index 7e7f2707d..3d8eb1c 100644
--- a/components/segmentation_platform/internal/signals/ukm_observer.cc
+++ b/components/segmentation_platform/internal/signals/ukm_observer.cc
@@ -15,11 +15,9 @@
 
 namespace segmentation_platform {
 
-UkmObserver::UkmObserver(ukm::UkmRecorderImpl* ukm_recorder,
-                         bool is_ukm_allowed)
+UkmObserver::UkmObserver(ukm::UkmRecorderImpl* ukm_recorder)
     : ukm_recorder_(ukm_recorder), ukm_data_manager_(nullptr) {
   // Listen to |OnUkmAllowedStateChanged| event.
-  OnUkmAllowedStateChanged(is_ukm_allowed);
   ukm_recorder_->AddUkmRecorderObserver(base::flat_set<uint64_t>(), this);
 }
 
diff --git a/components/segmentation_platform/internal/signals/ukm_observer.h b/components/segmentation_platform/internal/signals/ukm_observer.h
index 86c4c77..03e318c 100644
--- a/components/segmentation_platform/internal/signals/ukm_observer.h
+++ b/components/segmentation_platform/internal/signals/ukm_observer.h
@@ -25,7 +25,7 @@
 // entries or on source URL changes.
 class UkmObserver : public ukm::UkmRecorderObserver {
  public:
-  UkmObserver(ukm::UkmRecorderImpl* ukm_recorder, bool is_ukm_allowed);
+  explicit UkmObserver(ukm::UkmRecorderImpl* ukm_recorder);
   ~UkmObserver() override;
 
   UkmObserver(UkmObserver&) = delete;
diff --git a/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc b/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc
index 70934b0a..6172adb 100644
--- a/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc
+++ b/components/segmentation_platform/internal/signals/ukm_observer_unittest.cc
@@ -94,8 +94,8 @@
   }
 
   void InitializeUkmObserver(bool is_ukm_allowed) {
-    ukm_observer_ =
-        std::make_unique<UkmObserver>(ukm_recorder_.get(), is_ukm_allowed);
+    ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get());
+    ukm_observer_->OnUkmAllowedStateChanged(is_ukm_allowed);
     auto ukm_database = std::make_unique<MockUkmDatabase>();
     ukm_database_ = ukm_database.get();
     ukm_data_manager_ = std::make_unique<UkmDataManagerImpl>();
diff --git a/components/segmentation_platform/internal/stats.cc b/components/segmentation_platform/internal/stats.cc
index d8295d8..07a91488 100644
--- a/components/segmentation_platform/internal/stats.cc
+++ b/components/segmentation_platform/internal/stats.cc
@@ -9,9 +9,9 @@
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
 #include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform::stats {
 namespace {
@@ -27,7 +27,7 @@
 };
 
 // This is the segmentation subset of
-// optimization_guide::proto::OptimizationTarget.
+// proto::SegmentId.
 // Keep in sync with SegmentationPlatformSegmenationModel in
 // //tools/metrics/histograms/enums.xml.
 // See also SegmentationModel variant in
@@ -46,15 +46,15 @@
 };
 
 AdaptiveToolbarButtonVariant OptimizationTargetToAdaptiveToolbarButtonVariant(
-    OptimizationTarget segment_id) {
+    SegmentId segment_id) {
   switch (segment_id) {
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
       return AdaptiveToolbarButtonVariant::kNewTab;
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
       return AdaptiveToolbarButtonVariant::kShare;
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
       return AdaptiveToolbarButtonVariant::kVoice;
-    case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+    case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
       return AdaptiveToolbarButtonVariant::kNone;
     default:
       return AdaptiveToolbarButtonVariant::kUnknown;
@@ -70,70 +70,68 @@
          segmentation_key == kFeedUserSegmentationKey;
 }
 
-BooleanSegmentSwitch GetBooleanSegmentSwitch(
-    OptimizationTarget new_selection,
-    OptimizationTarget previous_selection) {
-  if (new_selection != OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN &&
-      previous_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+BooleanSegmentSwitch GetBooleanSegmentSwitch(SegmentId new_selection,
+                                             SegmentId previous_selection) {
+  if (new_selection != SegmentId::OPTIMIZATION_TARGET_UNKNOWN &&
+      previous_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     return BooleanSegmentSwitch::kNoneToEnabled;
-  } else if (new_selection == OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN &&
-             previous_selection !=
-                 OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN) {
+  } else if (new_selection == SegmentId::OPTIMIZATION_TARGET_UNKNOWN &&
+             previous_selection != SegmentId::OPTIMIZATION_TARGET_UNKNOWN) {
     return BooleanSegmentSwitch::kEnabledToNone;
   }
   return BooleanSegmentSwitch::kUnknown;
 }
 
 AdaptiveToolbarSegmentSwitch GetAdaptiveToolbarSegmentSwitch(
-    OptimizationTarget new_selection,
-    OptimizationTarget previous_selection) {
+    SegmentId new_selection,
+    SegmentId previous_selection) {
   switch (previous_selection) {
-    case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+    case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
       switch (new_selection) {
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
           return AdaptiveToolbarSegmentSwitch::kNoneToNewTab;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
           return AdaptiveToolbarSegmentSwitch::kNoneToShare;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
           return AdaptiveToolbarSegmentSwitch::kNoneToVoice;
         default:
           NOTREACHED();
           return AdaptiveToolbarSegmentSwitch::kUnknown;
       }
 
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
       switch (new_selection) {
-        case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+        case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
           return AdaptiveToolbarSegmentSwitch::kNewTabToNone;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
           return AdaptiveToolbarSegmentSwitch::kNewTabToShare;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
           return AdaptiveToolbarSegmentSwitch::kNewTabToVoice;
         default:
           NOTREACHED();
           return AdaptiveToolbarSegmentSwitch::kUnknown;
       }
 
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
       switch (new_selection) {
-        case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+        case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
           return AdaptiveToolbarSegmentSwitch::kShareToNone;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
           return AdaptiveToolbarSegmentSwitch::kShareToNewTab;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
           return AdaptiveToolbarSegmentSwitch::kShareToVoice;
         default:
           NOTREACHED();
           return AdaptiveToolbarSegmentSwitch::kUnknown;
       }
 
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
       switch (new_selection) {
-        case OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN:
+        case SegmentId::OPTIMIZATION_TARGET_UNKNOWN:
           return AdaptiveToolbarSegmentSwitch::kVoiceToNone;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
           return AdaptiveToolbarSegmentSwitch::kVoiceToNewTab;
-        case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+        case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
           return AdaptiveToolbarSegmentSwitch::kVoiceToShare;
         default:
           NOTREACHED();
@@ -146,26 +144,23 @@
   }
 }
 
-SegmentationModel OptimizationTargetToSegmentationModel(
-    OptimizationTarget segment_id) {
+SegmentationModel OptimizationTargetToSegmentationModel(SegmentId segment_id) {
   switch (segment_id) {
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
       return SegmentationModel::kNewTab;
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
       return SegmentationModel::kShare;
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
       return SegmentationModel::kVoice;
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
       return SegmentationModel::kDummy;
-    case OptimizationTarget::
-        OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
       return SegmentationModel::kChromeStartAndroid;
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
       return SegmentationModel::kQueryTiles;
-    case OptimizationTarget::
-        OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
       return SegmentationModel::kChromeLowUserEngagement;
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
       return SegmentationModel::kFeedUserSegment;
     default:
       return SegmentationModel::kUnknown;
@@ -226,26 +221,23 @@
 
 // Should map to SegmentationModel variant in
 // //tools/metrics/histograms/metadata/segmentation_platform/histograms.xml.
-std::string OptimizationTargetToHistogramVariant(
-    OptimizationTarget segment_id) {
+std::string OptimizationTargetToHistogramVariant(SegmentId segment_id) {
   switch (segment_id) {
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
       return "NewTab";
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
       return "Share";
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
       return "Voice";
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
       return "Dummy";
-    case OptimizationTarget::
-        OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
       return "ChromeStartAndroid";
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
       return "QueryTiles";
-    case OptimizationTarget::
-        OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
       return "ChromeLowUserEngagement";
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
       return "FeedUserSegment";
     default:
       return "Other";
@@ -274,13 +266,13 @@
   return "Unknown";
 }
 
-void RecordModelScore(OptimizationTarget segment_id, float score) {
+void RecordModelScore(SegmentId segment_id, float score) {
   // Special case adaptive toolbar models since it already has histograms being
   // recorded and updating names will affect current work.
   switch (segment_id) {
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
       base::UmaHistogramPercentage(
           "SegmentationPlatform.AdaptiveToolbar.ModelScore." +
               OptimizationTargetToHistogramVariant(segment_id),
@@ -291,16 +283,14 @@
   }
 
   switch (segment_id) {
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
-    case OptimizationTarget::
-        OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
-    case OptimizationTarget::
-        OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
-    case OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT:
+    case SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER:
       // Assumes all models return score between 0 and 1. This is true for all
       // the models we have currently.
       base::UmaHistogramPercentage(
@@ -315,8 +305,8 @@
 
 void RecordSegmentSelectionComputed(
     const std::string& segmentation_key,
-    OptimizationTarget new_selection,
-    absl::optional<OptimizationTarget> previous_selection) {
+    SegmentId new_selection,
+    absl::optional<SegmentId> previous_selection) {
   // Special case adaptive toolbar since it already has histograms being
   // recorded and updating names will affect current work.
   if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
@@ -330,10 +320,9 @@
   base::UmaHistogramEnumeration(
       computed_hist, OptimizationTargetToSegmentationModel(new_selection));
 
-  OptimizationTarget prev_segment =
-      previous_selection.has_value()
-          ? previous_selection.value()
-          : OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+  SegmentId prev_segment = previous_selection.has_value()
+                               ? previous_selection.value()
+                               : SegmentId::OPTIMIZATION_TARGET_UNKNOWN;
 
   if (prev_segment == new_selection)
     return;
@@ -371,15 +360,14 @@
       "SegmentationPlatform.Maintenance.SignalIdentifierCount", count);
 }
 
-void RecordModelDeliveryHasMetadata(OptimizationTarget segment_id,
-                                    bool has_metadata) {
+void RecordModelDeliveryHasMetadata(SegmentId segment_id, bool has_metadata) {
   base::UmaHistogramBoolean(
       "SegmentationPlatform.ModelDelivery.HasMetadata." +
           OptimizationTargetToHistogramVariant(segment_id),
       has_metadata);
 }
 
-void RecordModelDeliveryMetadataFeatureCount(OptimizationTarget segment_id,
+void RecordModelDeliveryMetadataFeatureCount(SegmentId segment_id,
                                              size_t count) {
   base::UmaHistogramCounts1000(
       "SegmentationPlatform.ModelDelivery.Metadata.FeatureCount." +
@@ -388,7 +376,7 @@
 }
 
 void RecordModelDeliveryMetadataValidation(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     bool processed,
     metadata_utils::ValidationResult validation_result) {
   // Should map to ValidationPhase variant string in
@@ -401,37 +389,34 @@
       validation_result);
 }
 
-void RecordModelDeliveryReceived(OptimizationTarget segment_id) {
+void RecordModelDeliveryReceived(SegmentId segment_id) {
   UMA_HISTOGRAM_ENUMERATION("SegmentationPlatform.ModelDelivery.Received",
                             OptimizationTargetToSegmentationModel(segment_id));
 }
 
-void RecordModelDeliverySaveResult(OptimizationTarget segment_id,
-                                   bool success) {
+void RecordModelDeliverySaveResult(SegmentId segment_id, bool success) {
   base::UmaHistogramBoolean(
       "SegmentationPlatform.ModelDelivery.SaveResult." +
           OptimizationTargetToHistogramVariant(segment_id),
       success);
 }
 
-void RecordModelDeliverySegmentIdMatches(OptimizationTarget segment_id,
-                                         bool matches) {
+void RecordModelDeliverySegmentIdMatches(SegmentId segment_id, bool matches) {
   base::UmaHistogramBoolean(
       "SegmentationPlatform.ModelDelivery.SegmentIdMatches." +
           OptimizationTargetToHistogramVariant(segment_id),
       matches);
 }
 
-void RecordModelExecutionDurationFeatureProcessing(
-    OptimizationTarget segment_id,
-    base::TimeDelta duration) {
+void RecordModelExecutionDurationFeatureProcessing(SegmentId segment_id,
+                                                   base::TimeDelta duration) {
   base::UmaHistogramTimes(
       "SegmentationPlatform.ModelExecution.Duration.FeatureProcessing." +
           OptimizationTargetToHistogramVariant(segment_id),
       duration);
 }
 
-void RecordModelExecutionDurationModel(OptimizationTarget segment_id,
+void RecordModelExecutionDurationModel(SegmentId segment_id,
                                        bool success,
                                        base::TimeDelta duration) {
   ModelExecutionStatus status = success ? ModelExecutionStatus::kSuccess
@@ -447,7 +432,7 @@
       duration);
 }
 
-void RecordModelExecutionDurationTotal(OptimizationTarget segment_id,
+void RecordModelExecutionDurationTotal(SegmentId segment_id,
                                        ModelExecutionStatus status,
                                        base::TimeDelta duration) {
   absl::optional<base::StringPiece> status_variant =
@@ -461,22 +446,21 @@
       duration);
 }
 
-void RecordModelExecutionResult(OptimizationTarget segment_id, float result) {
+void RecordModelExecutionResult(SegmentId segment_id, float result) {
   base::UmaHistogramPercentage(
       "SegmentationPlatform.ModelExecution.Result." +
           OptimizationTargetToHistogramVariant(segment_id),
       result * 100);
 }
 
-void RecordModelExecutionSaveResult(OptimizationTarget segment_id,
-                                    bool success) {
+void RecordModelExecutionSaveResult(SegmentId segment_id, bool success) {
   base::UmaHistogramBoolean(
       "SegmentationPlatform.ModelExecution.SaveResult." +
           OptimizationTargetToHistogramVariant(segment_id),
       success);
 }
 
-void RecordModelExecutionStatus(OptimizationTarget segment_id,
+void RecordModelExecutionStatus(SegmentId segment_id,
                                 bool default_provider,
                                 ModelExecutionStatus status) {
   if (!default_provider) {
@@ -492,7 +476,7 @@
   }
 }
 
-void RecordModelExecutionZeroValuePercent(OptimizationTarget segment_id,
+void RecordModelExecutionZeroValuePercent(SegmentId segment_id,
                                           const std::vector<float>& tensor) {
   base::UmaHistogramPercentage(
       "SegmentationPlatform.ModelExecution.ZeroValuePercent." +
@@ -551,7 +535,7 @@
       reason);
 }
 
-void RecordModelAvailability(OptimizationTarget segment_id,
+void RecordModelAvailability(SegmentId segment_id,
                              SegmentationModelAvailability availability) {
   base::UmaHistogramEnumeration(
       "SegmentationPlatform.ModelAvailability." +
@@ -565,7 +549,7 @@
       tensor_size);
 }
 
-void RecordTrainingDataCollectionEvent(OptimizationTarget segment_id,
+void RecordTrainingDataCollectionEvent(SegmentId segment_id,
                                        TrainingDataCollectionEvent event) {
   base::UmaHistogramEnumeration(
       "SegmentationPlatform.TrainingDataCollectionEvents." +
diff --git a/components/segmentation_platform/internal/stats.h b/components/segmentation_platform/internal/stats.h
index 3a468da..ac59117d 100644
--- a/components/segmentation_platform/internal/stats.h
+++ b/components/segmentation_platform/internal/stats.h
@@ -5,17 +5,17 @@
 #ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_STATS_H_
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_STATS_H_
 
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-using optimization_guide::proto::OptimizationTarget;
-
 namespace segmentation_platform::stats {
 
+using proto::SegmentId;
+
 // Keep in sync with AdaptiveToolbarSegmentSwitch in enums.xml.
 // Visible for testing.
 enum class AdaptiveToolbarSegmentSwitch {
@@ -45,20 +45,20 @@
 };
 
 // Returns an UMA display string for the given segment_id.
-std::string OptimizationTargetToHistogramVariant(OptimizationTarget segment_id);
+std::string OptimizationTargetToHistogramVariant(SegmentId segment_id);
 
 // Returns an UMA display string for the given `segmentation_key`.
 const char* SegmentationKeyToUmaName(const std::string& segmentation_key);
 
 // Records the score computed for a given segment.
-void RecordModelScore(OptimizationTarget segment_id, float score);
+void RecordModelScore(SegmentId segment_id, float score);
 
 // Records the result of segment selection whenever segment selection is
 // computed.
 void RecordSegmentSelectionComputed(
     const std::string& segmentation_key,
-    OptimizationTarget new_selection,
-    absl::optional<OptimizationTarget> previous_selection);
+    SegmentId new_selection,
+    absl::optional<SegmentId> previous_selection);
 
 // Database Maintenance metrics.
 // Records the number of unique signal identifiers that were successfully
@@ -74,61 +74,57 @@
 // Model Delivery metrics.
 // Records whether any incoming ML model had metadata attached that we were able
 // to parse.
-void RecordModelDeliveryHasMetadata(OptimizationTarget segment_id,
-                                    bool has_metadata);
+void RecordModelDeliveryHasMetadata(SegmentId segment_id, bool has_metadata);
 // Records the number of tensor features an updated ML model has.
-void RecordModelDeliveryMetadataFeatureCount(OptimizationTarget segment_id,
+void RecordModelDeliveryMetadataFeatureCount(SegmentId segment_id,
                                              size_t count);
 // Records the result of validating the metadata of an incoming ML model.
 // Recorded before and after it has been merged with the already stored
 // metadata.
 void RecordModelDeliveryMetadataValidation(
-    OptimizationTarget segment_id,
+    SegmentId segment_id,
     bool processed,
     metadata_utils::ValidationResult validation_result);
 // Record what type of model metadata we received.
-void RecordModelDeliveryReceived(OptimizationTarget segment_id);
+void RecordModelDeliveryReceived(SegmentId segment_id);
 // Records the result of attempting to save an updated version of the model
 // metadata.
-void RecordModelDeliverySaveResult(OptimizationTarget segment_id, bool success);
+void RecordModelDeliverySaveResult(SegmentId segment_id, bool success);
 // Records whether the currently stored segment_id matches the incoming
 // segment_id, as these are expected to match.
-void RecordModelDeliverySegmentIdMatches(OptimizationTarget segment_id,
-                                         bool matches);
+void RecordModelDeliverySegmentIdMatches(SegmentId segment_id, bool matches);
 
 // Model Execution metrics.
 // Records the duration of processing a single ML feature. This only takes into
 // account the time it takes to process (aggregate) a feature result, not
 // fetching it from the database. It also takes into account filtering any
 // enum histograms.
-void RecordModelExecutionDurationFeatureProcessing(
-    OptimizationTarget segment_id,
-    base::TimeDelta duration);
+void RecordModelExecutionDurationFeatureProcessing(SegmentId segment_id,
+                                                   base::TimeDelta duration);
 // Records the duration of executing an ML model. This only takes into account
 // the time it takes to invoke and wait for a result from the underlying ML
 // infrastructure from //components/optimization_guide, and not fetching the
 // relevant data from the database.
-void RecordModelExecutionDurationModel(OptimizationTarget segment_id,
+void RecordModelExecutionDurationModel(SegmentId segment_id,
                                        bool success,
                                        base::TimeDelta duration);
 // Records the duration of fetching data for, processing, and executing an ML
 // model.
-void RecordModelExecutionDurationTotal(OptimizationTarget segment_id,
+void RecordModelExecutionDurationTotal(SegmentId segment_id,
                                        ModelExecutionStatus status,
                                        base::TimeDelta duration);
 // Records the result value after successfully executing an ML model.
-void RecordModelExecutionResult(OptimizationTarget segment_id, float result);
+void RecordModelExecutionResult(SegmentId segment_id, float result);
 // Records whether the result value of of executing an ML model was successfully
 // saved.
-void RecordModelExecutionSaveResult(OptimizationTarget segment_id,
-                                    bool success);
+void RecordModelExecutionSaveResult(SegmentId segment_id, bool success);
 // Records the final execution status for any ML model execution.
-void RecordModelExecutionStatus(OptimizationTarget segment_id,
+void RecordModelExecutionStatus(SegmentId segment_id,
                                 bool default_provider,
                                 ModelExecutionStatus status);
 // Records the percent of features in a tensor that are equal to 0 when the
 // segmentation model is executed.
-void RecordModelExecutionZeroValuePercent(OptimizationTarget segment_id,
+void RecordModelExecutionZeroValuePercent(SegmentId segment_id,
                                           const std::vector<float>& tensor);
 
 // Signal Database metrics.
@@ -188,7 +184,7 @@
   kMaxValue = kMetadataInvalid
 };
 // Records the availability of segmentation models for each target needed.
-void RecordModelAvailability(OptimizationTarget segment_id,
+void RecordModelAvailability(SegmentId segment_id,
                              SegmentationModelAvailability availability);
 
 // Records the number of input tensor that's causing a failure to upload
@@ -212,7 +208,7 @@
 };
 
 // Records analytics for training data collection.
-void RecordTrainingDataCollectionEvent(OptimizationTarget segment_id,
+void RecordTrainingDataCollectionEvent(SegmentId segment_id,
                                        TrainingDataCollectionEvent event);
 
 }  // namespace segmentation_platform::stats
diff --git a/components/segmentation_platform/internal/stats_unittest.cc b/components/segmentation_platform/internal/stats_unittest.cc
index 722aa165..cc8d085 100644
--- a/components/segmentation_platform/internal/stats_unittest.cc
+++ b/components/segmentation_platform/internal/stats_unittest.cc
@@ -5,9 +5,9 @@
 #include "components/segmentation_platform/internal/stats.h"
 
 #include "base/test/metrics/histogram_tester.h"
-#include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
 #include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,44 +25,40 @@
   std::vector<float> all_non_zero{1, 2, 3};
 
   RecordModelExecutionZeroValuePercent(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, empty);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, empty);
   EXPECT_EQ(
       1, tester.GetBucketCount(
              "SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 0));
 
   RecordModelExecutionZeroValuePercent(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      single_zero);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, single_zero);
   EXPECT_EQ(
       1,
       tester.GetBucketCount(
           "SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 100));
 
   RecordModelExecutionZeroValuePercent(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      single_non_zero);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, single_non_zero);
   EXPECT_EQ(
       2, tester.GetBucketCount(
              "SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 0));
 
   RecordModelExecutionZeroValuePercent(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, all_zeroes);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, all_zeroes);
   EXPECT_EQ(
       2,
       tester.GetBucketCount(
           "SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 100));
 
   RecordModelExecutionZeroValuePercent(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      one_non_zero);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, one_non_zero);
   EXPECT_EQ(
       1,
       tester.GetBucketCount(
           "SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 66));
 
   RecordModelExecutionZeroValuePercent(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      all_non_zero);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB, all_non_zero);
   EXPECT_EQ(
       3, tester.GetBucketCount(
              "SegmentationPlatform.ModelExecution.ZeroValuePercent.NewTab", 0));
@@ -75,20 +71,19 @@
   // Share -> New tab.
   RecordSegmentSelectionComputed(
       kAdaptiveToolbarSegmentationKey,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
 
   // None -> Share.
   RecordSegmentSelectionComputed(
       kAdaptiveToolbarSegmentationKey,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-      absl::nullopt);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, absl::nullopt);
 
   // Share -> Share.
   RecordSegmentSelectionComputed(
       kAdaptiveToolbarSegmentationKey,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
   tester.ExpectTotalCount(histogram, 2);
 
   EXPECT_THAT(
@@ -111,9 +106,8 @@
   // Start to none.
   RecordSegmentSelectionComputed(
       kChromeStartAndroidSegmentationKey,
-      OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN,
-      OptimizationTarget::
-          OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID);
+      SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID);
 
   tester.ExpectTotalCount(histogram, 1);
   EXPECT_THAT(tester.GetAllSamples(histogram),
@@ -122,7 +116,7 @@
   // None to start.
   RecordSegmentSelectionComputed(
       kChromeStartAndroidSegmentationKey,
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
       absl::nullopt);
 
   tester.ExpectTotalCount(histogram, 2);
@@ -164,7 +158,7 @@
 TEST(StatsTest, TrainingDataCollectionEvent) {
   base::HistogramTester tester;
   RecordTrainingDataCollectionEvent(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
       TrainingDataCollectionEvent::kImmediateCollectionStart);
   EXPECT_EQ(1,
             tester.GetBucketCount(
diff --git a/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc b/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc
index e944d56..fb72278 100644
--- a/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc
+++ b/components/segmentation_platform/internal/ukm_data_manager_impl_unittest.cc
@@ -113,11 +113,10 @@
   }
 
   void AddModel(const proto::SegmentationModelMetadata& metadata) {
-    auto& callback =
-        model_provider_data_.model_providers_callbacks
-            [OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE];
-    callback.Run(OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
-                 metadata, 0);
+    auto& callback = model_provider_data_.model_providers_callbacks
+                         [SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE];
+    callback.Run(SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, metadata,
+                 0);
     segment_db_->GetCallback(true);
     segment_db_->UpdateCallback(true);
     segment_db_->LoadCallback(true);
@@ -128,7 +127,7 @@
     return *segmentation_platform_service_impl_;
   }
 
-  void SaveSegmentResult(OptimizationTarget segment_id,
+  void SaveSegmentResult(SegmentId segment_id,
                          absl::optional<proto::PredictionResult> result) {
     const std::string key = base::NumberToString(static_cast<int>(segment_id));
     auto& segment_info = segment_db_entries_[key];
@@ -142,7 +141,7 @@
     }
   }
 
-  bool HasSegmentResult(OptimizationTarget segment_id) {
+  bool HasSegmentResult(SegmentId segment_id) {
     const std::string key = base::NumberToString(static_cast<int>(segment_id));
     const auto it = segment_db_entries_.find(key);
     if (it == segment_db_entries_.end())
@@ -166,8 +165,7 @@
     ukm_recorder_ = std::make_unique<ukm::TestUkmRecorder>();
     auto ukm_db = std::make_unique<MockUkmDatabase>();
     ukm_database_ = ukm_db.get();
-    ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get(),
-                                                  true /*is_ukm_allowed*/);
+    ukm_observer_ = std::make_unique<UkmObserver>(ukm_recorder_.get());
     data_manager_->InitializeForTesting(std::move(ukm_db), ukm_observer_.get());
   }
 
@@ -218,8 +216,8 @@
 
 TEST_F(UkmDataManagerImplTest, HistoryNotification) {
   const GURL kUrl1 = GURL("https://www.url1.com/");
-  const OptimizationTarget kSegmentId =
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  const SegmentId kSegmentId =
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
 
   TestServicesForPlatform& platform1 = CreatePlatform();
   platform1.AddModel(PageLoadModelMetadata());
@@ -253,7 +251,7 @@
 
   // History based segment results should be removed.
   EXPECT_FALSE(platform1.HasSegmentResult(
-      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
+      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
 
   RemovePlatform(&platform1);
 }
diff --git a/components/segmentation_platform/public/BUILD.gn b/components/segmentation_platform/public/BUILD.gn
index d81eb92..760107d 100644
--- a/components/segmentation_platform/public/BUILD.gn
+++ b/components/segmentation_platform/public/BUILD.gn
@@ -25,6 +25,8 @@
     "service_proxy.h",
   ]
 
+  public_deps = [ "//components/segmentation_platform/public/proto" ]
+
   deps = [
     "//base",
     "//components/keyed_service/core",
diff --git a/components/segmentation_platform/public/config.h b/components/segmentation_platform/public/config.h
index 79a58bc4..8ce3100 100644
--- a/components/segmentation_platform/public/config.h
+++ b/components/segmentation_platform/public/config.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/time/time.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 
@@ -69,7 +69,7 @@
   base::TimeDelta unknown_selection_ttl;
 
   // List of segment ids that the current config requires to be available.
-  std::vector<optimization_guide::proto::OptimizationTarget> segment_ids;
+  std::vector<proto::SegmentId> segment_ids;
 
   // The selection only supports returning results from on-demand model
   // executions instead of returning result from previous sessions. The
diff --git a/components/segmentation_platform/public/field_trial_register.h b/components/segmentation_platform/public/field_trial_register.h
index 5dac271f..626c8844 100644
--- a/components/segmentation_platform/public/field_trial_register.h
+++ b/components/segmentation_platform/public/field_trial_register.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_FIELD_TRIAL_REGISTER_H_
 
 #include "base/strings/string_piece.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 
@@ -33,7 +33,7 @@
   // metadata.
   virtual void RegisterSubsegmentFieldTrialIfNeeded(
       base::StringPiece trial_name,
-      optimization_guide::proto::OptimizationTarget segment_id,
+      proto::SegmentId segment_id,
       int subsegment_rank) = 0;
 };
 
diff --git a/components/segmentation_platform/public/model_provider.cc b/components/segmentation_platform/public/model_provider.cc
index 1eede716..3f957a25 100644
--- a/components/segmentation_platform/public/model_provider.cc
+++ b/components/segmentation_platform/public/model_provider.cc
@@ -6,9 +6,8 @@
 
 namespace segmentation_platform {
 
-ModelProvider::ModelProvider(
-    optimization_guide::proto::OptimizationTarget optimization_target)
-    : optimization_target_(optimization_target) {}
+ModelProvider::ModelProvider(proto::SegmentId segment_id)
+    : segment_id_(segment_id) {}
 
 ModelProvider::~ModelProvider() = default;
 
diff --git a/components/segmentation_platform/public/model_provider.h b/components/segmentation_platform/public/model_provider.h
index fe41711..cfbc9b5f 100644
--- a/components/segmentation_platform/public/model_provider.h
+++ b/components/segmentation_platform/public/model_provider.h
@@ -7,7 +7,7 @@
 
 #include "base/callback.h"
 #include "base/task/sequenced_task_runner.h"
-#include "components/optimization_guide/proto/models.pb.h"
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform {
@@ -19,15 +19,12 @@
 // single optimization target.
 class ModelProvider {
  public:
-  using ModelUpdatedCallback = base::RepeatingCallback<void(
-      optimization_guide::proto::OptimizationTarget,
-      proto::SegmentationModelMetadata,
-      int64_t)>;
+  using ModelUpdatedCallback = base::RepeatingCallback<
+      void(proto::SegmentId, proto::SegmentationModelMetadata, int64_t)>;
   using ExecutionCallback =
       base::OnceCallback<void(const absl::optional<float>&)>;
 
-  explicit ModelProvider(
-      optimization_guide::proto::OptimizationTarget optimization_target);
+  explicit ModelProvider(proto::SegmentId segment_id);
   virtual ~ModelProvider();
 
   ModelProvider(ModelProvider&) = delete;
@@ -57,7 +54,7 @@
   virtual bool ModelAvailable() = 0;
 
  protected:
-  const optimization_guide::proto::OptimizationTarget optimization_target_;
+  const proto::SegmentId segment_id_;
 };
 
 // Interface used by segmentation platform to create ModelProvider(s).
@@ -65,15 +62,14 @@
  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 model provider for the given `segment_id`.
+  virtual std::unique_ptr<ModelProvider> CreateProvider(proto::SegmentId) = 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;
+      proto::SegmentId) = 0;
 };
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/public/proto/BUILD.gn b/components/segmentation_platform/public/proto/BUILD.gn
new file mode 100644
index 0000000..8118438
--- /dev/null
+++ b/components/segmentation_platform/public/proto/BUILD.gn
@@ -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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("proto") {
+  proto_in_dir = "//"
+  sources = [ "segmentation_platform.proto" ]
+}
diff --git a/components/segmentation_platform/public/proto/segmentation_platform.proto b/components/segmentation_platform/public/proto/segmentation_platform.proto
new file mode 100644
index 0000000..557a51da
--- /dev/null
+++ b/components/segmentation_platform/public/proto/segmentation_platform.proto
@@ -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.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package segmentation_platform.proto;
+
+// List of user segment types. Each enum entry should have a corresponding entry
+// in OptimizationTarget.
+enum SegmentId {
+  OPTIMIZATION_TARGET_UNKNOWN = 0;
+  // Should only be applied when the page load is predicted to be painful.
+  OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD = 1;
+  // Target for supplying the language detection model via the model downloader.
+  OPTIMIZATION_TARGET_LANGUAGE_DETECTION = 2;
+  // Target for determining topics present on a page.
+  OPTIMIZATION_TARGET_PAGE_TOPICS = 3;
+  // Target for segmentation: New tab page user.
+  OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB = 4;
+  // Target for segmentation: Share user.
+  OPTIMIZATION_TARGET_SEGMENTATION_SHARE = 5;
+  // Target for segmentation: Voice user.
+  OPTIMIZATION_TARGET_SEGMENTATION_VOICE = 6;
+  // Target for model validation.
+  OPTIMIZATION_TARGET_MODEL_VALIDATION = 7;
+  // Target for determining entities present on a page.
+  OPTIMIZATION_TARGET_PAGE_ENTITIES = 8;
+  // Target for Chrome Permissions Suggestions Service: Notification permission.
+  OPTIMIZATION_TARGET_NOTIFICATION_PERMISSION_PREDICTIONS = 9;
+  // Target that enables data collection on client side for various experiments.
+  OPTIMIZATION_TARGET_SEGMENTATION_DUMMY = 10;
+  // Target for segmentation: Chrome Android Start user.
+  OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID = 11;
+  // Target for segmentation: Query Tiles user.
+  OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES = 12;
+  // Target for determining the UI visibility of a page.
+  OPTIMIZATION_TARGET_PAGE_VISIBILITY = 13;
+  // Target for supplying the Autofill Assistant annotate DOM model via the
+  // model downloader.
+  OPTIMIZATION_TARGET_AUTOFILL_ASSISTANT = 14;
+  // Target for determining topics present on a page.
+  // TODO(crbug/1266504): Remove PAGE_TOPICS in favor of this target.
+  OPTIMIZATION_TARGET_PAGE_TOPICS_V2 = 15;
+  // Target for segmentation: Determine users with low engagement with chrome.
+  OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT = 16;
+  // Target for segmentation: Determine users who prefer to use Feed.
+  OPTIMIZATION_TARGET_SEGMENTATION_FEED_USER = 17;
+  // TODO(ssid): Remove this comment once the migration is final.
+  // DO NOT add new entries here. This is under process of migration from
+  // OptimizationTarget enum. New entries should start from a high value of 1000
+  // if OptimizationTarget does not have a corresponding type.
+};
diff --git a/components/segmentation_platform/public/service_proxy.cc b/components/segmentation_platform/public/service_proxy.cc
index 616fbe3..f4ffc6e 100644
--- a/components/segmentation_platform/public/service_proxy.cc
+++ b/components/segmentation_platform/public/service_proxy.cc
@@ -6,7 +6,7 @@
 
 namespace segmentation_platform {
 
-ServiceProxy::SegmentStatus::SegmentStatus(OptimizationTarget segment_id,
+ServiceProxy::SegmentStatus::SegmentStatus(SegmentId segment_id,
                                            const std::string& segment_metadata,
                                            const std::string& prediction_result,
                                            bool can_execute_segment)
@@ -16,7 +16,7 @@
       can_execute_segment(can_execute_segment) {}
 
 ServiceProxy::ClientInfo::ClientInfo(const std::string& segmentation_key,
-                                     OptimizationTarget selected_segment)
+                                     SegmentId selected_segment)
     : segmentation_key(segmentation_key), selected_segment(selected_segment) {}
 
 ServiceProxy::ClientInfo::~ClientInfo() = default;
diff --git a/components/segmentation_platform/public/service_proxy.h b/components/segmentation_platform/public/service_proxy.h
index 10f96e4..90303ee 100644
--- a/components/segmentation_platform/public/service_proxy.h
+++ b/components/segmentation_platform/public/service_proxy.h
@@ -9,23 +9,23 @@
 #include <vector>
 
 #include "base/observer_list_types.h"
-#include "components/optimization_guide/proto/models.pb.h"
-
-using optimization_guide::proto::OptimizationTarget;
+#include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 
 namespace segmentation_platform {
 
+using proto::SegmentId;
+
 // A helper class to expose internals of the segmentationss service to a logging
 // component and/or debug UI.
 class ServiceProxy {
  public:
   // Status about a segment.
   struct SegmentStatus {
-    SegmentStatus(OptimizationTarget segment_id,
+    SegmentStatus(SegmentId segment_id,
                   const std::string& segment_metadata,
                   const std::string& prediction_result,
                   bool can_execute_segment);
-    OptimizationTarget segment_id;
+    SegmentId segment_id;
     std::string segment_metadata;
     std::string prediction_result;
     bool can_execute_segment;
@@ -33,13 +33,12 @@
 
   // Information about a client to the segmentation platform.
   struct ClientInfo {
-    ClientInfo(const std::string& segmentation_key,
-               OptimizationTarget selected_segment);
+    ClientInfo(const std::string& segmentation_key, SegmentId selected_segment);
     ~ClientInfo();
     ClientInfo(const ClientInfo& other);
 
     std::string segmentation_key;
-    OptimizationTarget selected_segment;
+    SegmentId selected_segment;
     std::vector<SegmentStatus> segment_status;
   };
 
@@ -63,16 +62,16 @@
   virtual void GetServiceStatus() = 0;
 
   // Executes the given segment identified by |segment_id|.
-  virtual void ExecuteModel(OptimizationTarget segment_id) = 0;
+  virtual void ExecuteModel(SegmentId segment_id) = 0;
 
   // Overwrites the result for the given segment identified by |segment_id|.
   // This will trigger a new round of segment selection and update the existing
   // result in Prefs.
-  virtual void OverwriteResult(OptimizationTarget segment_id, float result) = 0;
+  virtual void OverwriteResult(SegmentId segment_id, float result) = 0;
 
   // Sets the selected segment for the config identified by |segment_id|.
   virtual void SetSelectedSegment(const std::string& segmentation_key,
-                                  OptimizationTarget segment_id) = 0;
+                                  SegmentId segment_id) = 0;
 
  protected:
   ServiceProxy() = default;
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
index 185921e..819c5a6 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -44,7 +44,7 @@
 
   bool CaseInsensitiveContains(base::StringPiece lowercase_key) const {
     const auto predicate = [lowercase_key](base::StringPiece element) {
-      return base::LowerCaseEqualsASCII(element, lowercase_key);
+      return base::EqualsCaseInsensitiveASCII(element, lowercase_key);
     };
     return std::find_if(pieces_.begin(), pieces_.end(), predicate) !=
            pieces_.end();
@@ -68,18 +68,21 @@
 
 mojom::ActivationLevel ParseActivationLevel(
     const base::StringPiece activation_level) {
-  if (base::LowerCaseEqualsASCII(activation_level, kActivationLevelEnabled))
+  if (base::EqualsCaseInsensitiveASCII(activation_level,
+                                       kActivationLevelEnabled))
     return mojom::ActivationLevel::kEnabled;
-  else if (base::LowerCaseEqualsASCII(activation_level, kActivationLevelDryRun))
+  else if (base::EqualsCaseInsensitiveASCII(activation_level,
+                                            kActivationLevelDryRun))
     return mojom::ActivationLevel::kDryRun;
   return mojom::ActivationLevel::kDisabled;
 }
 
 ActivationScope ParseActivationScope(const base::StringPiece activation_scope) {
-  if (base::LowerCaseEqualsASCII(activation_scope, kActivationScopeAllSites))
+  if (base::EqualsCaseInsensitiveASCII(activation_scope,
+                                       kActivationScopeAllSites))
     return ActivationScope::ALL_SITES;
-  else if (base::LowerCaseEqualsASCII(activation_scope,
-                                      kActivationScopeActivationList))
+  else if (base::EqualsCaseInsensitiveASCII(activation_scope,
+                                            kActivationScopeActivationList))
     return ActivationScope::ACTIVATION_LIST;
   return ActivationScope::NO_SITES;
 }
diff --git a/components/url_formatter/url_fixer.cc b/components/url_formatter/url_fixer.cc
index eebde604..c74b5bf3 100644
--- a/components/url_formatter/url_fixer.cc
+++ b/components/url_formatter/url_fixer.cc
@@ -575,7 +575,7 @@
 
   // 'about:blank' and 'about:srcdoc' are special-cased in various places in the
   // code and shouldn't use the chrome: scheme.
-  if (base::LowerCaseEqualsASCII(scheme, url::kAboutScheme)) {
+  if (base::EqualsCaseInsensitiveASCII(scheme, url::kAboutScheme)) {
     GURL about_url(base::ToLowerASCII(trimmed));
     if (about_url.IsAboutBlank() || about_url.IsAboutSrcdoc())
       return about_url;
diff --git a/components/url_matcher/regex_set_matcher.cc b/components/url_matcher/regex_set_matcher.cc
index 7223319f..893a241 100644
--- a/components/url_matcher/regex_set_matcher.cc
+++ b/components/url_matcher/regex_set_matcher.cc
@@ -15,7 +15,7 @@
 #include "third_party/re2/src/re2/filtered_re2.h"
 #include "third_party/re2/src/re2/re2.h"
 
-using base::StringPattern;
+using base::MatcherStringPattern;
 
 namespace url_matcher {
 
@@ -23,7 +23,7 @@
 RegexSetMatcher::~RegexSetMatcher() = default;
 
 void RegexSetMatcher::AddPatterns(
-    const std::vector<const StringPattern*>& regex_list) {
+    const std::vector<const MatcherStringPattern*>& regex_list) {
   if (regex_list.empty())
     return;
   for (size_t i = 0; i < regex_list.size(); ++i) {
@@ -39,7 +39,7 @@
 }
 
 bool RegexSetMatcher::Match(const std::string& text,
-                            std::set<StringPattern::ID>* matches) const {
+                            std::set<MatcherStringPattern::ID>* matches) const {
   size_t old_number_of_matches = matches->size();
   if (regexes_.empty())
     return false;
@@ -56,7 +56,7 @@
   filtered_re2_->AllMatches(text, atoms, &re2_ids);
 
   for (size_t i = 0; i < re2_ids.size(); ++i) {
-    StringPattern::ID id = re2_id_map_[re2_ids[i]];
+    MatcherStringPattern::ID id = re2_id_map_[re2_ids[i]];
     matches->insert(id);
   }
   return old_number_of_matches != matches->size();
@@ -97,7 +97,7 @@
   std::vector<std::string> strings_to_match;
   filtered_re2_->Compile(&strings_to_match);
 
-  std::vector<StringPattern> substring_patterns;
+  std::vector<MatcherStringPattern> substring_patterns;
   substring_patterns.reserve(strings_to_match.size());
 
   // Build SubstringSetMatcher from |strings_to_match|.
diff --git a/components/url_matcher/regex_set_matcher.h b/components/url_matcher/regex_set_matcher.h
index 06a261c..2aadc18e 100644
--- a/components/url_matcher/regex_set_matcher.h
+++ b/components/url_matcher/regex_set_matcher.h
@@ -11,7 +11,7 @@
 #include <string>
 #include <vector>
 
-#include "base/substring_set_matcher/string_pattern.h"
+#include "base/substring_set_matcher/matcher_string_pattern.h"
 #include "base/substring_set_matcher/substring_set_matcher.h"
 #include "components/url_matcher/url_matcher_export.h"
 
@@ -34,7 +34,8 @@
   // the FilteredRE2 matcher; thus, for efficiency, prefer adding multiple
   // patterns at once.
   // Ownership of the patterns remains with the caller.
-  void AddPatterns(const std::vector<const base::StringPattern*>& regex_list);
+  void AddPatterns(
+      const std::vector<const base::MatcherStringPattern*>& regex_list);
 
   // Removes all regex patterns.
   void ClearPatterns();
@@ -42,15 +43,16 @@
   // Appends the IDs of regular expressions in our set that match the |text|
   // to |matches|.
   bool Match(const std::string& text,
-             std::set<base::StringPattern::ID>* matches) const;
+             std::set<base::MatcherStringPattern::ID>* matches) const;
 
   bool IsEmpty() const;
 
  private:
   typedef int RE2ID;
-  typedef std::map<base::StringPattern::ID, const base::StringPattern*>
+  typedef std::map<base::MatcherStringPattern::ID,
+                   const base::MatcherStringPattern*>
       RegexMap;
-  typedef std::vector<base::StringPattern::ID> RE2IDMap;
+  typedef std::vector<base::MatcherStringPattern::ID> RE2IDMap;
 
   // Use Aho-Corasick SubstringSetMatcher to find which literal patterns
   // match the |text|.
@@ -62,10 +64,10 @@
   // apparently not supported by FilteredRE2.
   void RebuildMatcher();
 
-  // Mapping of regex StringPattern::IDs to regexes.
+  // Mapping of regex MatcherStringPattern::IDs to regexes.
   RegexMap regexes_;
   // Mapping of RE2IDs from FilteredRE2 (which are assigned in order)
-  // to regex StringPattern::IDs.
+  // to regex MatcherStringPattern::IDs.
   RE2IDMap re2_id_map_;
 
   std::unique_ptr<re2::FilteredRE2> filtered_re2_;
diff --git a/components/url_matcher/regex_set_matcher_unittest.cc b/components/url_matcher/regex_set_matcher_unittest.cc
index 71ff48d6..1dea84f 100644
--- a/components/url_matcher/regex_set_matcher_unittest.cc
+++ b/components/url_matcher/regex_set_matcher_unittest.cc
@@ -10,53 +10,53 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-using base::StringPattern;
+using base::MatcherStringPattern;
 using base::SubstringSetMatcher;
 
 namespace url_matcher {
 
 TEST(RegexSetMatcherTest, MatchRegexes) {
-  StringPattern pattern_1("ab.*c", 42);
-  StringPattern pattern_2("f*f", 17);
-  StringPattern pattern_3("c(ar|ra)b|brac", 239);
-  std::vector<const StringPattern*> regexes;
+  MatcherStringPattern pattern_1("ab.*c", 42);
+  MatcherStringPattern pattern_2("f*f", 17);
+  MatcherStringPattern pattern_3("c(ar|ra)b|brac", 239);
+  std::vector<const MatcherStringPattern*> regexes;
   regexes.push_back(&pattern_1);
   regexes.push_back(&pattern_2);
   regexes.push_back(&pattern_3);
   RegexSetMatcher matcher;
   matcher.AddPatterns(regexes);
 
-  std::set<StringPattern::ID> result1;
+  std::set<MatcherStringPattern::ID> result1;
   matcher.Match("http://abracadabra.com", &result1);
   EXPECT_EQ(2U, result1.size());
   EXPECT_TRUE(base::Contains(result1, 42));
   EXPECT_TRUE(base::Contains(result1, 239));
 
-  std::set<StringPattern::ID> result2;
+  std::set<MatcherStringPattern::ID> result2;
   matcher.Match("https://abfffffffffffffffffffffffffffffff.fi/cf", &result2);
   EXPECT_EQ(2U, result2.size());
   EXPECT_TRUE(base::Contains(result2, 17));
   EXPECT_TRUE(base::Contains(result2, 42));
 
-  std::set<StringPattern::ID> result3;
+  std::set<MatcherStringPattern::ID> result3;
   matcher.Match("http://nothing.com/", &result3);
   EXPECT_EQ(0U, result3.size());
 }
 
 TEST(RegexSetMatcherTest, CaseSensitivity) {
-  StringPattern pattern_1("AAA", 51);
-  StringPattern pattern_2("aaA", 57);
-  std::vector<const StringPattern*> regexes;
+  MatcherStringPattern pattern_1("AAA", 51);
+  MatcherStringPattern pattern_2("aaA", 57);
+  std::vector<const MatcherStringPattern*> regexes;
   regexes.push_back(&pattern_1);
   regexes.push_back(&pattern_2);
   RegexSetMatcher matcher;
   matcher.AddPatterns(regexes);
 
-  std::set<StringPattern::ID> result1;
+  std::set<MatcherStringPattern::ID> result1;
   matcher.Match("http://aaa.net/", &result1);
   EXPECT_EQ(0U, result1.size());
 
-  std::set<StringPattern::ID> result2;
+  std::set<MatcherStringPattern::ID> result2;
   matcher.Match("http://aaa.net/quaaACK", &result2);
   EXPECT_EQ(1U, result2.size());
   EXPECT_TRUE(base::Contains(result2, 57));
diff --git a/components/url_matcher/url_matcher.cc b/components/url_matcher/url_matcher.cc
index 5381500..1fce666e 100644
--- a/components/url_matcher/url_matcher.cc
+++ b/components/url_matcher/url_matcher.cc
@@ -15,22 +15,22 @@
 #include "url/gurl.h"
 #include "url/url_canon.h"
 
-using base::StringPattern;
+using base::MatcherStringPattern;
 using base::SubstringSetMatcher;
 
 namespace url_matcher {
 
 // This set of classes implement a mapping of URL Component Patterns, such as
-// host_prefix, host_suffix, host_equals, ..., etc., to StringPatterns
+// host_prefix, host_suffix, host_equals, ..., etc., to MatcherStringPatterns
 // for use in substring comparisons.
 //
 // The idea of this mapping is to reduce the problem of comparing many
 // URL Component Patterns against one URL to the problem of searching many
 // substrings in one string:
 //
-// ----------------------                    -----------------
-// | URL Query operator | ----translate----> | StringPattern |
-// ----------------------                    -----------------
+// ----------------------                    ------------------------
+// | URL Query operator | ----translate----> | MatcherStringPattern |
+// ----------------------                    ------------------------
 //                                                   ^
 //                                                   |
 //                                                compare
@@ -98,7 +98,7 @@
 //
 // Similarly for path query parameters ({path, query}_{prefix, suffix, equals}).
 //
-// With this, we can search the StringPatterns in the normalized URL.
+// With this, we can search the MatcherStringPatterns in the normalized URL.
 //
 //
 // Case 2: url_{prefix,suffix,equals,contains} searches.
@@ -171,8 +171,9 @@
 
 URLMatcherCondition::~URLMatcherCondition() {}
 
-URLMatcherCondition::URLMatcherCondition(Criterion criterion,
-                                         const StringPattern* string_pattern)
+URLMatcherCondition::URLMatcherCondition(
+    Criterion criterion,
+    const MatcherStringPattern* string_pattern)
     : criterion_(criterion), string_pattern_(string_pattern) {}
 
 URLMatcherCondition::URLMatcherCondition(const URLMatcherCondition& rhs)
@@ -227,7 +228,7 @@
 }
 
 bool URLMatcherCondition::IsMatch(
-    const std::set<StringPattern::ID>& matching_patterns,
+    const std::set<MatcherStringPattern::ID>& matching_patterns,
     const GURL& url) const {
   DCHECK(string_pattern_);
   if (!base::Contains(matching_patterns, string_pattern_->id()))
@@ -464,7 +465,7 @@
 }
 
 void URLMatcherConditionFactory::ForgetUnusedPatterns(
-    const std::set<StringPattern::ID>& used_patterns) {
+    const std::set<MatcherStringPattern::ID>& used_patterns) {
   auto i = substring_pattern_singletons_.begin();
   while (i != substring_pattern_singletons_.end()) {
     if (base::Contains(used_patterns, i->first->id()))
@@ -499,7 +500,7 @@
 URLMatcherCondition URLMatcherConditionFactory::CreateCondition(
     URLMatcherCondition::Criterion criterion,
     const std::string& pattern) {
-  StringPattern search_pattern(pattern, 0);
+  MatcherStringPattern search_pattern(pattern, 0);
   PatternSingletons* pattern_singletons = nullptr;
   if (IsRegexCriterion(criterion))
     pattern_singletons = &regex_pattern_singletons_;
@@ -513,7 +514,8 @@
   if (iter != pattern_singletons->end())
     return URLMatcherCondition(criterion, iter->first);
 
-  StringPattern* new_pattern = new StringPattern(pattern, GetNextID());
+  MatcherStringPattern* new_pattern =
+      new MatcherStringPattern(pattern, GetNextID());
   (*pattern_singletons)[new_pattern] = base::WrapUnique(new_pattern);
   return URLMatcherCondition(criterion, new_pattern);
 }
@@ -561,15 +563,15 @@
 int URLMatcherConditionFactory::GetNextID() {
   id_counter_++;
 
-  if (id_counter_ == StringPattern::kInvalidId)
+  if (id_counter_ == MatcherStringPattern::kInvalidId)
     id_counter_++;
 
   return id_counter_;
 }
 
-bool URLMatcherConditionFactory::StringPatternPointerCompare::operator()(
-    StringPattern* lhs,
-    StringPattern* rhs) const {
+bool URLMatcherConditionFactory::MatcherStringPatternPointerCompare::operator()(
+    MatcherStringPattern* lhs,
+    MatcherStringPattern* rhs) const {
   if (lhs == nullptr && rhs != nullptr)
     return true;
   if (lhs != nullptr && rhs != nullptr)
@@ -761,13 +763,13 @@
       port_filter_(std::move(port_filter)) {}
 
 bool URLMatcherConditionSet::IsMatch(
-    const std::set<StringPattern::ID>& matching_patterns,
+    const std::set<MatcherStringPattern::ID>& matching_patterns,
     const GURL& url) const {
   return IsMatch(matching_patterns, url, std::string());
 }
 
 bool URLMatcherConditionSet::IsMatch(
-    const std::set<StringPattern::ID>& matching_patterns,
+    const std::set<MatcherStringPattern::ID>& matching_patterns,
     const GURL& url,
     const std::string& url_for_component_searches) const {
   for (auto i = conditions_.begin(); i != conditions_.end(); ++i) {
@@ -828,10 +830,10 @@
 
 std::set<URLMatcherConditionSet::ID> URLMatcher::MatchURL(
     const GURL& url) const {
-  // Find all IDs of StringPatterns that match |url|.
+  // Find all IDs of MatcherStringPatterns that match |url|.
   // See URLMatcherConditionFactory for the canonicalization of URLs and the
   // distinction between full url searches and url component searches.
-  std::set<StringPattern::ID> matches;
+  std::set<MatcherStringPattern::ID> matches;
   std::string url_for_component_searches;
 
   if (!IsMatcherEmpty(full_url_matcher_)) {
@@ -894,7 +896,7 @@
 
   // Determine which patterns need to be registered when this function
   // terminates.
-  std::set<const StringPattern*> new_patterns;
+  std::set<const MatcherStringPattern*> new_patterns;
   for (URLMatcherConditionSets::const_iterator condition_set_iter =
            url_matcher_condition_sets_.begin();
        condition_set_iter != url_matcher_condition_sets_.end();
@@ -928,14 +930,14 @@
       full_url_conditions ? full_url_matcher_ : url_component_matcher_;
 
   url_matcher = std::make_unique<SubstringSetMatcher>();
-  bool success = url_matcher->Build(std::vector<const StringPattern*>(
+  bool success = url_matcher->Build(std::vector<const MatcherStringPattern*>(
       new_patterns.begin(), new_patterns.end()));
   CHECK(success);
 }
 
 void URLMatcher::UpdateRegexSetMatcher() {
-  std::vector<const StringPattern*> new_patterns;
-  std::vector<const StringPattern*> new_origin_and_path_patterns;
+  std::vector<const MatcherStringPattern*> new_patterns;
+  std::vector<const MatcherStringPattern*> new_origin_and_path_patterns;
 
   for (URLMatcherConditionSets::const_iterator condition_set_iter =
            url_matcher_condition_sets_.begin();
@@ -964,7 +966,7 @@
 
 void URLMatcher::UpdateTriggers() {
   // Count substring pattern frequencies.
-  std::map<StringPattern::ID, size_t> substring_pattern_frequencies;
+  std::map<MatcherStringPattern::ID, size_t> substring_pattern_frequencies;
   for (URLMatcherConditionSets::const_iterator condition_set_iter =
            url_matcher_condition_sets_.begin();
        condition_set_iter != url_matcher_condition_sets_.end();
@@ -973,7 +975,7 @@
         condition_set_iter->second->conditions();
     for (auto condition_iter = conditions.begin();
          condition_iter != conditions.end(); ++condition_iter) {
-      const StringPattern* pattern = condition_iter->string_pattern();
+      const MatcherStringPattern* pattern = condition_iter->string_pattern();
       substring_pattern_frequencies[pattern->id()]++;
     }
 
@@ -982,13 +984,14 @@
     for (auto query_condition_iter = query_conditions.begin();
          query_condition_iter != query_conditions.end();
          ++query_condition_iter) {
-      const StringPattern* pattern = query_condition_iter->string_pattern();
+      const MatcherStringPattern* pattern =
+          query_condition_iter->string_pattern();
       substring_pattern_frequencies[pattern->id()]++;
     }
   }
 
   // Update trigger conditions: Determine for each URLMatcherConditionSet which
-  // URLMatcherCondition contains a StringPattern that occurs least
+  // URLMatcherCondition contains a MatcherStringPattern that occurs least
   // frequently in this URLMatcher. We assume that this condition is very
   // specific and occurs rarely in URLs. If a match occurs for this
   // URLMatcherCondition, we want to test all other URLMatcherCondition in the
@@ -1004,11 +1007,12 @@
     if (conditions.empty())
       continue;
     auto condition_iter = conditions.begin();
-    StringPattern::ID trigger = condition_iter->string_pattern()->id();
+    MatcherStringPattern::ID trigger = condition_iter->string_pattern()->id();
     // We skip the first element in the following loop.
     ++condition_iter;
     for (; condition_iter != conditions.end(); ++condition_iter) {
-      StringPattern::ID current_id = condition_iter->string_pattern()->id();
+      MatcherStringPattern::ID current_id =
+          condition_iter->string_pattern()->id();
       if (substring_pattern_frequencies[trigger] >
           substring_pattern_frequencies[current_id]) {
         trigger = current_id;
@@ -1020,7 +1024,7 @@
     for (auto query_condition_iter = query_conditions.begin();
          query_condition_iter != query_conditions.end();
          ++query_condition_iter) {
-      StringPattern::ID current_id =
+      MatcherStringPattern::ID current_id =
           query_condition_iter->string_pattern()->id();
       if (substring_pattern_frequencies[trigger] >
           substring_pattern_frequencies[current_id]) {
@@ -1033,7 +1037,7 @@
 }
 
 void URLMatcher::UpdateConditionFactory() {
-  std::set<StringPattern::ID> used_patterns;
+  std::set<MatcherStringPattern::ID> used_patterns;
   for (URLMatcherConditionSets::const_iterator condition_set_iter =
            url_matcher_condition_sets_.begin();
        condition_set_iter != url_matcher_condition_sets_.end();
diff --git a/components/url_matcher/url_matcher.h b/components/url_matcher/url_matcher.h
index 75a52f7..88ef5f2 100644
--- a/components/url_matcher/url_matcher.h
+++ b/components/url_matcher/url_matcher.h
@@ -24,9 +24,9 @@
 // This class represents a single URL matching condition, e.g. a match on the
 // host suffix or the containment of a string in the query component of a GURL.
 //
-// The difference from a simple StringPattern is that this also supports
+// The difference from a simple MatcherStringPattern is that this also supports
 // checking whether the {Host, Path, Query} of a URL contains a string. The
-// reduction of URL matching conditions to StringPatterns conducted by
+// reduction of URL matching conditions to MatcherStringPatterns conducted by
 // URLMatcherConditionFactory is not capable of expressing that alone.
 //
 // Also supported is matching regular expressions against the URL (URL_MATCHES).
@@ -58,13 +58,15 @@
   URLMatcherCondition();
   ~URLMatcherCondition();
   URLMatcherCondition(Criterion criterion,
-                      const base::StringPattern* substring_pattern);
+                      const base::MatcherStringPattern* substring_pattern);
   URLMatcherCondition(const URLMatcherCondition& rhs);
   URLMatcherCondition& operator=(const URLMatcherCondition& rhs);
   bool operator<(const URLMatcherCondition& rhs) const;
 
   Criterion criterion() const { return criterion_; }
-  const base::StringPattern* string_pattern() const { return string_pattern_; }
+  const base::MatcherStringPattern* string_pattern() const {
+    return string_pattern_;
+  }
 
   // Returns whether this URLMatcherCondition needs to be executed on a
   // full URL rather than the individual components (see
@@ -81,16 +83,17 @@
 
   // Returns whether this condition is fulfilled according to
   // |matching_patterns| and |url|.
-  bool IsMatch(const std::set<base::StringPattern::ID>& matching_patterns,
-               const GURL& url) const;
+  bool IsMatch(
+      const std::set<base::MatcherStringPattern::ID>& matching_patterns,
+      const GURL& url) const;
 
  private:
   // |criterion_| and |string_pattern_| describe together what property a URL
   // needs to fulfill to be considered a match.
   Criterion criterion_;
 
-  // This is the StringPattern that is used in a SubstringSetMatcher.
-  raw_ptr<const base::StringPattern> string_pattern_;
+  // This is the MatcherStringPattern that is used in a SubstringSetMatcher.
+  raw_ptr<const base::MatcherStringPattern> string_pattern_;
 };
 
 // Class to map the problem of finding {host, path, query} {prefixes, suffixes,
@@ -110,7 +113,7 @@
 // of a dictionary in a text" problem, which can be solved very efficiently
 // by the Aho-Corasick algorithm.
 //
-// IMPORTANT: The URLMatcherConditionFactory owns the StringPattern
+// IMPORTANT: The URLMatcherConditionFactory owns the MatcherStringPattern
 // referenced by created URLMatcherConditions. Therefore, it must outlive
 // all created URLMatcherCondition and the SubstringSetMatcher.
 class URL_MATCHER_EXPORT URLMatcherConditionFactory {
@@ -179,14 +182,14 @@
   // |used_patterns|. These patterns are not referenced any more and get
   // freed.
   void ForgetUnusedPatterns(
-      const std::set<base::StringPattern::ID>& used_patterns);
+      const std::set<base::MatcherStringPattern::ID>& used_patterns);
 
   // Returns true if this object retains no allocated data. Only for debugging.
   bool IsEmpty() const;
 
  private:
   // Creates a URLMatcherCondition according to the parameters passed.
-  // The URLMatcherCondition will refer to a StringPattern that is
+  // The URLMatcherCondition will refer to a MatcherStringPattern that is
   // owned by |pattern_singletons_|.
   URLMatcherCondition CreateCondition(URLMatcherCondition::Criterion criterion,
                                       const std::string& pattern);
@@ -203,23 +206,26 @@
                                 bool prepend_beginning_of_query_component,
                                 bool append_end_of_query_component) const;
 
-  // Return the next StringPattern id to use.
+  // Return the next MatcherStringPattern id to use.
   int GetNextID();
 
-  // Counter that ensures that all created StringPatterns have unique IDs.
-  // Note that substring patterns and regex patterns will use different IDs.
+  // Counter that ensures that all created MatcherStringPatterns have unique
+  // IDs. Note that substring patterns and regex patterns will use different
+  // IDs.
   int id_counter_;
 
   // This comparison considers only the pattern() value of the
-  // StringPatterns.
-  struct StringPatternPointerCompare {
-    bool operator()(base::StringPattern* lhs, base::StringPattern* rhs) const;
+  // MatcherStringPatterns.
+  struct MatcherStringPatternPointerCompare {
+    bool operator()(base::MatcherStringPattern* lhs,
+                    base::MatcherStringPattern* rhs) const;
   };
-  // Set to ensure that we generate only one StringPattern for each content
-  // of StringPattern::pattern().
-  using PatternSingletons = std::map<base::StringPattern*,
-                                     std::unique_ptr<base::StringPattern>,
-                                     StringPatternPointerCompare>;
+  // Set to ensure that we generate only one MatcherStringPattern for each
+  // content of MatcherStringPattern::pattern().
+  using PatternSingletons =
+      std::map<base::MatcherStringPattern*,
+               std::unique_ptr<base::MatcherStringPattern>,
+               MatcherStringPatternPointerCompare>;
   PatternSingletons substring_pattern_singletons_;
   PatternSingletons regex_pattern_singletons_;
   PatternSingletons origin_and_path_regex_pattern_singletons_;
@@ -262,7 +268,9 @@
   // Returns whether the URL query satisfies the key value constraint.
   bool IsMatch(const std::string& canonical_url_query) const;
 
-  const base::StringPattern* string_pattern() const { return string_pattern_; }
+  const base::MatcherStringPattern* string_pattern() const {
+    return string_pattern_;
+  }
 
  private:
   Type match_type_;
@@ -270,7 +278,7 @@
   std::string value_;
   size_t key_length_;
   size_t value_length_;
-  raw_ptr<const base::StringPattern> string_pattern_;
+  raw_ptr<const base::MatcherStringPattern> string_pattern_;
 };
 
 // This class represents a filter for the URL scheme to be hooked up into a
@@ -322,7 +330,7 @@
   typedef int ID;
   typedef std::set<URLMatcherCondition> Conditions;
   typedef std::set<URLQueryElementMatcherCondition> QueryConditions;
-  typedef std::vector<scoped_refptr<URLMatcherConditionSet> > Vector;
+  typedef std::vector<scoped_refptr<URLMatcherConditionSet>> Vector;
 
   // Matches if all conditions in |conditions| are fulfilled.
   URLMatcherConditionSet(ID id, const Conditions& conditions);
@@ -352,12 +360,14 @@
   const Conditions& conditions() const { return conditions_; }
   const QueryConditions& query_conditions() const { return query_conditions_; }
 
-  bool IsMatch(const std::set<base::StringPattern::ID>& matching_patterns,
-               const GURL& url) const;
+  bool IsMatch(
+      const std::set<base::MatcherStringPattern::ID>& matching_patterns,
+      const GURL& url) const;
 
-  bool IsMatch(const std::set<base::StringPattern::ID>& matching_patterns,
-               const GURL& url,
-               const std::string& url_for_component_searches) const;
+  bool IsMatch(
+      const std::set<base::MatcherStringPattern::ID>& matching_patterns,
+      const GURL& url,
+      const std::string& url_for_component_searches) const;
 
  private:
   friend class base::RefCounted<URLMatcherConditionSet>;
@@ -420,16 +430,16 @@
   // Maps the ID of a URLMatcherConditionSet to the respective
   // URLMatcherConditionSet.
   typedef std::map<URLMatcherConditionSet::ID,
-                   scoped_refptr<URLMatcherConditionSet> >
+                   scoped_refptr<URLMatcherConditionSet>>
       URLMatcherConditionSets;
   URLMatcherConditionSets url_matcher_condition_sets_;
 
-  // Maps a StringPattern ID to the URLMatcherConditions that need to
-  // be triggered in case of a StringPattern match.
-  typedef std::map<base::StringPattern::ID,
+  // Maps a MatcherStringPattern ID to the URLMatcherConditions that need to
+  // be triggered in case of a MatcherStringPattern match.
+  typedef std::map<base::MatcherStringPattern::ID,
                    std::set<URLMatcherConditionSet::ID>>
-      StringPatternTriggers;
-  StringPatternTriggers substring_match_triggers_;
+      MatcherStringPatternTriggers;
+  MatcherStringPatternTriggers substring_match_triggers_;
 
   std::unique_ptr<base::SubstringSetMatcher> full_url_matcher_;
   std::unique_ptr<base::SubstringSetMatcher> url_component_matcher_;
diff --git a/components/url_matcher/url_matcher_unittest.cc b/components/url_matcher/url_matcher_unittest.cc
index e9c7227..c6e6b12 100644
--- a/components/url_matcher/url_matcher_unittest.cc
+++ b/components/url_matcher/url_matcher_unittest.cc
@@ -13,7 +13,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-using base::StringPattern;
+using base::MatcherStringPattern;
 
 namespace url_matcher {
 
@@ -22,7 +22,7 @@
 //
 
 TEST(URLMatcherConditionTest, Constructors) {
-  StringPattern pattern("example.com", 1);
+  MatcherStringPattern pattern("example.com", 1);
   URLMatcherCondition m1(URLMatcherCondition::HOST_SUFFIX, &pattern);
   EXPECT_EQ(URLMatcherCondition::HOST_SUFFIX, m1.criterion());
   EXPECT_EQ(&pattern, m1.string_pattern());
@@ -68,7 +68,7 @@
 }
 
 TEST(URLMatcherConditionTest, IsFullURLCondition) {
-  StringPattern pattern("example.com", 1);
+  MatcherStringPattern pattern("example.com", 1);
   EXPECT_FALSE(URLMatcherCondition(URLMatcherCondition::HOST_SUFFIX, &pattern)
                    .IsFullURLCondition());
 
@@ -93,10 +93,10 @@
   GURL url1("http://www.example.com/www.foobar.com/index.html");
   GURL url2("http://www.foobar.com/example.com/index.html");
 
-  StringPattern pattern("example.com", 1);
+  MatcherStringPattern pattern("example.com", 1);
   URLMatcherCondition m1(URLMatcherCondition::HOST_SUFFIX, &pattern);
 
-  std::set<StringPattern::ID> matching_patterns;
+  std::set<MatcherStringPattern::ID> matching_patterns;
 
   // matches = {0} --> matcher did not indicate that m1 was a match.
   matching_patterns.insert(0);
@@ -115,8 +115,8 @@
 }
 
 TEST(URLMatcherConditionTest, Comparison) {
-  StringPattern p1("foobar.com", 1);
-  StringPattern p2("foobar.com", 2);
+  MatcherStringPattern p1("foobar.com", 1);
+  MatcherStringPattern p2("foobar.com", 2);
   // The first component of each test is expected to be < than the second.
   URLMatcherCondition test_smaller[][2] = {
       {URLMatcherCondition(URLMatcherCondition::HOST_PREFIX, &p1),
@@ -233,11 +233,11 @@
   EXPECT_NE(c5.string_pattern(), c4.string_pattern());
   EXPECT_NE(c5.string_pattern()->id(), c4.string_pattern()->id());
 
-  // Check that all StringPattern singletons are freed if we call
+  // Check that all MatcherStringPattern singletons are freed if we call
   // ForgetUnusedPatterns.
-  StringPattern::ID old_id_1 = c1.string_pattern()->id();
-  StringPattern::ID old_id_4 = c4.string_pattern()->id();
-  factory.ForgetUnusedPatterns(std::set<StringPattern::ID>());
+  MatcherStringPattern::ID old_id_1 = c1.string_pattern()->id();
+  MatcherStringPattern::ID old_id_4 = c4.string_pattern()->id();
+  factory.ForgetUnusedPatterns(std::set<MatcherStringPattern::ID>());
   EXPECT_TRUE(factory.IsEmpty());
   URLMatcherCondition c6 = factory.CreateHostEqualsCondition("www.google.com");
   EXPECT_NE(old_id_1, c6.string_pattern()->id());
@@ -489,7 +489,7 @@
   EXPECT_EQ(1, condition_set->id());
   EXPECT_EQ(2u, condition_set->conditions().size());
 
-  std::set<StringPattern::ID> matching_patterns;
+  std::set<MatcherStringPattern::ID> matching_patterns;
   matching_patterns.insert(m1.string_pattern()->id());
   EXPECT_FALSE(condition_set->IsMatch(matching_patterns, url1));
 
diff --git a/components/webapps/renderer/web_page_metadata_extraction.cc b/components/webapps/renderer/web_page_metadata_extraction.cc
index b07bdc18..c20c62b 100644
--- a/components/webapps/renderer/web_page_metadata_extraction.cc
+++ b/components/webapps/renderer/web_page_metadata_extraction.cc
@@ -81,10 +81,11 @@
       //
       // Bookmark apps also support "apple-touch-icon" and
       // "apple-touch-icon-precomposed".
-      if (base::LowerCaseEqualsASCII(rel, "icon") ||
-          base::LowerCaseEqualsASCII(rel, "shortcut icon") ||
-          base::LowerCaseEqualsASCII(rel, "apple-touch-icon") ||
-          base::LowerCaseEqualsASCII(rel, "apple-touch-icon-precomposed")) {
+      if (base::EqualsCaseInsensitiveASCII(rel, "icon") ||
+          base::EqualsCaseInsensitiveASCII(rel, "shortcut icon") ||
+          base::EqualsCaseInsensitiveASCII(rel, "apple-touch-icon") ||
+          base::EqualsCaseInsensitiveASCII(rel,
+                                           "apple-touch-icon-precomposed")) {
         AddInstallIcon(elem, &metadata->icons);
       }
     } else if (elem.HasHTMLTagName("meta") && elem.HasAttribute("name")) {
@@ -101,10 +102,10 @@
         if (!metadata->application_url.is_valid())
           metadata->application_url = GURL();
       } else if (name == "mobile-web-app-capable" &&
-                 base::LowerCaseEqualsASCII(content.Utf16(), "yes")) {
+                 base::EqualsCaseInsensitiveASCII(content.Utf16(), "yes")) {
         metadata->mobile_capable = mojom::WebPageMobileCapable::ENABLED;
       } else if (name == "apple-mobile-web-app-capable" &&
-                 base::LowerCaseEqualsASCII(content.Utf16(), "yes") &&
+                 base::EqualsCaseInsensitiveASCII(content.Utf16(), "yes") &&
                  metadata->mobile_capable ==
                      mojom::WebPageMobileCapable::UNSPECIFIED) {
         metadata->mobile_capable = mojom::WebPageMobileCapable::ENABLED_APPLE;
diff --git a/components/webcrypto/algorithms/aes_ctr.cc b/components/webcrypto/algorithms/aes_ctr.cc
index 186a410..a93dc42 100644
--- a/components/webcrypto/algorithms/aes_ctr.cc
+++ b/components/webcrypto/algorithms/aes_ctr.cc
@@ -6,9 +6,12 @@
 #include <stdint.h>
 #include <string.h>
 
+#include <array>
 #include <memory>
 
 #include "base/check_op.h"
+#include "base/containers/span.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
 #include "components/webcrypto/algorithms/aes.h"
 #include "components/webcrypto/algorithms/util.h"
@@ -16,9 +19,9 @@
 #include "components/webcrypto/crypto_data.h"
 #include "components/webcrypto/status.h"
 #include "crypto/openssl_util.h"
+#include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
 #include "third_party/boringssl/src/include/openssl/aes.h"
-#include "third_party/boringssl/src/include/openssl/bn.h"
 #include "third_party/boringssl/src/include/openssl/cipher.h"
 
 namespace webcrypto {
@@ -39,30 +42,29 @@
 
 // Encrypts/decrypts given a 128-bit counter.
 //
-// |output| must be a pointer to a buffer which has a length of at least
-// |input.byte_length()|.
+// |output| must have the same length as |input|.
 Status AesCtrEncrypt128BitCounter(const EVP_CIPHER* cipher,
                                   const CryptoData& raw_key,
                                   const CryptoData& input,
-                                  const CryptoData& counter,
-                                  uint8_t* output) {
+                                  base::span<const uint8_t, 16> counter,
+                                  base::span<uint8_t> output) {
   DCHECK(cipher);
-  DCHECK_EQ(16u, counter.byte_length());
+  DCHECK_EQ(input.byte_length(), output.size());
 
   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
   bssl::ScopedEVP_CIPHER_CTX context;
   if (!EVP_CipherInit_ex(context.get(), cipher, nullptr, raw_key.bytes(),
-                         counter.bytes(), ENCRYPT)) {
+                         counter.data(), ENCRYPT)) {
     return Status::OperationError();
   }
 
   int output_len = 0;
-  if (!EVP_CipherUpdate(context.get(), output, &output_len, input.bytes(),
-                        input.byte_length())) {
+  if (!EVP_CipherUpdate(context.get(), output.data(), &output_len,
+                        input.bytes(), input.byte_length())) {
     return Status::OperationError();
   }
   int final_output_chunk_len = 0;
-  if (!EVP_CipherFinal_ex(context.get(), output + output_len,
+  if (!EVP_CipherFinal_ex(context.get(), output.data() + output_len,
                           &final_output_chunk_len)) {
     return Status::OperationError();
   }
@@ -80,41 +82,35 @@
   return a == 0 ? 0 : 1 + (a - 1) / b;
 }
 
-// Extracts the counter as a BIGNUM. The counter is the rightmost
-// "counter_length_bits" of the block, interpreted as a big-endian number.
-bssl::UniquePtr<BIGNUM> GetCounter(const CryptoData& counter_block,
-                                   unsigned int counter_length_bits) {
-  unsigned int counter_length_remainder_bits = (counter_length_bits % 8);
-
-  // If the counter is a multiple of 8 bits then can call BN_bin2bn() directly.
-  if (counter_length_remainder_bits == 0) {
-    unsigned int byte_length = counter_length_bits / 8;
-    return bssl::UniquePtr<BIGNUM>(BN_bin2bn(
-        counter_block.bytes() + counter_block.byte_length() - byte_length,
-        byte_length, nullptr));
-  }
-
-  // Otherwise make a copy of the counter and zero out the topmost bits so
-  // BN_bin2bn() can be called with a byte stream.
+// Extracts the counter as a `absl::uint128`. The counter is the rightmost
+// `counter_length_bits` of the block, interpreted as a big-endian number.
+absl::uint128 GetCounter(base::span<const uint8_t, 16> counter_block,
+                         unsigned int counter_length_bits) {
+  unsigned int counter_length_remainder_bits = counter_length_bits % 8;
   unsigned int byte_length = CeilDiv(counter_length_bits, 8u);
-  std::vector<uint8_t> counter(
-      counter_block.bytes() + counter_block.byte_length() - byte_length,
-      counter_block.bytes() + counter_block.byte_length());
-  counter[0] &= ~(0xFF << counter_length_remainder_bits);
+  DCHECK_GT(byte_length, 0u);
 
-  return bssl::UniquePtr<BIGNUM>(
-      BN_bin2bn(counter.data(), counter.size(), nullptr));
+  base::span<const uint8_t> suffix = counter_block.last(byte_length);
+  absl::uint128 ret = suffix[0];
+  // The first byte may be partial.
+  if (counter_length_remainder_bits != 0) {
+    ret &= ~(0xFF << counter_length_remainder_bits);
+  }
+  for (uint8_t b : suffix.subspan(1)) {
+    ret = (ret << 8) | b;
+  }
+  return ret;
 }
 
 // Returns a counter block with the counter bits all set all zero.
-std::vector<uint8_t> BlockWithZeroedCounter(const CryptoData& counter_block,
-                                            unsigned int counter_length_bits) {
+std::array<uint8_t, AES_BLOCK_SIZE> BlockWithZeroedCounter(
+    base::span<const uint8_t, AES_BLOCK_SIZE> counter_block,
+    unsigned int counter_length_bits) {
   unsigned int counter_length_bytes = counter_length_bits / 8;
   unsigned int counter_length_bits_remainder = counter_length_bits % 8;
 
-  std::vector<uint8_t> new_counter_block(
-      counter_block.bytes(),
-      counter_block.bytes() + counter_block.byte_length());
+  std::array<uint8_t, AES_BLOCK_SIZE> new_counter_block;
+  memcpy(new_counter_block.data(), counter_block.data(), AES_BLOCK_SIZE);
 
   size_t index = new_counter_block.size() - counter_length_bytes;
   memset(&new_counter_block.front() + index, 0, counter_length_bytes);
@@ -148,8 +144,10 @@
   const blink::WebCryptoAesCtrParams* params = algorithm.AesCtrParams();
   const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key);
 
-  if (params->Counter().size() != 16)
+  if (params->Counter().size() != AES_BLOCK_SIZE)
     return Status::ErrorIncorrectSizeAesCtrCounter();
+  base::span<const uint8_t, AES_BLOCK_SIZE> counter_block(
+      params->Counter().Data(), params->Counter().size());
 
   unsigned int counter_length_bits = params->LengthBits();
   if (counter_length_bits < 1 || counter_length_bits > 128)
@@ -165,46 +163,37 @@
   if (!cipher)
     return Status::ErrorUnexpected();
 
-  const CryptoData counter_block(params->Counter());
   buffer->resize(base::ValueOrDieForType<size_t>(output_max_len));
+  absl::uint128 current_counter =
+      GetCounter(counter_block, counter_length_bits);
+
+  if (counter_length_bits == 128) {
+    return AesCtrEncrypt128BitCounter(cipher, CryptoData(raw_key), data,
+                                      counter_block, *buffer);
+  }
 
   // The total number of possible counter values is pow(2, counter_length_bits)
-  bssl::UniquePtr<BIGNUM> num_counter_values(BN_new());
-  if (!BN_lshift(num_counter_values.get(), BN_value_one(), counter_length_bits))
-    return Status::ErrorUnexpected();
-
-  bssl::UniquePtr<BIGNUM> current_counter =
-      GetCounter(counter_block, counter_length_bits);
+  absl::uint128 num_counter_values = absl::uint128(1) << counter_length_bits;
 
   // The number of AES blocks needed for encryption/decryption. The counter is
   // incremented this many times.
-  bssl::UniquePtr<BIGNUM> num_output_blocks(BN_new());
-  if (!BN_set_word(
-          num_output_blocks.get(),
-          CeilDiv(buffer->size(), static_cast<size_t>(AES_BLOCK_SIZE)))) {
-    return Status::ErrorUnexpected();
-  }
+  size_t num_output_blocks = CeilDiv(buffer->size(), size_t{AES_BLOCK_SIZE});
 
   // If the counter is going to be incremented more times than there are counter
   // values, fail. (Repeating values of the counter block is bad).
-  if (BN_cmp(num_output_blocks.get(), num_counter_values.get()) > 0)
+  if (num_output_blocks > num_counter_values)
     return Status::ErrorAesCtrInputTooLongCounterRepeated();
 
   // This is the number of blocks that can be successfully encrypted without
   // overflowing the counter. Encrypting the subsequent block will need to
   // reset the counter to zero.
-  bssl::UniquePtr<BIGNUM> num_blocks_until_reset(BN_new());
-
-  if (!BN_sub(num_blocks_until_reset.get(), num_counter_values.get(),
-              current_counter.get())) {
-    return Status::ErrorUnexpected();
-  }
+  absl::uint128 num_blocks_until_reset = num_counter_values - current_counter;
 
   // If the counter can be incremented for the entire input without
   // wrapping-around, do it as a single call into BoringSSL.
-  if (BN_cmp(num_blocks_until_reset.get(), num_output_blocks.get()) >= 0) {
+  if (num_blocks_until_reset >= num_output_blocks) {
     return AesCtrEncrypt128BitCounter(cipher, CryptoData(raw_key), data,
-                                      counter_block, buffer->data());
+                                      counter_block, *buffer);
   }
 
   // Otherwise the encryption needs to be done in 2 parts. The first part using
@@ -213,26 +202,30 @@
 
   // This is guaranteed to fit in an "unsigned int" because input size in bytes
   // fits in an "unsigned int".
-  BN_ULONG num_blocks_part1 = BN_get_word(num_blocks_until_reset.get());
-  BN_ULONG input_size_part1 = num_blocks_part1 * AES_BLOCK_SIZE;
+  unsigned int input_size_part1 =
+      static_cast<unsigned int>(num_blocks_until_reset * AES_BLOCK_SIZE);
   DCHECK_LT(input_size_part1, data.byte_length());
+  base::span<uint8_t> output_part1 =
+      base::make_span(*buffer).first(input_size_part1);
+  base::span<uint8_t> output_part2 =
+      base::make_span(*buffer).subspan(input_size_part1);
 
   // Encrypt the first part (before wrap-around).
   Status status = AesCtrEncrypt128BitCounter(
       cipher, CryptoData(raw_key), CryptoData(data.bytes(), input_size_part1),
-      counter_block, buffer->data());
+      counter_block, output_part1);
   if (status.IsError())
     return status;
 
   // Encrypt the second part (after wrap-around).
-  std::vector<uint8_t> counter_block_part2 =
+  std::array<uint8_t, AES_BLOCK_SIZE> counter_block_part2 =
       BlockWithZeroedCounter(counter_block, counter_length_bits);
 
   return AesCtrEncrypt128BitCounter(
       cipher, CryptoData(raw_key),
       CryptoData(data.bytes() + input_size_part1,
                  data.byte_length() - input_size_part1),
-      CryptoData(counter_block_part2), buffer->data() + input_size_part1);
+      counter_block_part2, output_part2);
 }
 
 class AesCtrImplementation : public AesAlgorithm {
diff --git a/content/browser/accessibility/hit_testing_browsertest.cc b/content/browser/accessibility/hit_testing_browsertest.cc
index 1012f14..f83226f 100644
--- a/content/browser/accessibility/hit_testing_browsertest.cc
+++ b/content/browser/accessibility/hit_testing_browsertest.cc
@@ -426,8 +426,8 @@
 // this platform.
 #if !BUILDFLAG(IS_ANDROID)
 
-// crbug.com/1317505: Flaky on Lacros
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// crbug.com/1317505: Flaky on Lacros and Linux Wayland
+#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
 #define MAYBE_HitTestInPopup DISABLED_HitTestInPopup
 #else
 #define MAYBE_HitTestInPopup HitTestInPopup
diff --git a/content/browser/hid/hid_service.cc b/content/browser/hid/hid_service.cc
index 1069e702b..193a834 100644
--- a/content/browser/hid/hid_service.cc
+++ b/content/browser/hid/hid_service.cc
@@ -14,6 +14,7 @@
 #include "base/debug/stack_trace.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/document_service.h"
 #include "content/public/browser/hid_chooser.h"
 #include "content/public/browser/hid_delegate.h"
 #include "content/public/browser/render_frame_host.h"
@@ -75,23 +76,84 @@
 
 }  // namespace
 
-HidService::HidService(RenderFrameHost* render_frame_host,
+// Deletes the HidService when the connected document is destroyed.
+class DocumentHelper
+    : public content::DocumentService<blink::mojom::HidService> {
+ public:
+  DocumentHelper(HidService* parent,
+                 RenderFrameHost* render_frame_host,
+                 mojo::PendingReceiver<blink::mojom::HidService> receiver)
+      : DocumentService(render_frame_host, std::move(receiver)),
+        parent_(std::move(parent)) {
+    DCHECK(parent_);
+  }
+  ~DocumentHelper() override = default;
+
+  // blink::mojom::HidService:
+  void RegisterClient(
+      mojo::PendingAssociatedRemote<device::mojom::HidManagerClient> client)
+      override {
+    parent_->RegisterClient(std::move(client));
+  }
+  void GetDevices(GetDevicesCallback callback) override {
+    parent_->GetDevices(std::move(callback));
+  }
+  void RequestDevice(
+      std::vector<blink::mojom::HidDeviceFilterPtr> filters,
+      std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
+      RequestDeviceCallback callback) override {
+    parent_->RequestDevice(std::move(filters), std::move(exclusion_filters),
+                           std::move(callback));
+  }
+  void Connect(const std::string& device_guid,
+               mojo::PendingRemote<device::mojom::HidConnectionClient> client,
+               ConnectCallback callback) override {
+    parent_->Connect(device_guid, std::move(client), std::move(callback));
+  }
+  void Forget(device::mojom::HidDeviceInfoPtr device_info,
+              ForgetCallback callback) override {
+    parent_->Forget(std::move(device_info), std::move(callback));
+  }
+
+ private:
+  const std::unique_ptr<HidService> parent_;
+};
+
+HidService::HidService(BrowserContext* browser_context,
+                       const url::Origin& origin,
+                       RenderFrameHostImpl* render_frame_host,
                        mojo::PendingReceiver<blink::mojom::HidService> receiver)
-    : DocumentService(render_frame_host, std::move(receiver)),
-      origin_(render_frame_host->GetMainFrame()->GetLastCommittedOrigin()) {
+    : browser_context_(browser_context),
+      render_frame_host_(render_frame_host),
+      origin_(origin) {
   watchers_.set_disconnect_handler(
       base::BindRepeating(&HidService::OnWatcherRemoved, base::Unretained(this),
-                          true /* cleanup_watcher_ids */));
+                          /* cleanup_watcher_ids=*/true));
 
   HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate();
   if (delegate)
-    delegate->AddObserver(render_frame_host, this);
+    delegate->AddObserver(browser_context_, this);
+
+  if (render_frame_host_) {
+    // DocumentHelper observes the lifetime of the document connected to
+    // `render_frame_host_` and destroys the HidService (deletes `this`) when
+    // the document is destroyed or navigates cross-origin. It will also trigger
+    // self-destruction if the Mojo connection is disconnected.
+    new DocumentHelper(std::move(this), render_frame_host_,
+                       std::move(receiver));
+  } else {
+    // If there is no connected document, register a disconnect handler to
+    // self-destruct on disconnection.
+    receiver_.Bind(std::move(receiver));
+    receiver_.set_disconnect_handler(base::BindOnce(
+        &HidService::OnServiceDisconnected, base::Unretained(this)));
+  }
 }
 
 HidService::~HidService() {
   HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate();
   if (delegate)
-    delegate->RemoveObserver(render_frame_host(), this);
+    delegate->RemoveObserver(this);
 
   // The remaining watchers will be closed from this end.
   if (!watchers_.empty())
@@ -100,7 +162,7 @@
 
 // static
 void HidService::Create(
-    RenderFrameHost* render_frame_host,
+    RenderFrameHostImpl* render_frame_host,
     mojo::PendingReceiver<blink::mojom::HidService> receiver) {
   DCHECK(render_frame_host);
 
@@ -126,7 +188,27 @@
   // HidService owns itself. It will self-destruct when a mojo interface error
   // occurs, the render frame host is deleted, or the render frame host
   // navigates to a new document.
-  new HidService(render_frame_host, std::move(receiver));
+  new HidService(render_frame_host->GetBrowserContext(),
+                 render_frame_host->GetMainFrame()->GetLastCommittedOrigin(),
+                 render_frame_host, std::move(receiver));
+}
+
+// static
+void HidService::Create(
+    BrowserContext* browser_context,
+    const url::Origin& origin,
+    mojo::PendingReceiver<blink::mojom::HidService> receiver) {
+  DCHECK(browser_context);
+
+  // Avoid creating the HidService if there is no HID delegate to provide
+  // the implementation.
+  if (!GetContentClient()->browser()->GetHidDelegate())
+    return;
+
+  // HidService owns itself. It will self-destruct when a mojo interface
+  // error occurs.
+  new HidService(browser_context, origin, /*render_frame_host=*/nullptr,
+                 std::move(receiver));
 }
 
 void HidService::RegisterClient(
@@ -138,7 +220,7 @@
   GetContentClient()
       ->browser()
       ->GetHidDelegate()
-      ->GetHidManager(render_frame_host())
+      ->GetHidManager(browser_context_)
       ->GetDevices(base::BindOnce(&HidService::FinishGetDevices,
                                   weak_factory_.GetWeakPtr(),
                                   std::move(callback)));
@@ -149,13 +231,13 @@
     std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
     RequestDeviceCallback callback) {
   HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate();
-  if (!delegate->CanRequestDevicePermission(render_frame_host())) {
+  if (!render_frame_host_ ||
+      !delegate->CanRequestDevicePermission(browser_context_, origin_)) {
     std::move(callback).Run(std::vector<device::mojom::HidDeviceInfoPtr>());
     return;
   }
-
-  chooser_ = delegate->RunChooser(
-      render_frame_host(), std::move(filters), std::move(exclusion_filters),
+  chooser_ = GetContentClient()->browser()->GetHidDelegate()->RunChooser(
+      render_frame_host_, std::move(filters), std::move(exclusion_filters),
       base::BindOnce(&HidService::FinishRequestDevice,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -165,9 +247,7 @@
     mojo::PendingRemote<device::mojom::HidConnectionClient> client,
     ConnectCallback callback) {
   if (watchers_.empty()) {
-    auto* web_contents_impl = static_cast<WebContentsImpl*>(
-        WebContents::FromRenderFrameHost(render_frame_host()));
-    web_contents_impl->IncrementHidActiveFrameCount();
+    IncrementActiveFrameCount();
   }
 
   mojo::PendingRemote<device::mojom::HidConnectionWatcher> watcher;
@@ -176,19 +256,19 @@
   watcher_ids_.insert({device_guid, receiver_id});
 
   auto* delegate = GetContentClient()->browser()->GetHidDelegate();
-  delegate->GetHidManager(render_frame_host())
+  delegate->GetHidManager(browser_context_)
       ->Connect(
           device_guid, std::move(client), std::move(watcher),
           /*allow_protected_reports=*/false,
-          delegate->IsFidoAllowedForOrigin(render_frame_host(), origin_),
+          delegate->IsFidoAllowedForOrigin(browser_context_, origin_),
           base::BindOnce(&HidService::FinishConnect, weak_factory_.GetWeakPtr(),
                          std::move(callback)));
 }
 
 void HidService::Forget(device::mojom::HidDeviceInfoPtr device_info,
                         ForgetCallback callback) {
-  auto* delegate = GetContentClient()->browser()->GetHidDelegate();
-  delegate->RevokeDevicePermission(render_frame_host(), *device_info);
+  GetContentClient()->browser()->GetHidDelegate()->RevokeDevicePermission(
+      browser_context_, origin_, *device_info);
   std::move(callback).Run();
 }
 
@@ -204,22 +284,36 @@
   }
 }
 
+void HidService::OnServiceDisconnected() {
+  delete this;
+}
+
+void HidService::IncrementActiveFrameCount() {
+  if (render_frame_host_) {
+    auto* web_contents_impl =
+        WebContentsImpl::FromRenderFrameHostImpl(render_frame_host_);
+    web_contents_impl->IncrementHidActiveFrameCount();
+  }
+}
+
 void HidService::DecrementActiveFrameCount() {
-  auto* web_contents_impl = static_cast<WebContentsImpl*>(
-      WebContents::FromRenderFrameHost(render_frame_host()));
-  web_contents_impl->DecrementHidActiveFrameCount();
+  if (render_frame_host_) {
+    auto* web_contents_impl =
+        WebContentsImpl::FromRenderFrameHostImpl(render_frame_host_);
+    web_contents_impl->DecrementHidActiveFrameCount();
+  }
 }
 
 void HidService::OnDeviceAdded(
     const device::mojom::HidDeviceInfo& device_info) {
   auto* delegate = GetContentClient()->browser()->GetHidDelegate();
-  if (!delegate->HasDevicePermission(render_frame_host(), device_info))
+  if (!delegate->HasDevicePermission(browser_context_, origin_, device_info))
     return;
 
   auto filtered_device_info = device_info.Clone();
   RemoveProtectedReports(
       *filtered_device_info,
-      delegate->IsFidoAllowedForOrigin(render_frame_host(), origin_));
+      delegate->IsFidoAllowedForOrigin(browser_context_, origin_));
   if (filtered_device_info->collections.empty())
     return;
 
@@ -243,14 +337,14 @@
     OnWatcherRemoved(/*cleanup_watcher_ids=*/false);
 
   auto* delegate = GetContentClient()->browser()->GetHidDelegate();
-  if (!delegate->HasDevicePermission(render_frame_host(), device_info)) {
+  if (!delegate->HasDevicePermission(browser_context_, origin_, device_info)) {
     return;
   }
 
   auto filtered_device_info = device_info.Clone();
   RemoveProtectedReports(
       *filtered_device_info,
-      delegate->IsFidoAllowedForOrigin(render_frame_host(), origin_));
+      delegate->IsFidoAllowedForOrigin(browser_context_, origin_));
   if (filtered_device_info->collections.empty())
     return;
 
@@ -262,14 +356,14 @@
     const device::mojom::HidDeviceInfo& device_info) {
   auto* delegate = GetContentClient()->browser()->GetHidDelegate();
   const bool has_device_permission =
-      delegate->HasDevicePermission(render_frame_host(), device_info);
+      delegate->HasDevicePermission(browser_context_, origin_, device_info);
 
   device::mojom::HidDeviceInfoPtr filtered_device_info;
   if (has_device_permission) {
     filtered_device_info = device_info.Clone();
     RemoveProtectedReports(
         *filtered_device_info,
-        delegate->IsFidoAllowedForOrigin(render_frame_host(), origin_));
+        delegate->IsFidoAllowedForOrigin(browser_context_, origin_));
   }
 
   if (!has_device_permission || filtered_device_info->collections.empty()) {
@@ -305,16 +399,16 @@
   }
 
   HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate();
-  auto* rfh = render_frame_host();
 
   size_t watchers_removed =
       base::EraseIf(watcher_ids_, [&](const auto& watcher_entry) {
         const auto* device_info =
-            delegate->GetDeviceInfo(rfh, watcher_entry.first);
+            delegate->GetDeviceInfo(browser_context_, watcher_entry.first);
         if (!device_info)
           return true;
 
-        if (delegate->HasDevicePermission(rfh, *device_info)) {
+        if (delegate->HasDevicePermission(browser_context_, origin_,
+                                          *device_info)) {
           return false;
         }
 
@@ -333,14 +427,14 @@
   auto* delegate = GetContentClient()->browser()->GetHidDelegate();
 
   bool is_fido_allowed =
-      delegate->IsFidoAllowedForOrigin(render_frame_host(), origin_);
+      delegate->IsFidoAllowedForOrigin(browser_context_, origin_);
   std::vector<device::mojom::HidDeviceInfoPtr> result;
   for (auto& device : devices) {
     RemoveProtectedReports(*device, is_fido_allowed);
     if (device->collections.empty())
       continue;
 
-    if (delegate->HasDevicePermission(render_frame_host(), *device))
+    if (delegate->HasDevicePermission(browser_context_, origin_, *device))
       result.push_back(std::move(device));
   }
 
diff --git a/content/browser/hid/hid_service.h b/content/browser/hid/hid_service.h
index 7a1e10a..b2182acf 100644
--- a/content/browser/hid/hid_service.h
+++ b/content/browser/hid/hid_service.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
-#include "content/public/browser/document_service.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/public/browser/hid_delegate.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -18,22 +18,30 @@
 #include "mojo/public/cpp/bindings/remote_set.h"
 #include "services/device/public/mojom/hid.mojom.h"
 #include "third_party/blink/public/mojom/hid/hid.mojom.h"
+#include "url/origin.h"
 
 namespace content {
 
 class HidChooser;
-class RenderFrameHost;
 
 // HidService provides an implementation of the HidService mojom interface. This
 // interface is used by Blink to implement the WebHID API.
-class HidService : public content::DocumentService<blink::mojom::HidService>,
-                   public device::mojom::HidConnectionWatcher,
-                   public HidDelegate::Observer {
+class CONTENT_EXPORT HidService : public blink::mojom::HidService,
+                                  public device::mojom::HidConnectionWatcher,
+                                  public HidDelegate::Observer {
  public:
   HidService(HidService&) = delete;
   HidService& operator=(HidService&) = delete;
+  ~HidService() override;
 
-  static void Create(RenderFrameHost*,
+  // Use this when creating from a document.
+  static void Create(RenderFrameHostImpl*,
+                     mojo::PendingReceiver<blink::mojom::HidService>);
+
+  // Use this when creating from a service worker, which doesn't have
+  // RenderFrameHost.
+  static void Create(BrowserContext*,
+                     const url::Origin&,
                      mojo::PendingReceiver<blink::mojom::HidService>);
 
   // blink::mojom::HidService:
@@ -61,10 +69,14 @@
   void OnPermissionRevoked(const url::Origin& origin) override;
 
  private:
-  HidService(RenderFrameHost*, mojo::PendingReceiver<blink::mojom::HidService>);
-  ~HidService() override;
+  HidService(BrowserContext*,
+             const url::Origin&,
+             RenderFrameHostImpl*,
+             mojo::PendingReceiver<blink::mojom::HidService>);
 
   void OnWatcherRemoved(bool cleanup_watcher_ids);
+  void OnServiceDisconnected();
+  void IncrementActiveFrameCount();
   void DecrementActiveFrameCount();
 
   void FinishGetDevices(GetDevicesCallback callback,
@@ -76,6 +88,27 @@
       ConnectCallback callback,
       mojo::PendingRemote<device::mojom::HidConnection> connection);
 
+  // The browser_context pointed by |browser_context_| always outlives
+  // HidService itself.
+  const raw_ptr<BrowserContext> browser_context_;
+
+  // When render_frame_host pointed by |render_frame_host| destroys, the bound
+  // HidService will be destroyed first. It should be safe to access
+  // |render_frame_host_| whenever it is not null.
+  const raw_ptr<RenderFrameHostImpl> render_frame_host_;
+
+  // When created from a document, `receiver_` is not bound. Instead, the
+  // receiver is transferred to a DocumentService which manages the Mojo
+  // connection and observes the document lifecycle. The DocumentService ensures
+  // HidService is destroyed when the Mojo connection is disconnected,
+  // renderFrameHost is deleted, or the RenderFrameHost commits a cross-document
+  // navigation. The DocumentService forwards its Mojo interface to HidService.
+  //
+  // When created from a service worker, `receiver_` is bound and no
+  // DocumentService is created. HidService self-destructs when the Mojo
+  // connection is disconnected.
+  mojo::Receiver<blink::mojom::HidService> receiver_{this};
+
   // The last shown HID chooser UI.
   std::unique_ptr<HidChooser> chooser_;
   url::Origin origin_;
diff --git a/content/browser/hid/hid_service_unittest.cc b/content/browser/hid/hid_service_unittest.cc
index 12dd84b..dfaa8ec 100644
--- a/content/browser/hid/hid_service_unittest.cc
+++ b/content/browser/hid/hid_service_unittest.cc
@@ -6,12 +6,18 @@
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_future.h"
+#include "content/browser/hid/hid_service.h"
 #include "content/browser/hid/hid_test_utils.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/hid_delegate.h"
 #include "content/public/common/content_client.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_web_contents_factory.h"
 #include "content/test/test_render_view_host.h"
 #include "content/test/test_web_contents.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -24,20 +30,35 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/hid/hid.mojom.h"
 
+namespace content {
+
+namespace {
+
 using ::base::test::RunClosure;
+using ::base::test::TestFuture;
 using ::testing::_;
 using ::testing::ByMove;
 using ::testing::ElementsAre;
 using ::testing::Return;
 
-namespace content {
-
-namespace {
+enum HidServiceCreationType {
+  kCreateUsingRenderFrameHost,
+  kCreateUsingBrowserContextAndOrigin,
+};
 
 const char kTestUrl[] = "https://www.google.com";
 const char kTestGuid[] = "test-guid";
 const char kCrossOriginTestUrl[] = "https://www.chromium.org";
 
+std::string HidServiceCreationTypeToString(HidServiceCreationType type) {
+  switch (type) {
+    case kCreateUsingRenderFrameHost:
+      return "CreateUsingRenderFrameHost";
+    case kCreateUsingBrowserContextAndOrigin:
+      return "CreateUsingBrowserContextAndOrigin";
+  }
+}
+
 class FakeHidConnectionClient : public device::mojom::HidConnectionClient {
  public:
   FakeHidConnectionClient() = default;
@@ -80,28 +101,16 @@
   mojo::AssociatedReceiver<device::mojom::HidManagerClient> receiver_{this};
 };
 
-// Main test fixture.
-class HidServiceTest : public RenderViewHostImplTestHarness {
+class HidServiceTestHelper {
  public:
-  HidServiceTest() {
+  HidServiceTestHelper() {
     ON_CALL(hid_delegate(), GetHidManager).WillByDefault(Return(&hid_manager_));
     ON_CALL(hid_delegate(), IsFidoAllowedForOrigin)
         .WillByDefault(Return(false));
   }
-  HidServiceTest(HidServiceTest&) = delete;
-  HidServiceTest& operator=(HidServiceTest&) = delete;
-  ~HidServiceTest() override = default;
-
-  void SetUp() override {
-    original_client_ = SetBrowserClientForTesting(&test_client_);
-    RenderViewHostTestHarness::SetUp();
-  }
-
-  void TearDown() override {
-    RenderViewHostTestHarness::TearDown();
-    if (original_client_)
-      SetBrowserClientForTesting(original_client_);
-  }
+  HidServiceTestHelper(HidServiceTestHelper&) = delete;
+  HidServiceTestHelper& operator=(HidServiceTestHelper&) = delete;
+  ~HidServiceTestHelper() = default;
 
   void ConnectDevice(const device::mojom::HidDeviceInfo& device) {
     hid_manager_.AddDevice(device.Clone());
@@ -161,16 +170,72 @@
   raw_ptr<ContentBrowserClient> original_client_ = nullptr;
   device::FakeHidManager hid_manager_;
   FakeHidConnectionClient connection_client_;
+  ScopedContentBrowserClientSetting setting{&test_client_};
 };
 
+class HidServiceBaseTest : public testing::Test, public HidServiceTestHelper {
+ public:
+  HidServiceBaseTest() = default;
+  HidServiceBaseTest(HidServiceBaseTest&) = delete;
+  HidServiceBaseTest& operator=(HidServiceBaseTest&) = delete;
+  ~HidServiceBaseTest() override = default;
+
+  const mojo::Remote<blink::mojom::HidService>& GetService(
+      HidServiceCreationType type) {
+    switch (type) {
+      case kCreateUsingRenderFrameHost:
+        web_contents_ =
+            web_contents_factory_.CreateWebContents(&browser_context_);
+        static_cast<TestWebContents*>(web_contents_)
+            ->NavigateAndCommit(GURL(kTestUrl));
+        static_cast<TestWebContents*>(web_contents_)
+            ->GetMainFrame()
+            ->GetHidService(service_.BindNewPipeAndPassReceiver());
+        break;
+      case kCreateUsingBrowserContextAndOrigin:
+        HidService::Create(&browser_context_,
+                           url::Origin::Create(GURL(kTestUrl)),
+                           service_.BindNewPipeAndPassReceiver());
+        break;
+      default:
+        NOTREACHED();
+    }
+    return service_;
+  }
+
+  void CheckWebContentsHidServiceConnectedState(HidServiceCreationType type,
+                                                bool expected_state) {
+    // Skip the check when there is no web content.
+    if (type == kCreateUsingBrowserContextAndOrigin) {
+      return;
+    }
+    ASSERT_EQ(web_contents_->IsConnectedToHidDevice(), expected_state);
+  }
+
+ private:
+  BrowserTaskEnvironment task_environment_;
+  TestBrowserContext browser_context_;
+  mojo::Remote<blink::mojom::HidService> service_;
+  TestWebContentsFactory web_contents_factory_;
+  raw_ptr<WebContents> web_contents_;  // Owned by |web_contents_factory_|.
+};
+
+class HidServiceRenderFrameHostTest : public RenderViewHostImplTestHarness,
+                                      public HidServiceTestHelper {};
+
+class HidServiceTest
+    : public HidServiceBaseTest,
+      public testing::WithParamInterface<HidServiceCreationType> {};
+
+// Test fixture parameterized for fido allowed or not.
+class HidServiceFidoTest : public HidServiceBaseTest,
+                           public testing::WithParamInterface<
+                               std::tuple<HidServiceCreationType, bool>> {};
+
 }  // namespace
 
-TEST_F(HidServiceTest, GetDevicesWithPermission) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, GetDevicesWithPermission) {
+  const auto& service = GetService(GetParam());
 
   auto collection = device::mojom::HidCollectionInfo::New();
   collection->usage = device::mojom::HidUsageAndPage::New(0xff00, 0x0001);
@@ -194,12 +259,8 @@
   EXPECT_EQ(1u, devices.size());
 }
 
-TEST_F(HidServiceTest, GetDevicesWithoutPermission) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, GetDevicesWithoutPermission) {
+  const auto& service = GetService(GetParam());
 
   auto device_info = CreateDeviceWithOneReport();
   ConnectDevice(*device_info);
@@ -217,22 +278,21 @@
   EXPECT_EQ(0u, devices.size());
 }
 
-TEST_F(HidServiceTest, RequestDevice) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, RequestDevice) {
+  auto service_creation_type = GetParam();
+  const auto& service = GetService(service_creation_type);
 
   auto device_info = CreateDeviceWithOneReport();
   std::vector<device::mojom::HidDeviceInfoPtr> device_infos;
   device_infos.push_back(device_info.Clone());
   ConnectDevice(*device_info);
 
-  EXPECT_CALL(hid_delegate(), CanRequestDevicePermission)
-      .WillOnce(Return(true));
-  EXPECT_CALL(hid_delegate(), RunChooserInternal)
-      .WillOnce(Return(ByMove(std::move(device_infos))));
+  if (service_creation_type == kCreateUsingRenderFrameHost) {
+    EXPECT_CALL(hid_delegate(), CanRequestDevicePermission)
+        .WillOnce(Return(true));
+    EXPECT_CALL(hid_delegate(), RunChooserInternal)
+        .WillOnce(Return(ByMove(std::move(device_infos))));
+  }
 
   base::RunLoop run_loop;
   std::vector<device::mojom::HidDeviceInfoPtr> chosen_devices;
@@ -246,15 +306,16 @@
             run_loop.Quit();
           }));
   run_loop.Run();
-  EXPECT_EQ(1u, chosen_devices.size());
+  if (service_creation_type == kCreateUsingRenderFrameHost) {
+    EXPECT_EQ(1u, chosen_devices.size());
+  } else {
+    EXPECT_EQ(0u, chosen_devices.size());
+  }
 }
 
-TEST_F(HidServiceTest, OpenAndCloseHidConnection) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, OpenAndCloseHidConnection) {
+  auto service_creation_type = GetParam();
+  const auto& service = GetService(service_creation_type);
 
   auto device_info = CreateDeviceWithOneReport();
   ConnectDevice(*device_info);
@@ -263,7 +324,7 @@
   connection_client()->Bind(
       hid_connection_client.InitWithNewPipeAndPassReceiver());
 
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
 
   base::RunLoop run_loop;
   mojo::Remote<device::mojom::HidConnection> connection;
@@ -278,7 +339,7 @@
   run_loop.Run();
   EXPECT_TRUE(connection.is_connected());
 
-  EXPECT_TRUE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, true);
 
   // Destroying |connection| will also disconnect the watcher.
   connection.reset();
@@ -287,12 +348,12 @@
   // WebContents active frame count.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
 }
 
 // This test is disabled because it fails on the "linux-bfcache-rel" bot.
 // TODO(https://crbug.com/1232841): Re-enable this test.
-TEST_F(HidServiceTest, DISABLED_OpenAndNavigateCrossOrigin) {
+TEST_F(HidServiceRenderFrameHostTest, DISABLED_OpenAndNavigateCrossOrigin) {
   NavigateAndCommit(GURL(kTestUrl));
 
   mojo::Remote<blink::mojom::HidService> service;
@@ -333,7 +394,7 @@
   EXPECT_FALSE(connection.is_connected());
 }
 
-TEST_F(HidServiceTest, RegisterClient) {
+TEST_P(HidServiceTest, RegisterClient) {
   MockHidManagerClient mock_hid_manager_client;
 
   base::RunLoop device_added_loop;
@@ -348,11 +409,7 @@
       .WillOnce(Return(true))
       .WillOnce(Return(true));
 
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+  const auto& service = GetService(GetParam());
 
   mojo::PendingAssociatedRemote<device::mojom::HidManagerClient>
       hid_manager_client;
@@ -383,12 +440,9 @@
   device_removed_loop.Run();
 }
 
-TEST_F(HidServiceTest, RevokeDevicePermission) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, RevokeDevicePermission) {
+  auto service_creation_type = GetParam();
+  const auto& service = GetService(service_creation_type);
 
   // For now the device has permission.
   EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(true));
@@ -405,7 +459,7 @@
   connection_client()->Bind(
       hid_connection_client.InitWithNewPipeAndPassReceiver());
 
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
 
   base::RunLoop run_loop;
   mojo::Remote<device::mojom::HidConnection> connection;
@@ -419,7 +473,7 @@
           }));
   run_loop.Run();
 
-  EXPECT_TRUE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, true);
   EXPECT_TRUE(connection.is_connected());
 
   base::RunLoop disconnect_loop;
@@ -431,31 +485,25 @@
   hid_delegate().OnPermissionRevoked(origin);
 
   disconnect_loop.Run();
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
   EXPECT_FALSE(connection.is_connected());
 }
 
-TEST_F(HidServiceTest, RevokeDevicePermissionWithoutConnection) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, RevokeDevicePermissionWithoutConnection) {
+  auto service_creation_type = GetParam();
+  GetService(service_creation_type);
 
   // Simulate user revoking permission.
   url::Origin origin = url::Origin::Create(GURL(kTestUrl));
   hid_delegate().OnPermissionRevoked(origin);
 
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
 }
 
-TEST_F(HidServiceTest, DeviceRemovedDisconnect) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, DeviceRemovedDisconnect) {
+  auto service_creation_type = GetParam();
+  const auto& service = GetService(service_creation_type);
 
   // For now the device has permission.
   EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(true));
@@ -470,7 +518,7 @@
   connection_client()->Bind(
       hid_connection_client.InitWithNewPipeAndPassReceiver());
 
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
 
   base::RunLoop run_loop;
   mojo::Remote<device::mojom::HidConnection> connection;
@@ -484,7 +532,7 @@
           }));
   run_loop.Run();
 
-  EXPECT_TRUE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, true);
   EXPECT_TRUE(connection.is_connected());
 
   base::RunLoop disconnect_loop;
@@ -495,16 +543,13 @@
   DisconnectDevice(*device_info);
 
   disconnect_loop.Run();
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
   EXPECT_FALSE(connection.is_connected());
 }
 
-TEST_F(HidServiceTest, DeviceChangedDoesNotDisconnect) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, DeviceChangedDoesNotDisconnect) {
+  auto service_creation_type = GetParam();
+  const auto& service = GetService(service_creation_type);
 
   // Register the mock client with the service. Wait for GetDevices to return to
   // ensure the client has been set.
@@ -536,7 +581,7 @@
   connection_client()->Bind(
       hid_connection_client.InitWithNewPipeAndPassReceiver());
 
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
 
   base::RunLoop run_loop;
   mojo::Remote<device::mojom::HidConnection> connection;
@@ -549,7 +594,7 @@
           }));
   run_loop.Run();
 
-  EXPECT_TRUE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, true);
   EXPECT_TRUE(connection.is_connected());
 
   // Update the device info. Permissions are not affected.
@@ -560,7 +605,7 @@
   UpdateDevice(*updated_device_info);
 
   // Make sure the device is still connected.
-  EXPECT_TRUE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, true);
   EXPECT_TRUE(connection.is_connected());
 
   base::RunLoop disconnect_loop;
@@ -572,16 +617,13 @@
   hid_delegate().OnPermissionRevoked(origin);
 
   disconnect_loop.Run();
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
   EXPECT_FALSE(connection.is_connected());
 }
 
-TEST_F(HidServiceTest, UnblockedDeviceChangedToBlockedDisconnects) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, UnblockedDeviceChangedToBlockedDisconnects) {
+  auto service_creation_type = GetParam();
+  const auto& service = GetService(service_creation_type);
 
   // Register the mock client with the service. Wait for GetDevices to return to
   // ensure the client has been set.
@@ -613,7 +655,7 @@
   connection_client()->Bind(
       hid_connection_client.InitWithNewPipeAndPassReceiver());
 
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
 
   base::RunLoop connect_loop;
   mojo::Remote<device::mojom::HidConnection> connection;
@@ -626,7 +668,7 @@
           }));
   connect_loop.Run();
 
-  EXPECT_TRUE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, true);
   EXPECT_TRUE(connection.is_connected());
 
   // Update the device info. With the update, the device loses permission and
@@ -641,16 +683,12 @@
   UpdateDevice(*updated_device_info);
   disconnect_loop.Run();
 
-  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
   EXPECT_FALSE(connection.is_connected());
 }
 
-TEST_F(HidServiceTest, BlockedDeviceChangedToUnblockedDispatchesDeviceChanged) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+TEST_P(HidServiceTest, BlockedDeviceChangedToUnblockedDispatchesDeviceChanged) {
+  const auto& service = GetService(GetParam());
 
   // Register the mock client with the service. Wait for GetDevices to return to
   // ensure the client has been set.
@@ -694,17 +732,59 @@
   device_removed_loop.Run();
 }
 
-class HidServiceFidoTest : public HidServiceTest,
-                           public testing::WithParamInterface<bool> {};
+TEST_P(HidServiceTest, Forget) {
+  auto service_creation_type = GetParam();
+  const auto& service = GetService(service_creation_type);
+
+  // For now the device has permission.
+  EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(true));
+
+  // Create a new device.
+  auto device_info = device::mojom::HidDeviceInfo::New();
+  device_info->guid = kTestGuid;
+  ConnectDevice(*device_info);
+  EXPECT_CALL(hid_delegate(), GetDeviceInfo)
+      .WillOnce(Return(device_info.get()));
+
+  // Connect the device.
+  mojo::PendingRemote<device::mojom::HidConnectionClient> hid_connection_client;
+  connection_client()->Bind(
+      hid_connection_client.InitWithNewPipeAndPassReceiver());
+
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
+
+  TestFuture<mojo::PendingRemote<device::mojom::HidConnection>>
+      future_connection;
+  service->Connect(kTestGuid, std::move(hid_connection_client),
+                   future_connection.GetCallback());
+  mojo::Remote<device::mojom::HidConnection> connection(
+      future_connection.Take());
+
+  CheckWebContentsHidServiceConnectedState(service_creation_type, true);
+  EXPECT_TRUE(connection.is_connected());
+
+  base::RunLoop disconnect_loop;
+  connection.set_disconnect_handler(disconnect_loop.QuitClosure());
+
+  EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(false));
+  EXPECT_CALL(hid_delegate(), RevokeDevicePermission)
+      .WillOnce([this](content::BrowserContext* browser_context,
+                       const url::Origin& origin,
+                       const device::mojom::HidDeviceInfo& device) {
+        hid_delegate().OnPermissionRevoked(origin);
+      });
+  base::MockCallback<blink::mojom::HidService::ForgetCallback> forget_callback;
+  EXPECT_CALL(forget_callback, Run);
+  service->Forget(std::move(device_info), forget_callback.Get());
+
+  disconnect_loop.Run();
+  CheckWebContentsHidServiceConnectedState(service_creation_type, false);
+  EXPECT_FALSE(connection.is_connected());
+}
 
 TEST_P(HidServiceFidoTest, FidoDeviceAllowedWithPrivilegedOrigin) {
-  const bool is_fido_allowed = GetParam();
-  GURL test_url = GURL(kTestUrl);
-  NavigateAndCommit(test_url);
-
-  mojo::Remote<blink::mojom::HidService> service;
-  contents()->GetMainFrame()->GetHidService(
-      service.BindNewPipeAndPassReceiver());
+  const auto& service = GetService(std::get<0>(GetParam()));
+  const bool is_fido_allowed = std::get<1>(GetParam());
 
   // Register the mock client with the service.
   MockHidManagerClient mock_hid_manager_client;
@@ -890,8 +970,28 @@
   device_removed_loop.Run();
 }
 
-INSTANTIATE_TEST_SUITE_P(HidServiceFidoTests,
-                         HidServiceFidoTest,
-                         testing::Values(false, true));
+INSTANTIATE_TEST_SUITE_P(
+    HidServiceTests,
+    HidServiceTest,
+    testing::Values(kCreateUsingRenderFrameHost,
+                    kCreateUsingBrowserContextAndOrigin),
+    [](const ::testing::TestParamInfo<HidServiceCreationType>& info) {
+      return HidServiceCreationTypeToString(info.param);
+    });
+
+const bool kIsFidoAllowed[]{true, false};
+INSTANTIATE_TEST_SUITE_P(
+    HidServiceFidoTests,
+    HidServiceFidoTest,
+    testing::Combine(testing::Values(kCreateUsingRenderFrameHost,
+                                     kCreateUsingBrowserContextAndOrigin),
+                     testing::ValuesIn(kIsFidoAllowed)),
+    [](const ::testing::TestParamInfo<std::tuple<HidServiceCreationType, bool>>&
+           info) {
+      return base::StringPrintf(
+          "%s_%s",
+          HidServiceCreationTypeToString(std::get<0>(info.param)).c_str(),
+          std::get<1>(info.param) ? "FidoAllowed" : "FidoNotAllowed");
+    });
 
 }  // namespace content
diff --git a/content/browser/hid/hid_test_utils.cc b/content/browser/hid/hid_test_utils.cc
index 5f681dd..e6520d5 100644
--- a/content/browser/hid/hid_test_utils.cc
+++ b/content/browser/hid/hid_test_utils.cc
@@ -24,12 +24,12 @@
   return nullptr;
 }
 
-void MockHidDelegate::AddObserver(RenderFrameHost* frame, Observer* observer) {
+void MockHidDelegate::AddObserver(BrowserContext* browser_context,
+                                  Observer* observer) {
   observer_list_.AddObserver(observer);
 }
 
-void MockHidDelegate::RemoveObserver(RenderFrameHost* frame,
-                                     Observer* observer) {
+void MockHidDelegate::RemoveObserver(Observer* observer) {
   observer_list_.RemoveObserver(observer);
 }
 
diff --git a/content/browser/hid/hid_test_utils.h b/content/browser/hid/hid_test_utils.h
index 9ae19d0..ed96a08 100644
--- a/content/browser/hid/hid_test_utils.h
+++ b/content/browser/hid/hid_test_utils.h
@@ -37,8 +37,9 @@
       std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
       HidChooser::Callback callback) override;
 
-  void AddObserver(RenderFrameHost* frame, Observer* observer) override;
-  void RemoveObserver(RenderFrameHost* frame, Observer* observer) override;
+  void AddObserver(BrowserContext* browser_context,
+                   Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
 
   // MockHidDelegate does not register to receive device connection events. Use
   // these methods to broadcast device connections to all delegate observers.
@@ -49,22 +50,25 @@
 
   MOCK_METHOD0(RunChooserInternal,
                std::vector<device::mojom::HidDeviceInfoPtr>());
-  MOCK_METHOD1(CanRequestDevicePermission,
-               bool(RenderFrameHost* render_frame_host));
-  MOCK_METHOD2(HasDevicePermission,
-               bool(RenderFrameHost* render_frame_host,
+  MOCK_METHOD2(CanRequestDevicePermission,
+               bool(BrowserContext* browser_context,
+                    const url::Origin& origin));
+  MOCK_METHOD3(HasDevicePermission,
+               bool(BrowserContext* browser_context,
+                    const url::Origin& origin,
                     const device::mojom::HidDeviceInfo& device));
-  MOCK_METHOD2(RevokeDevicePermission,
-               void(RenderFrameHost* render_frame_host,
+  MOCK_METHOD3(RevokeDevicePermission,
+               void(BrowserContext* browser_context,
+                    const url::Origin& origin,
                     const device::mojom::HidDeviceInfo& device));
   MOCK_METHOD1(GetHidManager,
-               device::mojom::HidManager*(RenderFrameHost* render_frame_host));
+               device::mojom::HidManager*(BrowserContext* browser_context));
   MOCK_METHOD2(
       GetDeviceInfo,
-      const device::mojom::HidDeviceInfo*(RenderFrameHost* render_frame_host,
+      const device::mojom::HidDeviceInfo*(BrowserContext* browser_context,
                                           const std::string& guid));
   MOCK_METHOD2(IsFidoAllowedForOrigin,
-               bool(RenderFrameHost* render_frame_host,
+               bool(BrowserContext* browser_context,
                     const url::Origin& origin));
 
  private:
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index f6b2a9c..c93548b2 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -3561,18 +3561,14 @@
   subresource_loader_params_ = std::move(subresource_loader_params);
 
   if (render_frame_host_) {
-    // For sites that require a dedicated process, set the site URL now if it
-    // hasn't been set already. This will lock the process to that site, which
-    // will prevent other sites from incorrectly reusing this process. See
+    // Set the site URL now if it hasn't been set already. If the site requires
+    // a dedicated process, this will lock the process to that site, which will
+    // prevent other sites from incorrectly reusing this process. See
     // https://crbug.com/738634.
     SiteInstanceImpl* instance = render_frame_host_->GetSiteInstance();
-    const IsolationContext& isolation_context = instance->GetIsolationContext();
-    UrlInfo url_info = GetUrlInfo();
-    auto site_info = SiteInfo::Create(isolation_context, url_info);
     if (!instance->HasSite() &&
-        SiteInstanceImpl::ShouldAssignSiteForURL(common_params_->url) &&
-        site_info.RequiresDedicatedProcess(isolation_context)) {
-      instance->ConvertToDefaultOrSetSite(url_info);
+        SiteInstanceImpl::ShouldAssignSiteForURL(common_params_->url)) {
+      instance->ConvertToDefaultOrSetSite(GetUrlInfo());
     }
 
     // Since we've made the final pick for the RenderFrameHost above, the picked
@@ -3602,6 +3598,7 @@
     // will get a SiteInstance (regardless of process isolation) and tracking
     // will be handled by the existing pathway in
     // SiteInstanceImpl::SetSiteInfoInternal().
+    const IsolationContext& isolation_context = instance->GetIsolationContext();
     AddSameProcessOriginAgentClusterOptInIfNecessary(isolation_context,
                                                      GetURL());
 
@@ -3874,6 +3871,14 @@
           ? AssociatedSiteInstanceType::CURRENT
           : AssociatedSiteInstanceType::SPECULATIVE);
 
+  // Set the site URL now if it hasn't been set already.  It's possible to get
+  // here if we navigate to an error out of an initial "blank" SiteInstance.
+  // Also mark the process as used, since it will be hosting an error page.
+  SiteInstanceImpl* instance = render_frame_host_->GetSiteInstance();
+  if (!instance->HasSite())
+    instance->ConvertToDefaultOrSetSite(GetUrlInfo());
+  render_frame_host_->GetProcess()->SetIsUsed();
+
   // The check for WebUI should be performed only if error page isolation is
   // enabled for this failed navigation. It is possible for subframe error page
   // to be committed in a WebUI process as shown in https://crbug.com/944086.
diff --git a/content/browser/renderer_host/navigation_request_browsertest.cc b/content/browser/renderer_host/navigation_request_browsertest.cc
index fb033611..a746074 100644
--- a/content/browser/renderer_host/navigation_request_browsertest.cc
+++ b/content/browser/renderer_host/navigation_request_browsertest.cc
@@ -43,6 +43,7 @@
 #include "content/public/test/test_frame_navigation_observer.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
+#include "content/public/test/url_loader_interceptor.h"
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_content_browser_client.h"
@@ -3277,6 +3278,127 @@
   }
 }
 
+// Verify that when navigating to a site that doesn't require a dedicated
+// process from a initial siteless SiteInstance, the SiteInstance sets its site
+// at ready-to-commit time (rather than at DidCommitNavigation time).
+IN_PROC_BROWSER_TEST_F(NavigationRequestBrowserTest,
+                       SiteIsSetAtResponseTimeWithoutSiteIsolation) {
+  // A custom ContentBrowserClient to turn off strict site isolation.
+  class NoSiteIsolationContentBrowserClient : public ContentBrowserClient {
+   public:
+    bool ShouldEnableStrictSiteIsolation() override { return false; }
+  } no_site_isolation_client;
+
+  ContentBrowserClient* old_client =
+      SetBrowserClientForTesting(&no_site_isolation_client);
+
+  // The test should start in a blank shell with a siteless SiteInstance.
+  EXPECT_FALSE(
+      static_cast<SiteInstanceImpl*>(shell()->web_contents()->GetSiteInstance())
+          ->HasSite());
+
+  // Start a navigation and wait for response.  Note that this won't require a
+  // dedicated process due to the custom ContentBrowserClient.
+  GURL main_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
+  TestNavigationManager manager(shell()->web_contents(), main_url);
+  shell()->web_contents()->GetController().LoadURL(
+      main_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
+  EXPECT_TRUE(manager.WaitForResponse());
+
+  // At this point, the navigation should be processing the response but not
+  // committed yet. It should have already determined the final
+  // RenderFrameHost, which should just be the initial RenderFrameHost.
+  NavigationRequest* request =
+      static_cast<NavigationRequest*>(manager.GetNavigationHandle());
+  EXPECT_EQ(request->state(), NavigationRequest::WILL_PROCESS_RESPONSE);
+  EXPECT_EQ(shell()->web_contents()->GetMainFrame(),
+            request->GetRenderFrameHost());
+
+  // The navigation will stay in the initial SiteInstance, and that
+  // SiteInstance's site should now be set.
+  EXPECT_TRUE(
+      static_cast<SiteInstanceImpl*>(shell()->web_contents()->GetSiteInstance())
+          ->HasSite());
+
+  // The process should also be considered used at this point.
+  EXPECT_FALSE(
+      shell()->web_contents()->GetMainFrame()->GetProcess()->IsUnused());
+
+  SetBrowserClientForTesting(old_client);
+}
+
+// Verify that when navigation 1, which starts in an initial siteless
+// SiteInstance and results in an error page, races with navigation 2, which
+// requires a dedicated process and wants to reuse an existing process,
+// navigation 2 does not incorrectly reuse navigation 1's process.
+IN_PROC_BROWSER_TEST_F(NavigationRequestBrowserTest,
+                       ErrorPageMarksProcessAsUsed) {
+  // The scenario in this test originally led to a site isolation bypass only
+  // when error page isolation for main frames is turned off.  Do this via a
+  // custom ContentBrowserClient.
+  class NoErrorPageIsolationContentBrowserClient : public ContentBrowserClient {
+   public:
+    bool ShouldIsolateErrorPage(bool in_main_frame) override { return false; }
+  } no_error_isolation_client;
+
+  ContentBrowserClient* old_client =
+      SetBrowserClientForTesting(&no_error_isolation_client);
+
+  // Set the process limit to 1.  This will force main frame navigations to
+  // attempt to reuse existing processes.
+  RenderProcessHost::SetMaxRendererProcessCount(1);
+
+  // The test should start in a blank shell with a siteless SiteInstance.
+  EXPECT_FALSE(
+      static_cast<SiteInstanceImpl*>(shell()->web_contents()->GetSiteInstance())
+          ->HasSite());
+
+  // Set up a foo.com URL which will fail to load.
+  GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
+  std::unique_ptr<URLLoaderInterceptor> interceptor =
+      URLLoaderInterceptor::SetupRequestFailForURL(foo_url,
+                                                   net::ERR_CONNECTION_REFUSED);
+
+  // Set up a throttle that will be used to wait for WillFailRequest() and then
+  // defer the navigation.
+  TestNavigationThrottleInstaller installer(
+      shell()->web_contents(),
+      NavigationThrottle::PROCEED /* will_start_result */,
+      NavigationThrottle::PROCEED /* will_redirect_result */,
+      NavigationThrottle::DEFER /* will_fail_result */,
+      NavigationThrottle::PROCEED /* will_process_result */);
+
+  // Start a navigation to foo.com that will result in an error.
+  shell()->web_contents()->GetController().LoadURL(
+      foo_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
+
+  // Wait for WillFailRequest(). After this point, we will have picked the
+  // final RenderFrameHost for the error page.
+  installer.WaitForThrottleWillFail();
+
+  // Create a new tab and navigate it to a different site.  Ensure this site
+  // requires a dedicated process, even on Android.
+  GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
+  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+  policy->AddFutureIsolatedOrigins(
+      {url::Origin::Create(bar_url)},
+      ChildProcessSecurityPolicy::IsolatedOriginSource::TEST);
+  Shell* new_shell = CreateBrowser();
+  EXPECT_TRUE(NavigateToURL(new_shell, bar_url));
+
+  // Resume the error page navigation.  It should be able to finish without
+  // crashing.
+  installer.navigation_throttle()->ResumeNavigation();
+  EXPECT_FALSE(WaitForLoadStop(shell()->web_contents()));
+  EXPECT_TRUE(shell()->web_contents()->GetMainFrame()->IsErrorDocument());
+
+  // Ensure that bar.com didn't reuse the foo.com error page process.
+  EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetProcess(),
+            new_shell->web_contents()->GetMainFrame()->GetProcess());
+
+  SetBrowserClientForTesting(old_client);
+}
+
 using CSPEmbeddedEnforcementBrowserTest = NavigationRequestBrowserTest;
 
 IN_PROC_BROWSER_TEST_F(CSPEmbeddedEnforcementBrowserTest,
diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc
index cfc5263..dfa1e9a 100644
--- a/content/browser/renderer_host/navigator.cc
+++ b/content/browser/renderer_host/navigator.cc
@@ -503,11 +503,6 @@
   base::WeakPtr<RenderFrameHostImpl> old_frame_host =
       frame_tree_node->render_manager()->current_frame_host()->GetWeakPtr();
 
-  // At this point we have already chosen a SiteInstance for this navigation, so
-  // set OriginIsolationRequest to kNone in the conversion to UrlInfo below:
-  // this is done implicitly in the UrlInfoInit constructor.
-  const UrlInfo url_info(UrlInfoInit(params.url));
-
   if (auto& old_page_info = navigation_request->commit_params().old_page_info) {
     // This is a same-site main-frame navigation where we did a proactive
     // BrowsingInstance swap but we're reusing the old page's process, and we
@@ -564,13 +559,20 @@
     frame_tree_node->ResetForNavigation();
   }
 
-  // Update the site of the SiteInstance if it doesn't have one yet, unless
-  // assigning a site is not necessary for this URL. In that case, the
-  // SiteInstance can still be considered unused until a navigation to a real
-  // page.
+  // If the committing URL requires the SiteInstance's site to be assigned,
+  // that site assignment should've already happened at ReadyToCommit time. We
+  // should never get here with a SiteInstance that doesn't have a site
+  // assigned in that case.
   SiteInstanceImpl* site_instance = render_frame_host->GetSiteInstance();
   if (!site_instance->HasSite() &&
-      SiteInstanceImpl::ShouldAssignSiteForURL(url_info.url)) {
+      SiteInstanceImpl::ShouldAssignSiteForURL(params.url)) {
+    NOTREACHED() << "SiteInstance should have already set a site: "
+                 << params.url;
+    // TODO(alexmos): convert this to a CHECK and remove the fallback call to
+    // ConvertToDefaultOrSetSite() after verifying that this doesn't happen in
+    // practice.
+    base::debug::DumpWithoutCrashing();
+    const UrlInfo url_info(UrlInfoInit(params.url));
     site_instance->ConvertToDefaultOrSetSite(url_info);
   }
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 0ae2131..451b07c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -134,6 +134,7 @@
 #include "content/public/browser/render_widget_host_iterator.h"
 #include "content/public/browser/render_widget_host_observer.h"
 #include "content/public/browser/restore_type.h"
+#include "content/public/browser/site_isolation_policy.h"
 #include "content/public/browser/ssl_status.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -3876,14 +3877,29 @@
   bool renderer_started_hidden =
       params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB;
 
+  bool is_guest = IsGuest();
+
   // We usually create the new window in the same BrowsingInstance (group of
   // script-related windows), by passing in the current SiteInstance.  However,
-  // if the opener is being suppressed (in a non-guest), we do not provide
-  // a SiteInstance which causes a new one to get created in its own
-  // BrowsingInstance.
-  bool is_guest = IsGuest();
-  scoped_refptr<SiteInstance> site_instance =
-      params.opener_suppressed && !is_guest ? nullptr : source_site_instance;
+  // if the opener is being suppressed, we need to ensure that the new
+  // SiteInstance is created in a new BrowsingInstance.
+  scoped_refptr<SiteInstance> site_instance;
+  if (params.opener_suppressed) {
+    if (is_guest) {
+      // For site-isolated guests, noopener windows can be created in a new
+      // BrowsingInstance as long as they preserve the guest's StoragePartition.
+      // For non-isolated guests, we preserve the legacy behavior of keeping the
+      // new window in the old SiteInstance and BrowsingInstance.
+      site_instance = SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()
+                          ? SiteInstance::CreateForGuest(GetBrowserContext(),
+                                                         partition_config)
+                          : source_site_instance;
+    } else {
+      site_instance = SiteInstance::Create(GetBrowserContext());
+    }
+  } else {
+    site_instance = source_site_instance;
+  }
 
   // Create the new web contents. This will automatically create the new
   // WebContentsView. In the future, we may want to create the view separately.
diff --git a/content/browser/webui/web_ui_main_frame_observer.cc b/content/browser/webui/web_ui_main_frame_observer.cc
index 93532e80..b19058e6 100644
--- a/content/browser/webui/web_ui_main_frame_observer.cc
+++ b/content/browser/webui/web_ui_main_frame_observer.cc
@@ -55,18 +55,6 @@
 
 WebUIMainFrameObserver::~WebUIMainFrameObserver() = default;
 
-void WebUIMainFrameObserver::DidFinishNavigation(
-    NavigationHandle* navigation_handle) {
-  // Only disallow JavaScript on cross-document navigations in the main frame.
-  if (!navigation_handle->IsInPrimaryMainFrame() ||
-      !navigation_handle->HasCommitted() ||
-      navigation_handle->IsSameDocument()) {
-    return;
-  }
-
-  web_ui_->DisallowJavascriptOnAllHandlers();
-}
-
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 void WebUIMainFrameObserver::OnDidAddMessageToConsole(
     RenderFrameHost* source_frame,
@@ -181,6 +169,7 @@
 }
 
 void WebUIMainFrameObserver::PrimaryPageChanged(Page& page) {
+  web_ui_->DisallowJavascriptOnAllHandlers();
   web_ui_->GetController()->WebUIPrimaryPageChanged(page);
 }
 
diff --git a/content/browser/webui/web_ui_main_frame_observer.h b/content/browser/webui/web_ui_main_frame_observer.h
index 13474d0..dbe4258 100644
--- a/content/browser/webui/web_ui_main_frame_observer.h
+++ b/content/browser/webui/web_ui_main_frame_observer.h
@@ -40,7 +40,6 @@
   friend class WebUIMainFrameObserverTest;
 
   // Override from WebContentsObserver
-  void DidFinishNavigation(NavigationHandle* navigation_handle) override;
   void PrimaryPageChanged(Page& page) override;
 
 // TODO(crbug.com/1129544) This is currently disabled due to Windows DLL
diff --git a/content/public/browser/hid_delegate.h b/content/public/browser/hid_delegate.h
index 8836324..69637ecb 100644
--- a/content/public/browser/hid_delegate.h
+++ b/content/public/browser/hid_delegate.h
@@ -20,6 +20,7 @@
 
 namespace content {
 
+class BrowserContext;
 class RenderFrameHost;
 
 class CONTENT_EXPORT HidDelegate {
@@ -54,47 +55,47 @@
       std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
       HidChooser::Callback callback) = 0;
 
-  // Returns whether the main frame of |render_frame_host| has permission to
-  // request access to a device.
-  virtual bool CanRequestDevicePermission(
-      RenderFrameHost* render_frame_host) = 0;
+  // Returns whether `origin` has permission to request access to a device.
+  virtual bool CanRequestDevicePermission(BrowserContext* browser_context,
+                                          const url::Origin& origin) = 0;
 
-  // Returns whether the main frame of |render_frame_host| has permission to
-  // access |device|.
+  // Returns whether `origin` has permission to access `device`.
   virtual bool HasDevicePermission(
-      RenderFrameHost* render_frame_host,
+      BrowserContext* browser_context,
+      const url::Origin& origin,
       const device::mojom::HidDeviceInfo& device) = 0;
 
-  // Revoke `device` access permission to the main frame of `render_frame_host`.
+  // Revoke `device` access permission to `origin`.
   virtual void RevokeDevicePermission(
-      RenderFrameHost* render_frame_host,
+      BrowserContext* browser_context,
+      const url::Origin& origin,
       const device::mojom::HidDeviceInfo& device) = 0;
 
   // Returns an open connection to the HidManager interface owned by the
-  // embedder and being used to serve requests from |render_frame_host|.
+  // embedder and being used to serve requests associated with
+  // `browser_context`.
   //
   // Content and the embedder must use the same connection so that the embedder
   // can process connect/disconnect events for permissions management purposes
   // before they are delivered to content. Otherwise race conditions are
   // possible.
   virtual device::mojom::HidManager* GetHidManager(
-      RenderFrameHost* render_frame_host) = 0;
+      BrowserContext* browser_context) = 0;
 
   // Functions to manage the set of Observer instances registered to this
   // object.
-  virtual void AddObserver(RenderFrameHost* render_frame_host,
+  virtual void AddObserver(BrowserContext* browser_context,
                            Observer* observer) = 0;
-  virtual void RemoveObserver(RenderFrameHost* render_frame_host,
-                              Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
 
   // Returns true if |origin| is allowed to bypass the HID blocklist and
   // access reports contained in FIDO collections.
-  virtual bool IsFidoAllowedForOrigin(RenderFrameHost* render_frame_host,
+  virtual bool IsFidoAllowedForOrigin(BrowserContext* browser_context,
                                       const url::Origin& origin) = 0;
 
   // Gets the device info for a particular device, identified by its guid.
   virtual const device::mojom::HidDeviceInfo* GetDeviceInfo(
-      RenderFrameHost* render_frame_host,
+      BrowserContext* browser_context,
       const std::string& guid) = 0;
 };
 
diff --git a/content/renderer/agent_scheduling_group.cc b/content/renderer/agent_scheduling_group.cc
index 4d5a704..9ee3fbc 100644
--- a/content/renderer/agent_scheduling_group.cc
+++ b/content/renderer/agent_scheduling_group.cc
@@ -8,6 +8,10 @@
 #include <utility>
 
 #include "base/feature_list.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/sequence_bound.h"
 #include "base/types/pass_key.h"
 #include "content/common/agent_scheduling_group.mojom.h"
 #include "content/public/common/content_features.h"
@@ -16,6 +20,7 @@
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/render_view_impl.h"
 #include "content/services/shared_storage_worklet/public/mojom/shared_storage_worklet_service.mojom.h"
+#include "content/services/shared_storage_worklet/shared_storage_worklet_service_impl.h"
 #include "ipc/ipc_channel_mojo.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_sync_channel.h"
@@ -50,6 +55,50 @@
              : features::MBIMode::kLegacy;
 }
 
+// A thread for running shared storage worklet operations. It hosts a worklet
+// environment belonging to one Document. The object owns itself, cleaning up
+// when the worklet has shut down.
+class SelfOwnedSharedStorageWorkletThread {
+ public:
+  SelfOwnedSharedStorageWorkletThread(
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
+      mojo::PendingReceiver<
+          shared_storage_worklet::mojom::SharedStorageWorkletService> receiver)
+      : main_thread_runner_(std::move(main_thread_runner)) {
+    DCHECK(main_thread_runner_->BelongsToCurrentThread());
+
+    auto disconnect_handler = base::BindPostTask(
+        main_thread_runner_,
+        base::BindOnce(&SelfOwnedSharedStorageWorkletThread::
+                           OnSharedStorageWorkletServiceDestroyed,
+                       weak_factory_.GetWeakPtr()));
+
+    auto task_runner = base::ThreadPool::CreateSingleThreadTaskRunner(
+        {base::TaskPriority::BEST_EFFORT,
+         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+        base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+
+    // Initialize the worklet service in a new thread.
+    worklet_thread_ = base::SequenceBound<
+        shared_storage_worklet::SharedStorageWorkletServiceImpl>(
+        task_runner, std::move(receiver), std::move(disconnect_handler));
+  }
+
+ private:
+  void OnSharedStorageWorkletServiceDestroyed() {
+    DCHECK(main_thread_runner_->BelongsToCurrentThread());
+    worklet_thread_.Reset();
+    delete this;
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner_;
+
+  base::SequenceBound<shared_storage_worklet::SharedStorageWorkletServiceImpl>
+      worklet_thread_;
+
+  base::WeakPtrFactory<SelfOwnedSharedStorageWorkletThread> weak_factory_{this};
+};
+
 }  // namespace
 
 AgentSchedulingGroup::ReceiverData::ReceiverData(
@@ -258,8 +307,8 @@
 void AgentSchedulingGroup::CreateSharedStorageWorkletService(
     mojo::PendingReceiver<
         shared_storage_worklet::mojom::SharedStorageWorkletService> receiver) {
-  RenderThreadImpl& renderer = ToImpl(render_thread_);
-  renderer.CreateSharedStorageWorkletService(std::move(receiver));
+  new SelfOwnedSharedStorageWorkletThread(
+      agent_group_scheduler_->DefaultTaskRunner(), std::move(receiver));
 }
 
 void AgentSchedulingGroup::BindAssociatedInterfaces(
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 831db39..2e6a145 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2776,10 +2776,10 @@
 
   // The MHTML mime type should be same as the one we check in the browser
   // process's download_utils::MustDownload.
-  bool is_mhtml_archive =
-      base::LowerCaseEqualsASCII(response_head->mime_type,
-                                 "multipart/related") ||
-      base::LowerCaseEqualsASCII(response_head->mime_type, "message/rfc822");
+  bool is_mhtml_archive = base::EqualsCaseInsensitiveASCII(
+                              response_head->mime_type, "multipart/related") ||
+                          base::EqualsCaseInsensitiveASCII(
+                              response_head->mime_type, "message/rfc822");
   if (is_mhtml_archive && navigation_params->body_loader) {
     // Load full mhtml archive before committing navigation.
     // We need this to retrieve the document mime type prior to committing.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 3aaabfa..eb49980 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -39,7 +39,6 @@
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
-#include "base/threading/sequence_bound.h"
 #include "base/threading/simple_thread.h"
 #include "base/threading/thread_local.h"
 #include "base/threading/thread_restrictions.h"
@@ -95,7 +94,6 @@
 #include "content/renderer/variations_render_thread_observer.h"
 #include "content/renderer/worker/embedded_shared_worker_stub.h"
 #include "content/renderer/worker/worker_thread_registry.h"
-#include "content/services/shared_storage_worklet/shared_storage_worklet_service_impl.h"
 #include "device/gamepad/public/cpp/gamepads.h"
 #include "gin/public/debug.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -339,50 +337,6 @@
       switches::kSingleProcess);
 }
 
-// A thread for running shared storage worklet operations. It hosts a worklet
-// environment belonging to one Document. The object owns itself, cleaning up
-// when the worklet has shut down.
-class SelfOwnedSharedStorageWorkletThread {
- public:
-  SelfOwnedSharedStorageWorkletThread(
-      scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
-      mojo::PendingReceiver<
-          shared_storage_worklet::mojom::SharedStorageWorkletService> receiver)
-      : main_thread_runner_(std::move(main_thread_runner)) {
-    DCHECK(main_thread_runner_->BelongsToCurrentThread());
-
-    auto disconnect_handler = base::BindPostTask(
-        main_thread_runner_,
-        base::BindOnce(&SelfOwnedSharedStorageWorkletThread::
-                           OnSharedStorageWorkletServiceDestroyed,
-                       weak_factory_.GetWeakPtr()));
-
-    auto task_runner = base::ThreadPool::CreateSingleThreadTaskRunner(
-        {base::TaskPriority::BEST_EFFORT,
-         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-        base::SingleThreadTaskRunnerThreadMode::DEDICATED);
-
-    // Initialize the worklet service in a new thread.
-    worklet_thread_ = base::SequenceBound<
-        shared_storage_worklet::SharedStorageWorkletServiceImpl>(
-        task_runner, std::move(receiver), std::move(disconnect_handler));
-  }
-
- private:
-  void OnSharedStorageWorkletServiceDestroyed() {
-    DCHECK(main_thread_runner_->BelongsToCurrentThread());
-    worklet_thread_.Reset();
-    delete this;
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner_;
-
-  base::SequenceBound<shared_storage_worklet::SharedStorageWorkletServiceImpl>
-      worklet_thread_;
-
-  base::WeakPtrFactory<SelfOwnedSharedStorageWorkletThread> weak_factory_{this};
-};
-
 }  // namespace
 
 RenderThreadImpl::HistogramCustomizer::HistogramCustomizer() {
@@ -910,13 +864,6 @@
   return video_frame_compositor_task_runner_;
 }
 
-void RenderThreadImpl::CreateSharedStorageWorkletService(
-    mojo::PendingReceiver<
-        shared_storage_worklet::mojom::SharedStorageWorkletService> receiver) {
-  new SelfOwnedSharedStorageWorkletThread(
-      GetWebMainThreadScheduler()->DefaultTaskRunner(), std::move(receiver));
-}
-
 void RenderThreadImpl::InitializeWebKit(mojo::BinderMap* binders) {
   DCHECK(!blink_platform_impl_);
 
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index b7b11f7..3c5c2cb 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -396,10 +396,6 @@
   scoped_refptr<base::SingleThreadTaskRunner>
   CreateVideoFrameCompositorTaskRunner();
 
-  void CreateSharedStorageWorkletService(
-      mojo::PendingReceiver<
-          shared_storage_worklet::mojom::SharedStorageWorkletService> receiver);
-
   // The time the run loop started for this thread.
   base::TimeTicks run_loop_start_time() const { return run_loop_start_time_; }
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index 3fc22d63..47d92db5 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -108,6 +108,10 @@
 crbug.com/1305020 [ mac asan intel-0xd26 ] ContextLost_WebGPUContextLostFromGPUProcessExit [ Skip ]
 crbug.com/1305020 [ mac nvidia-0xfe9 ] ContextLost_WebGPUContextLostFromGPUProcessExit [ Skip ]
 
+# Flaky hangs on Mac Debug NVIDIA, stalling the test suite for 40 minutes.
+# This hardware configuration is effectively obsolete; this won't be investigated.
+crbug.com/1161570 [ mac nvidia debug ] GpuCrash_InfoForHardwareGpu [ Skip ]
+crbug.com/1161570 [ mac nvidia debug ] GpuCrash_InfoForDualHardwareGpus [ Skip ]
 
 ###############################
 # Temporary Skip Expectations #
@@ -130,9 +134,9 @@
 crbug.com/923134 [ mac ] ContextLost_WebGL2Blocked [ Skip ]
 crbug.com/923134 [ android ] ContextLost_WebGL2Blocked [ Skip ]
 
-# Flaky hangs on Mac Debug NVIDIA, stalling the test suite for 40 minutes
-crbug.com/1161570 [ mac nvidia debug ] GpuCrash_InfoForHardwareGpu [ Skip ]
-crbug.com/1161570 [ mac nvidia debug ] GpuCrash_InfoForDualHardwareGpus [ Skip ]
+# Flaky hangs on Mac Debug AMD, stalling the test suite for 60 minutes.
+crbug.com/1327754 [ mac amd debug ] GpuCrash_InfoForHardwareGpu [ Skip ]
+crbug.com/1327754 [ mac amd debug ] GpuCrash_InfoForDualHardwareGpus [ Skip ]
 
 ###################
 # Failures/Flakes #
@@ -162,8 +166,6 @@
 crbug.com/1205899 [ linux display-server-wayland ] ContextLost_WebGLBlockedAfterJSNavigation [ RetryOnFailure ]
 
 # GpuCrash_Info* flakes on Mac Debug (AMD, Intel)
-crbug.com/1276598 [ mac amd debug passthrough ] GpuCrash_InfoForHardwareGpu [ RetryOnFailure ]
-crbug.com/1276598 [ mac amd debug passthrough ] GpuCrash_InfoForDualHardwareGpus [ RetryOnFailure ]
 crbug.com/1297151 [ mac intel debug no-passthrough ] GpuCrash_InfoForHardwareGpu [ RetryOnFailure ]
 
 # Flaky on Fuchsia
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md
index 2cec12ca..605209439 100644
--- a/docs/updater/functional_spec.md
+++ b/docs/updater/functional_spec.md
@@ -714,6 +714,8 @@
 *   `use_cup`: Whether CUP is used at all.
 *   `cup_public_key`: An unarmored PEM-encoded ASN.1 SubjectPublicKeyInfo with
     the ecPublicKey algorithm and containing a named elliptic curve.
+*   `group_policies`: Allows setting group policies, such as install and update
+    policies.
 
 Windows: these overrides exist in registry, under
 `HKLM\Software\{Company}\Update\Clients\ClientState\UpdateDev`.
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index a2948102..90632c2 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -156,7 +156,6 @@
       "//extensions/browser/api/cec_private",
       "//extensions/browser/api/diagnostics",
       "//extensions/browser/api/virtual_keyboard",
-      "//extensions/browser/api/vpn_provider",
       "//extensions/browser/api/webcam_private",
     ]
   }
diff --git a/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc b/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc
index 2f3f3eef..1f32ffe 100644
--- a/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc
+++ b/extensions/browser/api/declarative_net_request/regex_rules_matcher.cc
@@ -231,9 +231,9 @@
                        });
                      }));
 
-  // Convert |strings_to_match| to StringPatterns. This is necessary to use
-  // url_matcher::SubstringSetMatcher.
-  std::vector<base::StringPattern> patterns;
+  // Convert |strings_to_match| to MatcherStringPatterns. This is necessary to
+  // use url_matcher::SubstringSetMatcher.
+  std::vector<base::MatcherStringPattern> patterns;
   patterns.reserve(strings_to_match.size());
 
   for (size_t i = 0; i < strings_to_match.size(); ++i)
diff --git a/extensions/browser/browser_context_keyed_service_factories.cc b/extensions/browser/browser_context_keyed_service_factories.cc
index 7204a52..143c1a3 100644
--- a/extensions/browser/browser_context_keyed_service_factories.cc
+++ b/extensions/browser/browser_context_keyed_service_factories.cc
@@ -50,7 +50,6 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.h"
-#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
 #include "extensions/browser/api/webcam_private/webcam_private_api.h"
 #endif
 
@@ -102,7 +101,6 @@
   UsbDeviceManager::GetFactoryInstance();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   VirtualKeyboardAPI::GetFactoryInstance();
-  chromeos::VpnServiceFactory::GetInstance();
   WebcamPrivateAPI::GetFactoryInstance();
 #endif
   WebRequestAPI::GetFactoryInstance();
diff --git a/extensions/common/api/schema.gni b/extensions/common/api/schema.gni
index ba1d6d3..0474a56 100644
--- a/extensions/common/api/schema.gni
+++ b/extensions/common/api/schema.gni
@@ -76,7 +76,6 @@
     "diagnostics.idl",
     "lock_screen_data.idl",
     "media_perception_private.idl",
-    "vpn_provider.idl",
     "webcam_private.idl",
   ]
 }
diff --git a/gpu/config/gpu_test_expectations_parser.cc b/gpu/config/gpu_test_expectations_parser.cc
index ff6b4f8..4602dce 100644
--- a/gpu/config/gpu_test_expectations_parser.cc
+++ b/gpu/config/gpu_test_expectations_parser.cc
@@ -174,7 +174,7 @@
     return kConfigGPUDeviceID;
 
   for (int32_t i = 0; i < kNumberOfExactMatchTokens; ++i) {
-    if (base::LowerCaseEqualsASCII(word, kTokenData[i].name))
+    if (base::EqualsCaseInsensitiveASCII(word, kTokenData[i].name))
       return static_cast<Token>(i);
   }
   return kTokenWord;
diff --git a/media/filters/audio_video_metadata_extractor.cc b/media/filters/audio_video_metadata_extractor.cc
index 69ff508..af9e84f 100644
--- a/media/filters/audio_video_metadata_extractor.cc
+++ b/media/filters/audio_video_metadata_extractor.cc
@@ -24,7 +24,7 @@
 bool ExtractString(AVDictionaryEntry* tag,
                    const char* expected_key,
                    std::string* destination) {
-  if (!base::LowerCaseEqualsASCII(std::string(tag->key), expected_key))
+  if (!base::EqualsCaseInsensitiveASCII(std::string(tag->key), expected_key))
     return false;
 
   if (destination->empty())
@@ -37,7 +37,7 @@
 bool ExtractInt(AVDictionaryEntry* tag,
                 const char* expected_key,
                 int* destination) {
-  if (!base::LowerCaseEqualsASCII(std::string(tag->key), expected_key))
+  if (!base::EqualsCaseInsensitiveASCII(std::string(tag->key), expected_key))
     return false;
 
   int temporary = -1;
diff --git a/mojo/core/platform_shared_memory_mapping.cc b/mojo/core/platform_shared_memory_mapping.cc
index 989f221c..6057a80e 100644
--- a/mojo/core/platform_shared_memory_mapping.cc
+++ b/mojo/core/platform_shared_memory_mapping.cc
@@ -4,100 +4,90 @@
 
 #include "mojo/core/platform_shared_memory_mapping.h"
 
+#include <type_traits>
 #include <utility>
 
-#include "base/check_op.h"
+#include "base/check.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "base/memory/writable_shared_memory_region.h"
-#include "base/system/sys_info.h"
 #include "build/build_config.h"
 
-#if BUILDFLAG(IS_NACL)
-// For getpagesize() on NaCl.
-#include <unistd.h>
-#endif
-
 namespace mojo {
 namespace core {
 
-namespace {
-
-size_t GetPageSize() {
-#if BUILDFLAG(IS_NACL)
-  // base::SysInfo isn't available under NaCl.
-  return getpagesize();
-#else
-  return base::SysInfo::VMAllocationGranularity();
-#endif
-}
-
-}  // namespace
-
 PlatformSharedMemoryMapping::PlatformSharedMemoryMapping(
     base::subtle::PlatformSharedMemoryRegion* region,
     size_t offset,
-    size_t length)
-    : type_(region->GetMode() ==
-                    base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly
-                ? Type::kReadOnly
-                : Type::kWritable),
-      offset_(offset),
-      length_(length) {
-  // Mojo shared buffers can be mapped at any offset, but //base shared memory
-  // regions must be mapped at a page boundary. We calculate the nearest whole
-  // page offset and map from there.
-  size_t offset_rounding = offset_ % GetPageSize();
-  off_t real_offset = static_cast<off_t>(offset_ - offset_rounding);
-  size_t real_length = length_ + offset_rounding;
-  void* mapped_memory = nullptr;
-  if (type_ == Type::kReadOnly) {
-    auto read_only_region =
-        base::ReadOnlySharedMemoryRegion::Deserialize(std::move(*region));
-    auto read_only_mapping = read_only_region.MapAt(real_offset, real_length);
-    mapped_memory = const_cast<void*>(read_only_mapping.memory());
-    mapping_ = std::make_unique<base::ReadOnlySharedMemoryMapping>(
-        std::move(read_only_mapping));
-    *region = base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
-        std::move(read_only_region));
-  } else if (region->GetMode() ==
-             base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe) {
-    auto unsafe_region =
-        base::UnsafeSharedMemoryRegion::Deserialize(std::move(*region));
-    auto writable_mapping = unsafe_region.MapAt(real_offset, real_length);
-    mapped_memory = writable_mapping.memory();
-    mapping_ = std::make_unique<base::WritableSharedMemoryMapping>(
-        std::move(writable_mapping));
-    *region = base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
-        std::move(unsafe_region));
-  } else {
-    DCHECK_EQ(region->GetMode(),
-              base::subtle::PlatformSharedMemoryRegion::Mode::kWritable);
-    auto writable_region =
-        base::WritableSharedMemoryRegion::Deserialize(std::move(*region));
-    auto writable_mapping = writable_region.MapAt(real_offset, real_length);
-    mapped_memory = writable_mapping.memory();
-    mapping_ = std::make_unique<base::WritableSharedMemoryMapping>(
-        std::move(writable_mapping));
-    *region = base::WritableSharedMemoryRegion::TakeHandleForSerialization(
-        std::move(writable_region));
+    size_t length) {
+  switch (region->GetMode()) {
+    case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly: {
+      auto read_only_region =
+          base::ReadOnlySharedMemoryRegion::Deserialize(std::move(*region));
+      mapping_ = read_only_region.MapAt(offset, length);
+      *region = base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
+          std::move(read_only_region));
+      return;
+    }
+    case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe: {
+      auto unsafe_region =
+          base::UnsafeSharedMemoryRegion::Deserialize(std::move(*region));
+      mapping_ = unsafe_region.MapAt(offset, length);
+      *region = base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+          std::move(unsafe_region));
+      return;
+    }
+    case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable: {
+      auto writable_region =
+          base::WritableSharedMemoryRegion::Deserialize(std::move(*region));
+      mapping_ = writable_region.MapAt(offset, length);
+      *region = base::WritableSharedMemoryRegion::TakeHandleForSerialization(
+          std::move(writable_region));
+      return;
+    }
   }
-
-  base_ = static_cast<char*>(mapped_memory) + offset_rounding;
+  CHECK(false);
 }
 
 PlatformSharedMemoryMapping::~PlatformSharedMemoryMapping() = default;
 
 bool PlatformSharedMemoryMapping::IsValid() const {
-  return mapping_ && mapping_->IsValid();
+  return absl::visit(
+      [](const auto& member) {
+        using T = std::decay_t<decltype(member)>;
+        if constexpr (std::is_same_v<T, absl::monostate>) {
+          return false;
+        } else {
+          return member.IsValid();
+        }
+      },
+      mapping_);
 }
 
 void* PlatformSharedMemoryMapping::GetBase() const {
-  return base_;
+  return absl::visit(
+      [](const auto& member) -> void* {
+        using T = std::decay_t<decltype(member)>;
+        if constexpr (std::is_same_v<T, absl::monostate>) {
+          return nullptr;
+        } else {
+          return const_cast<void*>(member.memory());
+        }
+      },
+      mapping_);
 }
 
 size_t PlatformSharedMemoryMapping::GetLength() const {
-  return length_;
+  return absl::visit(
+      [](const auto& member) -> size_t {
+        using T = std::decay_t<decltype(member)>;
+        if constexpr (std::is_same_v<T, absl::monostate>) {
+          return 0;
+        } else {
+          return member.size();
+        }
+      },
+      mapping_);
 }
 
 }  // namespace core
diff --git a/mojo/core/platform_shared_memory_mapping.h b/mojo/core/platform_shared_memory_mapping.h
index 4726294..5a5b2b5 100644
--- a/mojo/core/platform_shared_memory_mapping.h
+++ b/mojo/core/platform_shared_memory_mapping.h
@@ -7,11 +7,10 @@
 
 #include <stddef.h>
 
-#include <memory>
-
 #include "base/memory/platform_shared_memory_region.h"
 #include "base/memory/shared_memory_mapping.h"
 #include "mojo/core/system_impl_export.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace mojo {
 namespace core {
@@ -27,11 +26,6 @@
 // offsets for convenience.
 class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedMemoryMapping {
  public:
-  enum class Type {
-    kReadOnly,
-    kWritable,
-  };
-
   PlatformSharedMemoryMapping(base::subtle::PlatformSharedMemoryRegion* region,
                               size_t offset,
                               size_t length);
@@ -48,11 +42,10 @@
   size_t GetLength() const;
 
  private:
-  const Type type_;
-  const size_t offset_;
-  const size_t length_;
-  void* base_ = nullptr;
-  std::unique_ptr<base::SharedMemoryMapping> mapping_;
+  absl::variant<absl::monostate,
+                base::ReadOnlySharedMemoryMapping,
+                base::WritableSharedMemoryMapping>
+      mapping_;
 };
 
 }  // namespace core
diff --git a/pdf/url_loader_wrapper_impl.cc b/pdf/url_loader_wrapper_impl.cc
index 9cb596e..5c67596 100644
--- a/pdf/url_loader_wrapper_impl.cc
+++ b/pdf/url_loader_wrapper_impl.cc
@@ -78,7 +78,7 @@
 bool GetByteRangeFromHeaders(const std::string& headers, int* start, int* end) {
   net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
   while (it.GetNext()) {
-    if (base::LowerCaseEqualsASCII(it.name_piece(), "content-range")) {
+    if (base::EqualsCaseInsensitiveASCII(it.name_piece(), "content-range")) {
       if (GetByteRangeFromStr(it.values().c_str(), start, end))
         return true;
     }
@@ -198,13 +198,14 @@
                                     response_headers.end(), "\n");
   while (it.GetNext()) {
     base::StringPiece name = it.name_piece();
-    if (base::LowerCaseEqualsASCII(name, "content-length")) {
+    if (base::EqualsCaseInsensitiveASCII(name, "content-length")) {
       content_length_ = atoi(it.values().c_str());
-    } else if (base::LowerCaseEqualsASCII(name, "accept-ranges")) {
-      accept_ranges_bytes_ = base::LowerCaseEqualsASCII(it.values(), "bytes");
-    } else if (base::LowerCaseEqualsASCII(name, "content-encoding")) {
+    } else if (base::EqualsCaseInsensitiveASCII(name, "accept-ranges")) {
+      accept_ranges_bytes_ =
+          base::EqualsCaseInsensitiveASCII(it.values(), "bytes");
+    } else if (base::EqualsCaseInsensitiveASCII(name, "content-encoding")) {
       content_encoded_ = true;
-    } else if (base::LowerCaseEqualsASCII(name, "content-type")) {
+    } else if (base::EqualsCaseInsensitiveASCII(name, "content-type")) {
       content_type_ = it.values();
       size_t semi_colon_pos = content_type_.find(';');
       if (semi_colon_pos != std::string::npos) {
@@ -221,9 +222,9 @@
           is_multipart_ = !multipart_boundary_.empty();
         }
       }
-    } else if (base::LowerCaseEqualsASCII(name, "content-disposition")) {
+    } else if (base::EqualsCaseInsensitiveASCII(name, "content-disposition")) {
       content_disposition_ = it.values();
-    } else if (base::LowerCaseEqualsASCII(name, "content-range")) {
+    } else if (base::EqualsCaseInsensitiveASCII(name, "content-range")) {
       int start = 0;
       int end = 0;
       if (GetByteRangeFromStr(it.values().c_str(), &start, &end)) {
diff --git a/remoting/codec/webrtc_video_encoder_vpx.cc b/remoting/codec/webrtc_video_encoder_vpx.cc
index aa631c2d..657116b 100644
--- a/remoting/codec/webrtc_video_encoder_vpx.cc
+++ b/remoting/codec/webrtc_video_encoder_vpx.cc
@@ -143,7 +143,7 @@
   // Request the lowest-CPU usage that VP9 supports, which depends on whether
   // we are encoding lossy or lossless.
   // Note that this knob uses the same parameter name as VP8.
-  int cpu_used = lossless_encode ? 5 : 8;
+  int cpu_used = lossless_encode ? 5 : 9;
   vpx_codec_err_t ret = vpx_codec_control(codec, VP8E_SET_CPUUSED, cpu_used);
   DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set CPUUSED";
 
diff --git a/services/network/public/cpp/header_util.cc b/services/network/public/cpp/header_util.cc
index 6724a79..c8d3353 100644
--- a/services/network/public/cpp/header_util.cc
+++ b/services/network/public/cpp/header_util.cc
@@ -176,7 +176,7 @@
                                           &content_type_options);
   }
   bool sniffing_blocked =
-      base::LowerCaseEqualsASCII(content_type_options, "nosniff");
+      base::EqualsCaseInsensitiveASCII(content_type_options, "nosniff");
   bool we_would_like_to_sniff =
       net::ShouldSniffMimeType(url, response.mime_type);
 
diff --git a/services/network/public/cpp/x_frame_options_parser.cc b/services/network/public/cpp/x_frame_options_parser.cc
index e288d20..88b1a2d 100644
--- a/services/network/public/cpp/x_frame_options_parser.cc
+++ b/services/network/public/cpp/x_frame_options_parser.cc
@@ -27,11 +27,11 @@
     base::StringPiece trimmed =
         base::TrimWhitespaceASCII(value, base::TRIM_ALL);
 
-    if (base::LowerCaseEqualsASCII(trimmed, "deny"))
+    if (base::EqualsCaseInsensitiveASCII(trimmed, "deny"))
       current = mojom::XFrameOptionsValue::kDeny;
-    else if (base::LowerCaseEqualsASCII(trimmed, "allowall"))
+    else if (base::EqualsCaseInsensitiveASCII(trimmed, "allowall"))
       current = mojom::XFrameOptionsValue::kAllowAll;
-    else if (base::LowerCaseEqualsASCII(trimmed, "sameorigin"))
+    else if (base::EqualsCaseInsensitiveASCII(trimmed, "sameorigin"))
       current = mojom::XFrameOptionsValue::kSameOrigin;
 
     if (result == mojom::XFrameOptionsValue::kNone)
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index f135a46..8dbe6ba9 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1911,7 +1911,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R104-14829.0.0",
+        "cros_img": "atlas-release/R104-14844.0.0",
         "name": "lacros_all_tast_tests ATLAS_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -1943,7 +1943,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R102-14695.25.0",
+        "cros_img": "atlas-release/R102-14695.68.0",
         "name": "lacros_all_tast_tests ATLAS_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -1959,7 +1959,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R104-14829.0.0",
+        "cros_img": "eve-release/R104-14844.0.0",
         "name": "lacros_all_tast_tests EVE_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -1991,7 +1991,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R102-14695.25.0",
+        "cros_img": "eve-release/R102-14695.55.0",
         "name": "lacros_all_tast_tests EVE_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2052,7 +2052,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R104-14829.0.0",
+        "cros_img": "hana-release/R104-14844.0.0",
         "name": "lacros_all_tast_tests HANA_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2084,7 +2084,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R102-14695.25.0",
+        "cros_img": "hana-release/R102-14695.68.0",
         "name": "lacros_all_tast_tests HANA_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2100,7 +2100,7 @@
       {
         "args": [],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R104-14829.0.0",
+        "cros_img": "jacuzzi-release/R104-14844.0.0",
         "name": "lacros_all_tast_tests JACUZZI_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2132,7 +2132,7 @@
       {
         "args": [],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R102-14695.25.0",
+        "cros_img": "jacuzzi-release/R102-14695.68.0",
         "name": "lacros_all_tast_tests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2150,7 +2150,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R104-14829.0.0",
+        "cros_img": "hana-release/R104-14844.0.0",
         "name": "ozone_unittests HANA_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2184,7 +2184,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R102-14695.25.0",
+        "cros_img": "hana-release/R102-14695.68.0",
         "name": "ozone_unittests HANA_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2201,7 +2201,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R104-14829.0.0",
+        "cros_img": "jacuzzi-release/R104-14844.0.0",
         "name": "ozone_unittests JACUZZI_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2235,7 +2235,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R102-14695.25.0",
+        "cros_img": "jacuzzi-release/R102-14695.68.0",
         "name": "ozone_unittests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2252,7 +2252,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R104-14829.0.0",
+        "cros_img": "hana-release/R104-14844.0.0",
         "name": "viz_unittests HANA_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2286,7 +2286,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "hana",
-        "cros_img": "hana-release/R102-14695.25.0",
+        "cros_img": "hana-release/R102-14695.68.0",
         "name": "viz_unittests HANA_RELEASE_BETA",
         "resultdb": {
           "enable": true,
@@ -2303,7 +2303,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R104-14829.0.0",
+        "cros_img": "jacuzzi-release/R104-14844.0.0",
         "name": "viz_unittests JACUZZI_RELEASE_LKGM",
         "resultdb": {
           "enable": true,
@@ -2337,7 +2337,7 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
         ],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R102-14695.25.0",
+        "cros_img": "jacuzzi-release/R102-14695.68.0",
         "name": "viz_unittests JACUZZI_RELEASE_BETA",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index 801ffb4..a2dde892 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1126,7 +1126,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R104-14829.0.0",
+        "cros_img": "octopus-release/R104-14844.0.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1150,7 +1150,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R102-14695.55.0",
+        "cros_img": "octopus-release/R102-14695.68.0",
         "name": "lacros_fyi_tast_tests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1174,7 +1174,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R104-14829.0.0",
+        "cros_img": "octopus-release/R104-14844.0.0",
         "name": "ozone_unittests OCTOPUS_RELEASE_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1196,7 +1196,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R102-14695.55.0",
+        "cros_img": "octopus-release/R102-14695.68.0",
         "name": "ozone_unittests OCTOPUS_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1226,7 +1226,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R104-14829.0.0",
+        "cros_img": "strongbad-release/R104-14844.0.0",
         "name": "lacros_all_tast_tests STRONGBAD_RELEASE_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1250,7 +1250,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R102-14695.55.0",
+        "cros_img": "strongbad-release/R102-14695.68.0",
         "name": "lacros_all_tast_tests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1274,7 +1274,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R104-14829.0.0",
+        "cros_img": "strongbad-release/R104-14844.0.0",
         "name": "ozone_unittests STRONGBAD_RELEASE_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1296,7 +1296,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R102-14695.55.0",
+        "cros_img": "strongbad-release/R102-14695.68.0",
         "name": "ozone_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1318,7 +1318,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R104-14829.0.0",
+        "cros_img": "strongbad-release/R104-14844.0.0",
         "name": "viz_unittests STRONGBAD_RELEASE_LKGM",
         "swarming": {},
         "test": "viz_unittests",
@@ -1340,7 +1340,7 @@
       {
         "args": [],
         "cros_board": "strongbad",
-        "cros_img": "strongbad-release/R102-14695.55.0",
+        "cros_img": "strongbad-release/R102-14695.68.0",
         "name": "viz_unittests STRONGBAD_RELEASE_BETA",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index eb5c44cf..72aa16e3 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -895,8 +895,8 @@
   'CROS_ATLAS_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '104.0.5061.0',
-      'cros_img': 'atlas-release/R104-14829.0.0',
+      'cros_chrome_version': '104.0.5065.0',
+      'cros_img': 'atlas-release/R104-14844.0.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_RELEASE_LKGM',
@@ -913,8 +913,8 @@
   'CROS_ATLAS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '102.0.5005.22',
-      'cros_img': 'atlas-release/R102-14695.25.0',
+      'cros_chrome_version': '102.0.5005.56',
+      'cros_img': 'atlas-release/R102-14695.68.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_RELEASE_BETA',
@@ -922,8 +922,8 @@
   'CROS_EVE_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '104.0.5061.0',
-      'cros_img': 'eve-release/R104-14829.0.0',
+      'cros_chrome_version': '104.0.5065.0',
+      'cros_img': 'eve-release/R104-14844.0.0',
     },
     'enabled': True,
     'identifier': 'EVE_RELEASE_LKGM',
@@ -940,8 +940,8 @@
   'CROS_EVE_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '102.0.5005.22',
-      'cros_img': 'eve-release/R102-14695.25.0',
+      'cros_chrome_version': '102.0.5005.48',
+      'cros_img': 'eve-release/R102-14695.55.0',
     },
     'enabled': True,
     'identifier': 'EVE_RELEASE_BETA',
@@ -958,8 +958,8 @@
   'CROS_HANA_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'hana',
-      'cros_chrome_version': '104.0.5061.0',
-      'cros_img': 'hana-release/R104-14829.0.0',
+      'cros_chrome_version': '104.0.5065.0',
+      'cros_img': 'hana-release/R104-14844.0.0',
     },
     'enabled': True,
     'identifier': 'HANA_RELEASE_LKGM',
@@ -976,8 +976,8 @@
   'CROS_HANA_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'hana',
-      'cros_chrome_version': '102.0.5005.22',
-      'cros_img': 'hana-release/R102-14695.25.0',
+      'cros_chrome_version': '102.0.5005.56',
+      'cros_img': 'hana-release/R102-14695.68.0',
     },
     'enabled': True,
     'identifier': 'HANA_RELEASE_BETA',
@@ -985,8 +985,8 @@
   'CROS_JACUZZI_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_chrome_version': '104.0.5061.0',
-      'cros_img': 'jacuzzi-release/R104-14829.0.0',
+      'cros_chrome_version': '104.0.5065.0',
+      'cros_img': 'jacuzzi-release/R104-14844.0.0',
     },
     'enabled': True,
     'identifier': 'JACUZZI_RELEASE_LKGM',
@@ -1003,8 +1003,8 @@
   'CROS_JACUZZI_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'jacuzzi',
-      'cros_chrome_version': '102.0.5005.22',
-      'cros_img': 'jacuzzi-release/R102-14695.25.0',
+      'cros_chrome_version': '102.0.5005.56',
+      'cros_img': 'jacuzzi-release/R102-14695.68.0',
     },
     'enabled': True,
     'identifier': 'JACUZZI_RELEASE_BETA',
@@ -1021,8 +1021,8 @@
   'CROS_OCTOPUS_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '104.0.5061.0',
-      'cros_img': 'octopus-release/R104-14829.0.0',
+      'cros_chrome_version': '104.0.5065.0',
+      'cros_img': 'octopus-release/R104-14844.0.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_LKGM',
@@ -1039,8 +1039,8 @@
   'CROS_OCTOPUS_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '102.0.5005.48',
-      'cros_img': 'octopus-release/R102-14695.55.0',
+      'cros_chrome_version': '102.0.5005.56',
+      'cros_img': 'octopus-release/R102-14695.68.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_RELEASE_BETA',
@@ -1057,8 +1057,8 @@
   'CROS_STRONGBAD_RELEASE_LKGM': {
     'skylab': {
       'cros_board': 'strongbad',
-      'cros_chrome_version': '104.0.5061.0',
-      'cros_img': 'strongbad-release/R104-14829.0.0',
+      'cros_chrome_version': '104.0.5065.0',
+      'cros_img': 'strongbad-release/R104-14844.0.0',
     },
     'enabled': True,
     'identifier': 'STRONGBAD_RELEASE_LKGM',
@@ -1075,8 +1075,8 @@
   'CROS_STRONGBAD_RELEASE_BETA': {
     'skylab': {
       'cros_board': 'strongbad',
-      'cros_chrome_version': '102.0.5005.48',
-      'cros_img': 'strongbad-release/R102-14695.55.0',
+      'cros_chrome_version': '102.0.5005.56',
+      'cros_img': 'strongbad-release/R102-14695.68.0',
     },
     'enabled': True,
     'identifier': 'STRONGBAD_RELEASE_BETA',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9dd7bfd..bef1295 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -912,6 +912,28 @@
             ]
         }
     ],
+    "AutofillEnableSendingBcnInGetUploadDetails": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillEnableSendingBcnInGetUploadDetails"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillEnableSupportForMoreStructureInAddresses": [
         {
             "platforms": [
@@ -6781,47 +6803,6 @@
             ]
         }
     ],
-    "ScaleTileMemoryLimit": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Scale 75%_1",
-                    "params": {
-                        "Factor": "0.75"
-                    },
-                    "enable_features": [
-                        "ScaleTileMemoryLimit"
-                    ]
-                },
-                {
-                    "name": "Scale 120%_1",
-                    "params": {
-                        "Factor": "1.25"
-                    },
-                    "enable_features": [
-                        "ScaleTileMemoryLimit"
-                    ]
-                },
-                {
-                    "name": "Scale 50%_1",
-                    "params": {
-                        "Factor": "0.5"
-                    },
-                    "enable_features": [
-                        "ScaleTileMemoryLimit"
-                    ]
-                }
-            ]
-        }
-    ],
     "ScreenTime": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index f93969c..28548c5 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1355,14 +1355,6 @@
 const base::Feature kClientHintsPartitionedCookies{
     "ClientHintsPartitionedCookies", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// If enabled, the memory limit used for tiles is scaled by
-// `kScaleTileMemoryLimitFactor`.
-const base::Feature kScaleTileMemoryLimit{"ScaleTileMemoryLimit",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::FeatureParam<double> kScaleTileMemoryLimitFactor{
-    &kScaleTileMemoryLimit, "Factor", 1.0};
-
 const base::Feature kDurableClientHintsCache{"DurableClientHintsCache",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -1472,5 +1464,14 @@
 const base::Feature kPendingBeaconAPI{"PendingBeaconAPI",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSimulateClickOnAXFocus {
+  "SimulateClickOnAXFocus",
+#if BUILDFLAG(IS_WIN)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/common/loader/mime_sniffing_throttle.cc b/third_party/blink/common/loader/mime_sniffing_throttle.cc
index 54e225e..b0b8efd 100644
--- a/third_party/blink/common/loader/mime_sniffing_throttle.cc
+++ b/third_party/blink/common/loader/mime_sniffing_throttle.cc
@@ -42,7 +42,7 @@
       response_head->headers->GetNormalizedHeader("x-content-type-options",
                                                   &content_type_options)) {
     blocked_sniffing_mime =
-        base::LowerCaseEqualsASCII(content_type_options, "nosniff");
+        base::EqualsCaseInsensitiveASCII(content_type_options, "nosniff");
   }
 
   if (!blocked_sniffing_mime &&
diff --git a/third_party/blink/common/manifest/manifest_util.cc b/third_party/blink/common/manifest/manifest_util.cc
index ebb6a64..a95b416 100644
--- a/third_party/blink/common/manifest/manifest_util.cc
+++ b/third_party/blink/common/manifest/manifest_util.cc
@@ -47,17 +47,17 @@
 }
 
 blink::mojom::DisplayMode DisplayModeFromString(const std::string& display) {
-  if (base::LowerCaseEqualsASCII(display, "browser"))
+  if (base::EqualsCaseInsensitiveASCII(display, "browser"))
     return blink::mojom::DisplayMode::kBrowser;
-  if (base::LowerCaseEqualsASCII(display, "minimal-ui"))
+  if (base::EqualsCaseInsensitiveASCII(display, "minimal-ui"))
     return blink::mojom::DisplayMode::kMinimalUi;
-  if (base::LowerCaseEqualsASCII(display, "standalone"))
+  if (base::EqualsCaseInsensitiveASCII(display, "standalone"))
     return blink::mojom::DisplayMode::kStandalone;
-  if (base::LowerCaseEqualsASCII(display, "fullscreen"))
+  if (base::EqualsCaseInsensitiveASCII(display, "fullscreen"))
     return blink::mojom::DisplayMode::kFullscreen;
-  if (base::LowerCaseEqualsASCII(display, "window-controls-overlay"))
+  if (base::EqualsCaseInsensitiveASCII(display, "window-controls-overlay"))
     return blink::mojom::DisplayMode::kWindowControlsOverlay;
-  if (base::LowerCaseEqualsASCII(display, "tabbed"))
+  if (base::EqualsCaseInsensitiveASCII(display, "tabbed"))
     return blink::mojom::DisplayMode::kTabbed;
   return blink::mojom::DisplayMode::kUndefined;
 }
@@ -100,41 +100,42 @@
 
 device::mojom::ScreenOrientationLockType WebScreenOrientationLockTypeFromString(
     const std::string& orientation) {
-  if (base::LowerCaseEqualsASCII(orientation, "portrait-primary"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "portrait-primary"))
     return device::mojom::ScreenOrientationLockType::PORTRAIT_PRIMARY;
-  if (base::LowerCaseEqualsASCII(orientation, "portrait-secondary"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "portrait-secondary"))
     return device::mojom::ScreenOrientationLockType::PORTRAIT_SECONDARY;
-  if (base::LowerCaseEqualsASCII(orientation, "landscape-primary"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "landscape-primary"))
     return device::mojom::ScreenOrientationLockType::LANDSCAPE_PRIMARY;
-  if (base::LowerCaseEqualsASCII(orientation, "landscape-secondary"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "landscape-secondary"))
     return device::mojom::ScreenOrientationLockType::LANDSCAPE_SECONDARY;
-  if (base::LowerCaseEqualsASCII(orientation, "any"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "any"))
     return device::mojom::ScreenOrientationLockType::ANY;
-  if (base::LowerCaseEqualsASCII(orientation, "landscape"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "landscape"))
     return device::mojom::ScreenOrientationLockType::LANDSCAPE;
-  if (base::LowerCaseEqualsASCII(orientation, "portrait"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "portrait"))
     return device::mojom::ScreenOrientationLockType::PORTRAIT;
-  if (base::LowerCaseEqualsASCII(orientation, "natural"))
+  if (base::EqualsCaseInsensitiveASCII(orientation, "natural"))
     return device::mojom::ScreenOrientationLockType::NATURAL;
   return device::mojom::ScreenOrientationLockType::DEFAULT;
 }
 
 mojom::CaptureLinks CaptureLinksFromString(const std::string& capture_links) {
-  if (base::LowerCaseEqualsASCII(capture_links, "none"))
+  if (base::EqualsCaseInsensitiveASCII(capture_links, "none"))
     return mojom::CaptureLinks::kNone;
-  if (base::LowerCaseEqualsASCII(capture_links, "new-client"))
+  if (base::EqualsCaseInsensitiveASCII(capture_links, "new-client"))
     return mojom::CaptureLinks::kNewClient;
-  if (base::LowerCaseEqualsASCII(capture_links, "existing-client-navigate"))
+  if (base::EqualsCaseInsensitiveASCII(capture_links,
+                                       "existing-client-navigate"))
     return mojom::CaptureLinks::kExistingClientNavigate;
   return mojom::CaptureLinks::kUndefined;
 }
 
 mojom::HandleLinks HandleLinksFromString(const std::string& handle_links) {
-  if (base::LowerCaseEqualsASCII(handle_links, "auto"))
+  if (base::EqualsCaseInsensitiveASCII(handle_links, "auto"))
     return mojom::HandleLinks::kAuto;
-  if (base::LowerCaseEqualsASCII(handle_links, "preferred"))
+  if (base::EqualsCaseInsensitiveASCII(handle_links, "preferred"))
     return mojom::HandleLinks::kPreferred;
-  if (base::LowerCaseEqualsASCII(handle_links, "not-preferred"))
+  if (base::EqualsCaseInsensitiveASCII(handle_links, "not-preferred"))
     return mojom::HandleLinks::kNotPreferred;
   return mojom::HandleLinks::kUndefined;
 }
@@ -152,24 +153,24 @@
 
 absl::optional<ParsedRouteTo> RouteToFromString(const std::string& route_to) {
   using RouteTo = Manifest::LaunchHandler::RouteTo;
-  if (base::LowerCaseEqualsASCII(route_to, "auto"))
+  if (base::EqualsCaseInsensitiveASCII(route_to, "auto"))
     return ParsedRouteTo{.route_to = RouteTo::kAuto};
-  if (base::LowerCaseEqualsASCII(route_to, "new-client"))
+  if (base::EqualsCaseInsensitiveASCII(route_to, "new-client"))
     return ParsedRouteTo{.route_to = RouteTo::kNewClient};
-  if (base::LowerCaseEqualsASCII(route_to, "existing-client"))
+  if (base::EqualsCaseInsensitiveASCII(route_to, "existing-client"))
     return ParsedRouteTo{.legacy_existing_client_value = true};
-  if (base::LowerCaseEqualsASCII(route_to, "existing-client-navigate"))
+  if (base::EqualsCaseInsensitiveASCII(route_to, "existing-client-navigate"))
     return ParsedRouteTo{.route_to = RouteTo::kExistingClientNavigate};
-  if (base::LowerCaseEqualsASCII(route_to, "existing-client-retain"))
+  if (base::EqualsCaseInsensitiveASCII(route_to, "existing-client-retain"))
     return ParsedRouteTo{.route_to = RouteTo::kExistingClientRetain};
   return absl::nullopt;
 }
 
 absl::optional<NavigateExistingClient> NavigateExistingClientFromString(
     const std::string& navigate_existing_client) {
-  if (base::LowerCaseEqualsASCII(navigate_existing_client, "always"))
+  if (base::EqualsCaseInsensitiveASCII(navigate_existing_client, "always"))
     return NavigateExistingClient::kAlways;
-  if (base::LowerCaseEqualsASCII(navigate_existing_client, "never"))
+  if (base::EqualsCaseInsensitiveASCII(navigate_existing_client, "never"))
     return NavigateExistingClient::kNever;
   return absl::nullopt;
 }
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 91e3417..e368cc2 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -659,10 +659,6 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature kClientHintsPartitionedCookies;
 
-BLINK_COMMON_EXPORT extern const base::Feature kScaleTileMemoryLimit;
-BLINK_COMMON_EXPORT
-extern const base::FeatureParam<double> kScaleTileMemoryLimitFactor;
-
 // If enabled, the client hints cache will be loaded on browser restarts.
 BLINK_COMMON_EXPORT extern const base::Feature kDurableClientHintsCache;
 
@@ -761,6 +757,12 @@
 // https://github.com/darrenw/docs/blob/main/explainers/beacon_api.md
 BLINK_COMMON_EXPORT extern const base::Feature kPendingBeaconAPI;
 
+// TODO(accessibility): This flag is set to accommodate JAWS on Windows so they
+// can adjust to us not simulating click events on a focus action. It should be
+// disabled by default (and removed) before 5/17/2023.
+// See https://crbug.com/1326622 for more info.
+BLINK_COMMON_EXPORT extern const base::Feature kSimulateClickOnAXFocus;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/manifest/manifest.mojom b/third_party/blink/public/mojom/manifest/manifest.mojom
index e287b24..9765a8c 100644
--- a/third_party/blink/public/mojom/manifest/manifest.mojom
+++ b/third_party/blink/public/mojom/manifest/manifest.mojom
@@ -119,7 +119,7 @@
 
   // TODO(crbug.com/1271804): This field is non-standard and part of a Chrome
   // experiment. See:
-  // https://github.com/w3c/manifest/issues/975#issuecomment-960222756
+  // https://github.com/WICG/manifest-incubations/blob/gh-pages/user-preferences-explainer.md
   ManifestUserPreferences? user_preferences;
 
   // TODO(crbug.com/1254457): This field is non-standard and part of a Chrome
diff --git a/third_party/blink/public/mojom/use_counter/OWNERS b/third_party/blink/public/mojom/use_counter/OWNERS
index 588a397..d3adf30 100644
--- a/third_party/blink/public/mojom/use_counter/OWNERS
+++ b/third_party/blink/public/mojom/use_counter/OWNERS
@@ -1,4 +1,4 @@
 dcheng@chromium.org
 
-per-file *.mojom=set noparent                                
-per-file *.mojom=file://ipc/SECURITY_OWNERS 
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index aaa55b2..a9cf1b6 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3554,6 +3554,7 @@
   kV8PaymentInstruments_Has_Method = 4233,
   kV8PaymentInstruments_Keys_Method = 4234,
   kV8PaymentInstruments_Set_Method = 4235,
+  kPerformanceMeasureFindExistingName = 4236,
 
   // 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/core/animation/compositor_animations.cc b/third_party/blink/renderer/core/animation/compositor_animations.cc
index a752da79..bcb3ef5f 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations.cc
@@ -215,8 +215,11 @@
     case CSSPropertyID::kBackdropFilter:
       return CompositorElementIdNamespace::kPrimaryEffect;
     case CSSPropertyID::kRotate:
+      return CompositorElementIdNamespace::kRotateTransform;
     case CSSPropertyID::kScale:
+      return CompositorElementIdNamespace::kScaleTransform;
     case CSSPropertyID::kTranslate:
+      return CompositorElementIdNamespace::kTranslateTransform;
     case CSSPropertyID::kTransform:
       return CompositorElementIdNamespace::kPrimaryTransform;
     case CSSPropertyID::kFilter:
@@ -279,6 +282,12 @@
       if (const auto* svg_element = DynamicTo<SVGElement>(target_element)) {
         reasons |=
             CheckCanStartTransformAnimationOnCompositorForSVG(*svg_element);
+        // TODO(https://crbug.com/1278452): When we make the transform tree
+        // structure for SVG work like everything else, we should instead
+        // start compositing animations of transform properties other than
+        // transform.
+        if (!property.GetCSSProperty().IDEquals(CSSPropertyID::kTransform))
+          reasons |= kSVGTargetHasIndependentTransformProperty;
       }
       transform_property_count++;
     }
@@ -452,6 +461,7 @@
   }
 
   // TODO: Support multiple transform property animations on the compositor
+  // TODO(https://crbug.com/696374): remove this
   if (transform_property_count > 1)
     reasons |= kMultipleTransformAnimationsOnSameTarget;
 
@@ -566,10 +576,16 @@
     } else if (const auto* paint_properties =
                    layout_object->FirstFragment().PaintProperties()) {
       const auto* transform = paint_properties->Transform();
+      const auto* scale = paint_properties->Scale();
+      const auto* rotate = paint_properties->Rotate();
+      const auto* translate = paint_properties->Translate();
       const auto* effect = paint_properties->Effect();
       const auto* filter = paint_properties->Filter();
       has_direct_compositing_reasons =
           (transform && transform->HasDirectCompositingReasons()) ||
+          (scale && scale->HasDirectCompositingReasons()) ||
+          (rotate && rotate->HasDirectCompositingReasons()) ||
+          (translate && translate->HasDirectCompositingReasons()) ||
           (effect && effect->HasDirectCompositingReasons()) ||
           (filter && filter->HasDirectCompositingReasons());
     }
@@ -898,7 +914,8 @@
     DCHECK(timing.timing_function);
     absl::optional<cc::KeyframeModel::TargetPropertyId> target_property_id =
         absl::nullopt;
-    switch (property.GetCSSProperty().PropertyID()) {
+    CSSPropertyID css_property_id = property.GetCSSProperty().PropertyID();
+    switch (css_property_id) {
       case CSSPropertyID::kOpacity: {
         auto float_curve = gfx::KeyframedFloatAnimationCurve::Create();
         AddKeyframesToCurve(*float_curve, values);
@@ -917,7 +934,7 @@
         filter_curve->set_scaled_duration(scale);
         curve = std::move(filter_curve);
         target_property_id = cc::KeyframeModel::TargetPropertyId(
-            property.GetCSSProperty().PropertyID() == CSSPropertyID::kFilter
+            css_property_id == CSSPropertyID::kFilter
                 ? cc::TargetProperty::FILTER
                 : cc::TargetProperty::BACKDROP_FILTER);
         break;
@@ -934,8 +951,27 @@
         transform_curve->SetTimingFunction(timing.timing_function->CloneToCC());
         transform_curve->set_scaled_duration(scale);
         curve = std::move(transform_curve);
-        target_property_id =
-            cc::KeyframeModel::TargetPropertyId(cc::TargetProperty::TRANSFORM);
+        switch (css_property_id) {
+          case CSSPropertyID::kRotate:
+            target_property_id =
+                cc::KeyframeModel::TargetPropertyId(cc::TargetProperty::ROTATE);
+            break;
+          case CSSPropertyID::kScale:
+            target_property_id =
+                cc::KeyframeModel::TargetPropertyId(cc::TargetProperty::SCALE);
+            break;
+          case CSSPropertyID::kTranslate:
+            target_property_id = cc::KeyframeModel::TargetPropertyId(
+                cc::TargetProperty::TRANSLATE);
+            break;
+          case CSSPropertyID::kTransform:
+            target_property_id = cc::KeyframeModel::TargetPropertyId(
+                cc::TargetProperty::TRANSFORM);
+            break;
+          default:
+            NOTREACHED() << "only possible cases for nested switch";
+            break;
+        }
         break;
       }
       case CSSPropertyID::kBackgroundColor:
diff --git a/third_party/blink/renderer/core/animation/compositor_animations.h b/third_party/blink/renderer/core/animation/compositor_animations.h
index 33e17c28..a808a0c 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations.h
+++ b/third_party/blink/renderer/core/animation/compositor_animations.h
@@ -108,11 +108,17 @@
     // Cases where we are animating a property that is marked important.
     kAffectsImportantProperty = 1 << 18,
 
+    kSVGTargetHasIndependentTransformProperty = 1 << 19,
+
+    // When adding new values, update the count below *and* add a description
+    // of the value to CompositorAnimationsFailureReason in
+    // tools/metrics/histograms/enums.xml .
+
     // The maximum number of flags in this enum (excluding itself). New flags
     // should increment this number but it should never be decremented because
     // the values are used in UMA histograms. It should also be noted that it
     // excludes the kNoFailure value.
-    kFailureReasonCount = 19,
+    kFailureReasonCount = 20,
   };
 
   static FailureReasons CheckCanStartAnimationOnCompositor(
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index efc649942..01958152 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -1317,6 +1317,8 @@
   effect2->SnapshotAllCompositorKeyframesIfNecessary(*element_.Get(), *style,
                                                      nullptr);
 
+  UpdateAllLifecyclePhasesForTest();
+
   EXPECT_TRUE(CheckCanStartEffectOnCompositor(timing_, *element_.Get(),
                                               animation2, *effect2) &
               CompositorAnimations::kMultipleTransformAnimationsOnSameTarget);
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 098c68c..71612032 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -534,7 +534,8 @@
   }
 
   bool transform_zoom_changed =
-      old_style->HasCurrentTransformAnimation() &&
+      (old_style->HasCurrentTranslateAnimation() ||
+       old_style->HasCurrentTransformAnimation()) &&
       old_style->EffectiveZoom() != style.EffectiveZoom();
 
   const auto& snapshot = [&](AnimationEffect* effect) {
@@ -879,12 +880,14 @@
                                    ComputedStyle& style) {
   if (effect.Affects(PropertyHandle(GetCSSPropertyOpacity())))
     style.SetHasCurrentOpacityAnimation(true);
-  if (effect.Affects(PropertyHandle(GetCSSPropertyTransform())) ||
-      effect.Affects(PropertyHandle(GetCSSPropertyRotate())) ||
-      effect.Affects(PropertyHandle(GetCSSPropertyScale())) ||
-      effect.Affects(PropertyHandle(GetCSSPropertyTranslate()))) {
+  if (effect.Affects(PropertyHandle(GetCSSPropertyTransform())))
     style.SetHasCurrentTransformAnimation(true);
-  }
+  if (effect.Affects(PropertyHandle(GetCSSPropertyRotate())))
+    style.SetHasCurrentRotateAnimation(true);
+  if (effect.Affects(PropertyHandle(GetCSSPropertyScale())))
+    style.SetHasCurrentScaleAnimation(true);
+  if (effect.Affects(PropertyHandle(GetCSSPropertyTranslate())))
+    style.SetHasCurrentTranslateAnimation(true);
   if (effect.Affects(PropertyHandle(GetCSSPropertyFilter())))
     style.SetHasCurrentFilterAnimation(true);
   if (effect.Affects(PropertyHandle(GetCSSPropertyBackdropFilter())))
@@ -1000,6 +1003,21 @@
           effect_stack.HasActiveAnimationsOnCompositor(
               PropertyHandle(GetCSSPropertyTransform())));
     }
+    if (style.HasCurrentScaleAnimation()) {
+      style.SetIsRunningScaleAnimationOnCompositor(
+          effect_stack.HasActiveAnimationsOnCompositor(
+              PropertyHandle(GetCSSPropertyScale())));
+    }
+    if (style.HasCurrentRotateAnimation()) {
+      style.SetIsRunningRotateAnimationOnCompositor(
+          effect_stack.HasActiveAnimationsOnCompositor(
+              PropertyHandle(GetCSSPropertyRotate())));
+    }
+    if (style.HasCurrentTranslateAnimation()) {
+      style.SetIsRunningTranslateAnimationOnCompositor(
+          effect_stack.HasActiveAnimationsOnCompositor(
+              PropertyHandle(GetCSSPropertyTranslate())));
+    }
     if (style.HasCurrentFilterAnimation()) {
       style.SetIsRunningFilterAnimationOnCompositor(
           effect_stack.HasActiveAnimationsOnCompositor(
diff --git a/third_party/blink/renderer/core/animation/css/css_animations_test.cc b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
index 1730f75..ba5094fb 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
@@ -318,6 +318,15 @@
 bool TransformFlag(const ComputedStyle& style) {
   return style.HasCurrentTransformAnimation();
 }
+bool ScaleFlag(const ComputedStyle& style) {
+  return style.HasCurrentScaleAnimation();
+}
+bool RotateFlag(const ComputedStyle& style) {
+  return style.HasCurrentRotateAnimation();
+}
+bool TranslateFlag(const ComputedStyle& style) {
+  return style.HasCurrentTranslateAnimation();
+}
 bool FilterFlag(const ComputedStyle& style) {
   return style.HasCurrentFilterAnimation();
 }
@@ -337,6 +346,15 @@
 bool CompositedTransformFlag(const ComputedStyle& style) {
   return style.IsRunningTransformAnimationOnCompositor();
 }
+bool CompositedScaleFlag(const ComputedStyle& style) {
+  return style.IsRunningScaleAnimationOnCompositor();
+}
+bool CompositedRotateFlag(const ComputedStyle& style) {
+  return style.IsRunningRotateAnimationOnCompositor();
+}
+bool CompositedTranslateFlag(const ComputedStyle& style) {
+  return style.IsRunningTranslateAnimationOnCompositor();
+}
 bool CompositedFilterFlag(const ComputedStyle& style) {
   return style.IsRunningFilterAnimationOnCompositor();
 }
@@ -356,9 +374,9 @@
 FlagData flag_data[] = {
     {"opacity", "0", "1", OpacityFlag},
     {"transform", "scale(1)", "scale(2)", TransformFlag},
-    {"rotate", "10deg", "20deg", TransformFlag},
-    {"scale", "1", "2", TransformFlag},
-    {"translate", "10px", "20px", TransformFlag},
+    {"rotate", "10deg", "20deg", RotateFlag},
+    {"scale", "1", "2", ScaleFlag},
+    {"translate", "10px", "20px", TranslateFlag},
     {"filter", "contrast(10%)", "contrast(20%)", FilterFlag},
     {"backdrop-filter", "blur(10px)", "blur(20px)", BackdropFilterFlag},
     {"background-color", "red", "blue", BackgroundColorFlag},
@@ -368,6 +386,9 @@
 FlagData compositor_flag_data[] = {
     {"opacity", "0", "1", CompositedOpacityFlag},
     {"transform", "scale(1)", "scale(2)", CompositedTransformFlag},
+    {"scale", "1", "2", CompositedScaleFlag},
+    {"rotate", "45deg", "90deg", CompositedRotateFlag},
+    {"translate", "10px 0px", "10px 20px", CompositedTranslateFlag},
     {"filter", "contrast(10%)", "contrast(20%)", CompositedFilterFlag},
     {"backdrop-filter", "blur(10px)", "blur(20px)",
      CompositedBackdropFilterFlag},
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc
index c060ede..ae8bc247 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -352,6 +352,7 @@
 
     // Do not put transforms on compositor if more than one of them are defined
     // in computed style because they need to be explicitly ordered
+    // TODO(https://crbug.com/696374): remove this
     if (HasMultipleTransformProperties())
       reasons |= CompositorAnimations::kTargetHasMultipleTransformProperties;
 
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni
index 368cef97..ea4f7d2 100644
--- a/third_party/blink/renderer/core/css/build.gni
+++ b/third_party/blink/renderer/core/css/build.gni
@@ -51,6 +51,7 @@
   "container_query_data.h",
   "container_query_evaluator.cc",
   "container_query_evaluator.h",
+  "css_anchor_query_type.h",
   "css_axis_value.cc",
   "css_axis_value.h",
   "css_basic_shape_values.cc",
diff --git a/third_party/blink/renderer/core/css/css_anchor_query_type.h b/third_party/blink/renderer/core/css/css_anchor_query_type.h
new file mode 100644
index 0000000..b28d306
--- /dev/null
+++ b/third_party/blink/renderer/core/css/css_anchor_query_type.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_CORE_CSS_CSS_ANCHOR_QUERY_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_ANCHOR_QUERY_TYPE_H_
+
+#include <cstdint>
+
+namespace blink {
+
+enum class CSSAnchorQueryType : uint8_t {
+  kAnchor = 1 << 0,
+  kAnchorSize = 1 << 1
+};
+
+using CSSAnchorQueryTypes = uint8_t;
+constexpr CSSAnchorQueryTypes kCSSAnchorQueryTypesNone = 0u;
+constexpr CSSAnchorQueryTypes kCSSAnchorQueryTypesAll =
+    ~kCSSAnchorQueryTypesNone;
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_ANCHOR_QUERY_TYPE_H_
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index c9f9df2..d7872d4 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/css/css_math_expression_node.h"
 
 #include "base/memory/values_equivalent.h"
+#include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_value_clamping_utils.h"
@@ -1035,11 +1036,68 @@
 
 // ------ End of CSSMathExpressionOperation member functions ------
 
+// ------ Start of CSSMathExpressionAnchorQuery member functions ------
+
+CSSMathExpressionAnchorQuery::CSSMathExpressionAnchorQuery(
+    CSSAnchorQueryType type,
+    const CSSCustomIdentValue& anchor_name,
+    const CSSValue& value,
+    const CSSPrimitiveValue* fallback)
+    : CSSMathExpressionNode(kCalcPercentLength, false /* has_comparisons */),
+      type_(type),
+      anchor_name_(anchor_name),
+      value_(value),
+      fallback_(fallback) {}
+
+String CSSMathExpressionAnchorQuery::CustomCSSText() const {
+  StringBuilder result;
+  result.Append(IsAnchor() ? "anchor(" : "anchor-size(");
+  result.Append(anchor_name_->CustomCSSText());
+  result.Append(" ");
+  result.Append(value_->CssText());
+  if (fallback_) {
+    result.Append(", ");
+    result.Append(fallback_->CustomCSSText());
+  }
+  result.Append(")");
+  return result.ToString();
+}
+
+bool CSSMathExpressionAnchorQuery::operator==(
+    const CSSMathExpressionNode& other) const {
+  const auto* other_anchor = DynamicTo<CSSMathExpressionAnchorQuery>(other);
+  if (!other_anchor)
+    return false;
+  return type_ == other_anchor->type_ &&
+         base::ValuesEquivalent(anchor_name_, other_anchor->anchor_name_) &&
+         base::ValuesEquivalent(value_, other_anchor->value_) &&
+         base::ValuesEquivalent(fallback_, other_anchor->fallback_);
+}
+
+scoped_refptr<const CalculationExpressionNode>
+CSSMathExpressionAnchorQuery::ToCalculationExpression(
+    const CSSToLengthConversionData&) const {
+  // TODO(crbug.com/1309178): Implement.
+  return base::MakeRefCounted<CalculationExpressionPixelsAndPercentNode>(
+      PixelsAndPercent(0, 0));
+}
+
+void CSSMathExpressionAnchorQuery::Trace(Visitor* visitor) const {
+  visitor->Trace(anchor_name_);
+  visitor->Trace(value_);
+  visitor->Trace(fallback_);
+  CSSMathExpressionNode::Trace(visitor);
+}
+
+// ------ End of CSSMathExpressionAnchorQuery member functions ------
+
 class CSSMathExpressionNodeParser {
   STACK_ALLOCATED();
 
  public:
-  CSSMathExpressionNodeParser() {}
+  CSSMathExpressionNodeParser(const CSSParserContext& context,
+                              CSSAnchorQueryTypes allowed_anchor_queries)
+      : context_(context), allowed_anchor_queries_(allowed_anchor_queries) {}
 
   bool IsSupportedMathFunction(CSSValueID function_id) {
     switch (function_id) {
@@ -1047,15 +1105,87 @@
       case CSSValueID::kMax:
       case CSSValueID::kClamp:
         return true;
+      case CSSValueID::kAnchor:
+      case CSSValueID::kAnchorSize:
+        return RuntimeEnabledFeatures::CSSAnchorPositioningEnabled();
       // TODO(crbug.com/1284199): Support other math functions.
       default:
         return false;
     }
   }
 
+  CSSMathExpressionNode* ParseAnchorQuery(CSSValueID function_id,
+                                          CSSParserTokenRange& tokens) {
+    DCHECK(RuntimeEnabledFeatures::CSSAnchorPositioningEnabled());
+    CSSAnchorQueryType anchor_query_type;
+    switch (function_id) {
+      case CSSValueID::kAnchor:
+        anchor_query_type = CSSAnchorQueryType::kAnchor;
+        break;
+      case CSSValueID::kAnchorSize:
+        anchor_query_type = CSSAnchorQueryType::kAnchorSize;
+        break;
+      default:
+        return nullptr;
+    }
+    if (!(static_cast<CSSAnchorQueryTypes>(anchor_query_type) &
+          allowed_anchor_queries_)) {
+      return nullptr;
+    }
+
+    const CSSCustomIdentValue* anchor_name =
+        css_parsing_utils::ConsumeDashedIdent(tokens, context_);
+    if (!anchor_name)
+      return nullptr;
+
+    tokens.ConsumeWhitespace();
+    const CSSValue* value = nullptr;
+    switch (anchor_query_type) {
+      case CSSAnchorQueryType::kAnchor:
+        value = css_parsing_utils::ConsumeIdent<
+            CSSValueID::kTop, CSSValueID::kLeft, CSSValueID::kRight,
+            CSSValueID::kBottom, CSSValueID::kStart, CSSValueID::kEnd,
+            CSSValueID::kSelfStart, CSSValueID::kSelfEnd, CSSValueID::kCenter>(
+            tokens);
+        if (!value) {
+          value = css_parsing_utils::ConsumePercent(
+              tokens, context_, CSSPrimitiveValue::ValueRange::kAll);
+        }
+        break;
+      case CSSAnchorQueryType::kAnchorSize:
+        value = css_parsing_utils::ConsumeIdent<
+            CSSValueID::kWidth, CSSValueID::kHeight, CSSValueID::kBlock,
+            CSSValueID::kInline, CSSValueID::kSelfBlock,
+            CSSValueID::kSelfInline>(tokens);
+        break;
+    }
+    if (!value)
+      return nullptr;
+
+    const CSSPrimitiveValue* fallback = nullptr;
+    if (css_parsing_utils::ConsumeCommaIncludingWhitespace(tokens)) {
+      fallback = css_parsing_utils::ConsumeLengthOrPercent(
+          tokens, context_, CSSPrimitiveValue::ValueRange::kAll,
+          css_parsing_utils::UnitlessQuirk::kForbid, allowed_anchor_queries_);
+      if (!fallback)
+        return nullptr;
+    }
+
+    tokens.ConsumeWhitespace();
+    if (!tokens.AtEnd())
+      return nullptr;
+    return MakeGarbageCollected<CSSMathExpressionAnchorQuery>(
+        anchor_query_type, *anchor_name, *value, fallback);
+  }
+
   CSSMathExpressionNode* ParseMathFunction(CSSValueID function_id,
                                            CSSParserTokenRange& tokens,
                                            int depth) {
+    if (RuntimeEnabledFeatures::CSSAnchorPositioningEnabled()) {
+      if (auto* anchor_query = ParseAnchorQuery(function_id, tokens))
+        return anchor_query;
+    }
+
     // "arguments" refers to comma separated ones.
     wtf_size_t min_argument_count = 1;
     wtf_size_t max_argument_count = std::numeric_limits<wtf_size_t>::max();
@@ -1252,6 +1382,9 @@
       return nullptr;
     return ParseAdditiveValueExpression(tokens, depth);
   }
+
+  const CSSParserContext& context_;
+  const CSSAnchorQueryTypes allowed_anchor_queries_;
 };
 
 scoped_refptr<const CalculationValue> CSSMathExpressionNode::ToCalcValue(
@@ -1381,8 +1514,10 @@
 // static
 CSSMathExpressionNode* CSSMathExpressionNode::ParseMathFunction(
     CSSValueID function_id,
-    CSSParserTokenRange tokens) {
-  CSSMathExpressionNodeParser parser;
+    CSSParserTokenRange tokens,
+    const CSSParserContext& context,
+    CSSAnchorQueryTypes allowed_anchor_queries) {
+  CSSMathExpressionNodeParser parser(context, allowed_anchor_queries);
   CSSMathExpressionNode* result =
       parser.ParseMathFunction(function_id, tokens, 0);
 
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.h b/third_party/blink/renderer/core/css/css_math_expression_node.h
index fe27c3d3..52a846d9 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.h
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.h
@@ -34,9 +34,11 @@
 #include "base/dcheck_is_on.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_anchor_query_type.h"
 #include "third_party/blink/renderer/core/css/css_math_operator.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
 #include "third_party/blink/renderer/platform/geometry/calculation_value.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -46,6 +48,7 @@
 static const int kMaxExpressionDepth = 100;
 
 class CalculationExpressionNode;
+class CSSCustomIdentValue;
 class CSSNumericLiteralValue;
 
 // The order of this enum should not change since its elements are used as
@@ -54,6 +57,9 @@
   kCalcNumber,
   kCalcLength,
   kCalcPercent,
+  // TODO(crbug.com/1309178): We are now using this for all calculated lengths
+  // that can't be resolved at style time, including not only calc(px + %) but
+  // also anchor queries. Rename this category accordingly.
   kCalcPercentLength,
   kCalcAngle,
   kCalcTime,
@@ -68,11 +74,15 @@
   static CSSMathExpressionNode* Create(PixelsAndPercent pixels_and_percent);
   static CSSMathExpressionNode* Create(const CalculationExpressionNode& node);
 
-  static CSSMathExpressionNode* ParseMathFunction(CSSValueID function_id,
-                                                  CSSParserTokenRange tokens);
+  static CSSMathExpressionNode* ParseMathFunction(
+      CSSValueID function_id,
+      CSSParserTokenRange tokens,
+      const CSSParserContext&,
+      CSSAnchorQueryTypes allowed_anchor_queries);
 
   virtual bool IsNumericLiteral() const { return false; }
   virtual bool IsOperation() const { return false; }
+  virtual bool IsAnchorQuery() const { return false; }
 
   virtual bool IsMathFunction() const { return false; }
 
@@ -295,6 +305,84 @@
   }
 };
 
+// anchor() and anchor-size()
+class CORE_EXPORT CSSMathExpressionAnchorQuery final
+    : public CSSMathExpressionNode {
+ public:
+  CSSMathExpressionAnchorQuery(CSSAnchorQueryType type,
+                               const CSSCustomIdentValue& anchor_name,
+                               const CSSValue& value,
+                               const CSSPrimitiveValue* fallback);
+
+  bool IsAnchor() const { return type_ == CSSAnchorQueryType::kAnchor; }
+  bool IsAnchorSize() const { return type_ == CSSAnchorQueryType::kAnchorSize; }
+
+  // TODO(crbug.com/1309178): This is not entirely correct, since "math
+  // function" should refer to functions defined in [1]. We may need to clean up
+  // the terminology in the code.
+  // [1] https://drafts.csswg.org/css-values-4/#math
+  bool IsMathFunction() const final { return true; }
+
+  bool IsAnchorQuery() const final { return true; }
+  bool IsZero() const final { return false; }
+  CSSPrimitiveValue::UnitType ResolvedUnitType() const final {
+    return CSSPrimitiveValue::UnitType::kUnknown;
+  }
+  absl::optional<double> ComputeValueInCanonicalUnit() const final {
+    return absl::nullopt;
+  }
+  absl::optional<PixelsAndPercent> ToPixelsAndPercent(
+      const CSSToLengthConversionData&) const final {
+    return absl::nullopt;
+  }
+  bool AccumulateLengthArray(CSSLengthArray& length_array,
+                             double multiplier) const final {
+    return false;
+  }
+  bool IsComputationallyIndependent() const final { return false; }
+  double DoubleValue() const final {
+    // We can't resolve an anchor query until layout time.
+    NOTREACHED();
+    return 0;
+  }
+  double ComputeLengthPx(
+      const CSSToLengthConversionData& conversion_data) const final {
+    // We can't resolve an anchor query until layout time.
+    NOTREACHED();
+    return 0;
+  }
+  void AccumulateLengthUnitTypes(
+      CSSPrimitiveValue::LengthTypeFlags& types) const final {
+    // AccumulateLengthUnitTypes() is only used when interpolating the
+    // 'transform' property, where anchor queries are not allowed.
+    NOTREACHED();
+    return;
+  }
+
+  String CustomCSSText() const final;
+  scoped_refptr<const CalculationExpressionNode> ToCalculationExpression(
+      const CSSToLengthConversionData&) const final;
+  bool operator==(const CSSMathExpressionNode& other) const final;
+  void Trace(Visitor* visitor) const final;
+
+#if DCHECK_IS_ON()
+  bool InvolvesPercentageComparisons() const final { return false; }
+#endif
+
+ private:
+  CSSAnchorQueryType type_;
+  Member<const CSSCustomIdentValue> anchor_name_;
+  Member<const CSSValue> value_;
+  Member<const CSSPrimitiveValue> fallback_;
+};
+
+template <>
+struct DowncastTraits<CSSMathExpressionAnchorQuery> {
+  static bool AllowFrom(const CSSMathExpressionNode& node) {
+    return node.IsAnchorQuery();
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_MATH_EXPRESSION_NODE_H_
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node_test.cc b/third_party/blink/renderer/core/css/css_math_expression_node_test.cc
index ccd5245..c0bbd83 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node_test.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node_test.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
@@ -325,8 +326,10 @@
     CSSTokenizer tokenizer(String(ss.str().c_str()));
     const auto tokens = tokenizer.TokenizeToEOF();
     const CSSParserTokenRange range(tokens);
-    const CSSMathExpressionNode* res =
-        CSSMathExpressionNode::ParseMathFunction(CSSValueID::kCalc, range);
+    const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
+        kHTMLStandardMode, SecureContextMode::kInsecureContext);
+    const CSSMathExpressionNode* res = CSSMathExpressionNode::ParseMathFunction(
+        CSSValueID::kCalc, range, *context, kCSSAnchorQueryTypesNone);
 
     if (test_case.expected) {
       EXPECT_TRUE(res);
diff --git a/third_party/blink/renderer/core/css/css_syntax_definition.cc b/third_party/blink/renderer/core/css/css_syntax_definition.cc
index 9c76a5b..ef77f352 100644
--- a/third_party/blink/renderer/core/css/css_syntax_definition.cc
+++ b/third_party/blink/renderer/core/css/css_syntax_definition.cc
@@ -67,7 +67,8 @@
       CSSParserContext::ParserModeOverridingScope scope(context,
                                                         kHTMLStandardMode);
       return css_parsing_utils::ConsumeLengthOrPercent(
-          range, context, CSSPrimitiveValue::ValueRange::kAll);
+          range, context, CSSPrimitiveValue::ValueRange::kAll,
+          css_parsing_utils::UnitlessQuirk::kForbid, kCSSAnchorQueryTypesAll);
     }
     case CSSSyntaxType::kColor: {
       CSSParserContext::ParserModeOverridingScope scope(context,
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index 720ef123..44eda8e1 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -1493,5 +1493,24 @@
 
     // basic-shape functions.
     "xywh",
+
+    // anchor positioning
+    "anchor",
+    // top,
+    // left,
+    // right,
+    // bottom,
+    // start,
+    // end,
+    // self-start,
+    // self-end,
+    // center,
+    "anchor-size",
+    "width",
+    "height",
+    // block,
+    // inline,
+    "self-block",
+    "self-inline",
   ],
 }
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
index deacdb89..bc4e22f 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
@@ -240,8 +240,10 @@
 }
 
 // static
-CSSNumericValue* CSSNumericValue::parse(const String& css_text,
-                                        ExceptionState& exception_state) {
+CSSNumericValue* CSSNumericValue::parse(
+    const ExecutionContext* execution_context,
+    const String& css_text,
+    ExceptionState& exception_state) {
   CSSTokenizer tokenizer(css_text);
   CSSParserTokenStream stream(tokenizer);
   stream.ConsumeWhitespace();
@@ -268,8 +270,12 @@
           range.Peek().FunctionId() == CSSValueID::kMin ||
           range.Peek().FunctionId() == CSSValueID::kMax ||
           range.Peek().FunctionId() == CSSValueID::kClamp) {
+        // TODO(crbug.com/1309178): Decide how to handle anchor queries here.
         CSSMathExpressionNode* expression =
-            CSSMathExpressionNode::ParseMathFunction(CSSValueID::kCalc, range);
+            CSSMathExpressionNode::ParseMathFunction(
+                CSSValueID::kCalc, range,
+                *MakeGarbageCollected<CSSParserContext>(*execution_context),
+                kCSSAnchorQueryTypesNone);
         if (expression)
           return CalcToNumericValue(*expression);
       }
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.h b/third_party/blink/renderer/core/css/cssom/css_numeric_value.h
index 66584a7..4fd2868 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.h
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.h
@@ -33,7 +33,9 @@
   CSSNumericValue(const CSSNumericValue&) = delete;
   CSSNumericValue& operator=(const CSSNumericValue&) = delete;
 
-  static CSSNumericValue* parse(const String& css_text, ExceptionState&);
+  static CSSNumericValue* parse(const ExecutionContext*,
+                                const String& css_text,
+                                ExceptionState&);
   // Blink-internal ways of creating CSSNumericValues.
   static CSSNumericValue* FromCSSValue(const CSSPrimitiveValue&);
   // https://drafts.css-houdini.org/css-typed-om/#rectify-a-numberish-value
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.idl b/third_party/blink/renderer/core/css/cssom/css_numeric_value.idl
index ae86a128..e12ebf8 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.idl
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.idl
@@ -23,5 +23,6 @@
   CSSNumericType type();
 
   // Putting Exposed=Window in the next line makes |parse| not exposed to PaintWorklet.
-  [RaisesException, NewObject, Exposed=Window] static CSSNumericValue parse(CSSOMString cssText);
+  [RaisesException, NewObject, Exposed=Window, CallWith=ExecutionContext]
+  static CSSNumericValue parse(CSSOMString cssText);
 };
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
index c46f35b..13316fd 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
@@ -833,4 +833,21 @@
   EXPECT_TRUE(value->IsRevertLayerValue());
 }
 
+// anchor() and anchor-size() shouldn't parse when the feature is disabled.
+TEST(CSSPropertyParserTest, AnchorPositioningDisabled) {
+  ScopedCSSAnchorPositioningForTest disabled_scope(false);
+
+  auto* context = MakeGarbageCollected<CSSParserContext>(
+      kHTMLStandardMode, SecureContextMode::kInsecureContext);
+
+  EXPECT_FALSE(
+      ParseCSSValue(CSSPropertyID::kTop, "anchor(--foo top)", context));
+  EXPECT_FALSE(
+      ParseCSSValue(CSSPropertyID::kBottom, "anchor(--foo bottom)", context));
+  EXPECT_FALSE(ParseCSSValue(CSSPropertyID::kWidth, "anchor-size(--foo width)",
+                             context));
+  EXPECT_FALSE(ParseCSSValue(CSSPropertyID::kHeight,
+                             "anchor-size(--foo height)", context));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 14b122f..a4ab6ec 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -2159,10 +2159,10 @@
   gfx::RectF reference_box = ReferenceBoxForTransform(*layout_object);
 
   TransformationMatrix transform;
-  style.ApplyTransform(transform, reference_box,
-                       ComputedStyle::kExcludeTransformOrigin,
-                       ComputedStyle::kExcludeMotionPath,
-                       ComputedStyle::kExcludeIndependentTransformProperties);
+  style.ApplyTransform(
+      transform, reference_box, ComputedStyle::kIncludeTransformOperations,
+      ComputedStyle::kExcludeTransformOrigin, ComputedStyle::kExcludeMotionPath,
+      ComputedStyle::kExcludeIndependentTransformProperties);
 
   // FIXME: Need to print out individual functions
   // (https://bugs.webkit.org/show_bug.cgi?id=23924)
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 553efc0..95a6ab4a 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -713,15 +713,18 @@
   STACK_ALLOCATED();
 
  public:
-  MathFunctionParser(CSSParserTokenRange& range,
-                     const CSSParserContext& context,
-                     CSSPrimitiveValue::ValueRange value_range)
+  MathFunctionParser(
+      CSSParserTokenRange& range,
+      const CSSParserContext& context,
+      CSSPrimitiveValue::ValueRange value_range,
+      CSSAnchorQueryTypes allowed_anchor_queries = kCSSAnchorQueryTypesNone)
       : source_range_(range), range_(range) {
     const CSSParserToken& token = range.Peek();
     if (token.GetType() == kFunctionToken) {
       calc_value_ = CSSMathFunctionValue::Create(
-          CSSMathExpressionNode::ParseMathFunction(token.FunctionId(),
-                                                   ConsumeFunction(range_)),
+          CSSMathExpressionNode::ParseMathFunction(
+              token.FunctionId(), ConsumeFunction(range_), context,
+              allowed_anchor_queries),
           value_range);
     }
     if (calc_value_ && calc_value_->HasComparisons())
@@ -1018,13 +1021,15 @@
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     CSSPrimitiveValue::ValueRange value_range,
-    UnitlessQuirk unitless) {
+    UnitlessQuirk unitless,
+    CSSAnchorQueryTypes allowed_anchor_queries) {
   const CSSParserToken& token = range.Peek();
   if (token.GetType() == kDimensionToken || token.GetType() == kNumberToken)
     return ConsumeLength(range, context, value_range, unitless);
   if (token.GetType() == kPercentageToken)
     return ConsumePercent(range, context, value_range);
-  MathFunctionParser math_parser(range, context, value_range);
+  MathFunctionParser math_parser(range, context, value_range,
+                                 allowed_anchor_queries);
   if (const CSSMathFunctionValue* calculation = math_parser.Value()) {
     if (CanConsumeCalcValue(calculation->Category(), context.Mode()))
       return math_parser.ConsumeValue();
@@ -5058,7 +5063,8 @@
       ValidWidthOrHeightKeyword(range.Peek().Id(), context))
     return ConsumeIdent(range);
   return ConsumeLengthOrPercent(
-      range, context, CSSPrimitiveValue::ValueRange::kNonNegative, unitless);
+      range, context, CSSPrimitiveValue::ValueRange::kNonNegative, unitless,
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchorSize));
 }
 
 CSSValue* ConsumeWidthOrHeight(CSSParserTokenRange& range,
@@ -5068,16 +5074,19 @@
       ValidWidthOrHeightKeyword(range.Peek().Id(), context))
     return ConsumeIdent(range);
   return ConsumeLengthOrPercent(
-      range, context, CSSPrimitiveValue::ValueRange::kNonNegative, unitless);
+      range, context, CSSPrimitiveValue::ValueRange::kNonNegative, unitless,
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchorSize));
 }
 
 CSSValue* ConsumeMarginOrOffset(CSSParserTokenRange& range,
                                 const CSSParserContext& context,
-                                UnitlessQuirk unitless) {
+                                UnitlessQuirk unitless,
+                                CSSAnchorQueryTypes allowed_anchor_queries) {
   if (range.Peek().Id() == CSSValueID::kAuto)
     return ConsumeIdent(range);
   return ConsumeLengthOrPercent(range, context,
-                                CSSPrimitiveValue::ValueRange::kAll, unitless);
+                                CSSPrimitiveValue::ValueRange::kAll, unitless,
+                                allowed_anchor_queries);
 }
 
 CSSValue* ConsumeScrollPadding(CSSParserTokenRange& range,
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
index 18117a88..2741f35 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -7,6 +7,7 @@
 
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_anchor_query_type.h"
 #include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
 #include "third_party/blink/renderer/core/css/css_function_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
@@ -117,7 +118,8 @@
     CSSParserTokenRange&,
     const CSSParserContext&,
     CSSPrimitiveValue::ValueRange,
-    UnitlessQuirk = UnitlessQuirk::kForbid);
+    UnitlessQuirk = UnitlessQuirk::kForbid,
+    CSSAnchorQueryTypes = kCSSAnchorQueryTypesNone);
 CSSPrimitiveValue* ConsumeSVGGeometryPropertyLength(
     CSSParserTokenRange&,
     const CSSParserContext&,
@@ -455,7 +457,8 @@
 
 CSSValue* ConsumeMarginOrOffset(CSSParserTokenRange&,
                                 const CSSParserContext&,
-                                UnitlessQuirk);
+                                UnitlessQuirk,
+                                CSSAnchorQueryTypes = kCSSAnchorQueryTypesNone);
 CSSValue* ConsumeScrollPadding(CSSParserTokenRange&, const CSSParserContext&);
 CSSValue* ConsumeOffsetPath(CSSParserTokenRange&, const CSSParserContext&);
 CSSValue* ConsumePathOrNone(CSSParserTokenRange&);
diff --git a/third_party/blink/renderer/core/css/properties/longhands/custom_property_test.cc b/third_party/blink/renderer/core/css/properties/longhands/custom_property_test.cc
index 266d8c9..80257781 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/custom_property_test.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/custom_property_test.cc
@@ -237,4 +237,52 @@
   EXPECT_TRUE(length.HasInitialValue());
 }
 
+TEST_F(CustomPropertyTest, ParseAnchorQueriesAsLength) {
+  ScopedCSSAnchorPositioningForTest enabled_scope(true);
+
+  RegisterProperty(GetDocument(), "--x", "<length>", "0px", false);
+  CustomProperty property("--x", GetDocument());
+
+  // We can't parse anchor queries as a <length>, because it can't be resolved
+  // into a pixel value at style time.
+  EXPECT_FALSE(
+      ParseValue(property, "anchor(--foo top)", CSSParserLocalContext()));
+  EXPECT_FALSE(ParseValue(property, "anchor-size(--foo width)",
+                          CSSParserLocalContext()));
+}
+
+TEST_F(CustomPropertyTest, ParseAnchorQueriesAsLengthPercentage) {
+  ScopedCSSAnchorPositioningForTest enabled_scope(true);
+
+  RegisterProperty(GetDocument(), "--x", "<length-percentage>", "0px", false);
+  CustomProperty property("--x", GetDocument());
+
+  {
+    const CSSValue* value =
+        ParseValue(property, "anchor(--foo top)", CSSParserLocalContext());
+    ASSERT_TRUE(value);
+    EXPECT_EQ("anchor(--foo top)", value->CssText());
+  }
+
+  {
+    const CSSValue* value = ParseValue(property, "anchor-size(--foo width)",
+                                       CSSParserLocalContext());
+    ASSERT_TRUE(value);
+    EXPECT_EQ("anchor-size(--foo width)", value->CssText());
+  }
+
+  {
+    // There are no restrictions on what anchor queries are allowed in a custom
+    // property, so mixing anchor() and anchor-size() is also allowed, although
+    // using it in any builtin property via var() makes it invalid at
+    // computed-value time.
+    const CSSValue* value = ParseValue(
+        property, "calc(anchor(--foo top) + anchor-size(--foo width))",
+        CSSParserLocalContext());
+    ASSERT_TRUE(value);
+    EXPECT_EQ("calc(anchor(--foo top) + anchor-size(--foo width))",
+              value->CssText());
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 94b4af3..374e818 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -4,6 +4,7 @@
 
 #include "base/numerics/clamped_math.h"
 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
+#include "third_party/blink/renderer/core/css/css_anchor_query_type.h"
 #include "third_party/blink/renderer/core/css/css_axis_value.h"
 #include "third_party/blink/renderer/core/css/css_color.h"
 #include "third_party/blink/renderer/core/css/css_counter_value.h"
@@ -1281,8 +1282,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext& local_context) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context,
-      css_parsing_utils::UnitlessUnlessShorthand(local_context));
+      range, context, css_parsing_utils::UnitlessUnlessShorthand(local_context),
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 bool Bottom::IsLayoutDependent(const ComputedStyle* style,
@@ -3662,7 +3663,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context, css_parsing_utils::UnitlessQuirk::kForbid);
+      range, context, css_parsing_utils::UnitlessQuirk::kForbid,
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 const CSSValue* InsetBlockStart::ParseSingleValue(
@@ -3670,7 +3672,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context, css_parsing_utils::UnitlessQuirk::kForbid);
+      range, context, css_parsing_utils::UnitlessQuirk::kForbid,
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 const CSSValue* InsetInlineEnd::ParseSingleValue(
@@ -3678,7 +3681,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context, css_parsing_utils::UnitlessQuirk::kForbid);
+      range, context, css_parsing_utils::UnitlessQuirk::kForbid,
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 const CSSValue* InsetInlineStart::ParseSingleValue(
@@ -3686,7 +3690,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext&) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context, css_parsing_utils::UnitlessQuirk::kForbid);
+      range, context, css_parsing_utils::UnitlessQuirk::kForbid,
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 const blink::Color InternalVisitedBackgroundColor::ColorIncludingFallback(
@@ -4313,8 +4318,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext& local_context) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context,
-      css_parsing_utils::UnitlessUnlessShorthand(local_context));
+      range, context, css_parsing_utils::UnitlessUnlessShorthand(local_context),
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 bool Left::IsLayoutDependent(const ComputedStyle* style,
@@ -5895,8 +5900,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext& local_context) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context,
-      css_parsing_utils::UnitlessUnlessShorthand(local_context));
+      range, context, css_parsing_utils::UnitlessUnlessShorthand(local_context),
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 bool Right::IsLayoutDependent(const ComputedStyle* style,
@@ -7325,8 +7330,8 @@
     const CSSParserContext& context,
     const CSSParserLocalContext& local_context) const {
   return css_parsing_utils::ConsumeMarginOrOffset(
-      range, context,
-      css_parsing_utils::UnitlessUnlessShorthand(local_context));
+      range, context, css_parsing_utils::UnitlessUnlessShorthand(local_context),
+      static_cast<CSSAnchorQueryTypes>(CSSAnchorQueryType::kAnchor));
 }
 
 bool Top::IsLayoutDependent(const ComputedStyle* style,
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index 2f8ce06..3951516f 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -48,7 +48,7 @@
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
-using base::StringPattern;
+using base::MatcherStringPattern;
 using base::SubstringSetMatcher;
 
 namespace blink {
@@ -695,7 +695,7 @@
     if (ruleset->size() < GetMinimumRulesetSizeForSubstringMatcher()) {
       continue;
     }
-    std::vector<StringPattern> patterns;
+    std::vector<MatcherStringPattern> patterns;
     int rule_index = 0;
     for (const Member<const RuleData>& rule : *ruleset) {
       AtomicString id;
@@ -724,7 +724,7 @@
       // use the tree for true/false information anyway, we can remove them.
       bool already_exists =
           any_of(patterns.begin(), patterns.end(),
-                 [&pattern](const StringPattern& existing_pattern) {
+                 [&pattern](const MatcherStringPattern& existing_pattern) {
                    return existing_pattern.pattern() == pattern;
                  });
       if (!already_exists) {
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index b905913..70ef148 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -375,15 +375,19 @@
     if (SVGImage* svg_image = EmbeddedSVGImage()) {
       svg_image->GetIntrinsicSizingInfo(intrinsic_sizing_info);
 
-      if (auto view_box = ComputeObjectViewBoxRect()) {
+      // Scale for the element's effective zoom (which includes scaling for
+      // device scale) is already applied when computing the view box. If the
+      // element has no view box then it needs to be explicitly applied here.
+      if (auto view_box_size = ComputeObjectViewBoxSizeForIntrinsicSizing()) {
         DCHECK(intrinsic_sizing_info.has_width);
         DCHECK(intrinsic_sizing_info.has_height);
-        intrinsic_sizing_info.size = static_cast<gfx::SizeF>(view_box->size);
+        intrinsic_sizing_info.size = *view_box_size;
+      } else {
+        intrinsic_sizing_info.size.Scale(StyleRef().EffectiveZoom());
       }
 
       // Handle zoom & vertical writing modes here, as the embedded SVG document
       // doesn't know about them.
-      intrinsic_sizing_info.size.Scale(StyleRef().EffectiveZoom());
       if (StyleRef().GetObjectFit() != EObjectFit::kScaleDown)
         intrinsic_sizing_info.size.Scale(ImageDevicePixelRatio());
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 2e7579e7..f725763 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -2800,11 +2800,12 @@
                         WebFeature::kHiddenBackfaceWithPossible3D);
       // For consistency with existing code usage, this uses
       // Has3DTransformOperation rather than the slightly narrower
-      // HasNonTrivial3DTransformOperation (which is only web-exposed for
-      // compositing decisions on low-end devices).  However, given the
-      // discussion in https://github.com/w3c/csswg-drafts/issues/3305 it's
-      // possible we may want to tie backface-visibility behavior to something
-      // closer to the latter.
+      // HasNonTrivial3DTransformOperation (which used to exist, and was only
+      // web-exposed for compositing decisions on low-end devices).  However,
+      // given the discussion in
+      // https://github.com/w3c/csswg-drafts/issues/3305 it's possible we may
+      // want to tie backface-visibility behavior to something closer to the
+      // latter.
       if (style_->Has3DTransformOperation()) {
         UseCounter::Count(GetDocument(), WebFeature::kHiddenBackfaceWith3D);
       }
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.cc b/third_party/blink/renderer/core/layout/layout_replaced.cc
index 5d1ec58a..579ffc08 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.cc
+++ b/third_party/blink/renderer/core/layout/layout_replaced.cc
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
+#include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/size_f.h"
 
 namespace blink {
@@ -634,22 +635,23 @@
   computed_values.position_ = logical_top_pos;
 }
 
+absl::optional<gfx::SizeF>
+LayoutReplaced::ComputeObjectViewBoxSizeForIntrinsicSizing() const {
+  if (IntrinsicWidthOverride() || IntrinsicHeightOverride())
+    return absl::nullopt;
+
+  if (auto view_box = ComputeObjectViewBoxRect())
+    return static_cast<gfx::SizeF>(view_box->size);
+
+  return absl::nullopt;
+}
+
 absl::optional<PhysicalRect> LayoutReplaced::ComputeObjectViewBoxRect(
     const LayoutSize* overridden_intrinsic_size) const {
   scoped_refptr<BasicShape> object_view_box = StyleRef().ObjectViewBox();
   if (LIKELY(!object_view_box))
     return absl::nullopt;
 
-  // We ignore applying the object-view-box property if the element has size
-  // containment, including when contain-intrinsic-size is specified to provide
-  // an explicit intrinsic size for the element's content.
-  // See https://github.com/w3c/csswg-drafts/issues/7187 for a detailed
-  // discussion.
-  // TODO(khushalsagar) : Its unclear whether the view-box should still apply
-  // for paint operations. Update once the github issue is resolved.
-  if (IntrinsicWidthOverride() || IntrinsicHeightOverride())
-    return absl::nullopt;
-
   const auto& intrinsic_size =
       overridden_intrinsic_size ? *overridden_intrinsic_size : intrinsic_size_;
   if (intrinsic_size.IsEmpty())
@@ -658,24 +660,20 @@
   if (!CanApplyObjectViewBox())
     return absl::nullopt;
 
-  // TODO(khushalsagar) : Also allow rect() and xywh().
-  const auto* inset_shape = To<BasicShapeInset>(object_view_box.get());
-  LayoutUnit left =
-      MinimumValueForLength(inset_shape->Left(), intrinsic_size.Width());
-  LayoutUnit top =
-      MinimumValueForLength(inset_shape->Top(), intrinsic_size.Height());
-  LayoutUnit right =
-      intrinsic_size.Width() -
-      MinimumValueForLength(inset_shape->Right(), intrinsic_size.Width());
-  LayoutUnit bottom =
-      intrinsic_size.Height() -
-      MinimumValueForLength(inset_shape->Bottom(), intrinsic_size.Height());
+  DCHECK(object_view_box->GetType() == BasicShape::kBasicShapeRectType ||
+         object_view_box->GetType() == BasicShape::kBasicShapeInsetType ||
+         object_view_box->GetType() == BasicShape::kBasicShapeXYWHType);
 
-  if (left >= right || top >= bottom)
+  Path path;
+  gfx::RectF bounding_box(0, 0, intrinsic_size.Width().ToFloat(),
+                          intrinsic_size.Height().ToFloat());
+  object_view_box->GetPath(path, bounding_box, 1.f);
+
+  const PhysicalRect view_box_rect =
+      PhysicalRect::EnclosingRect(path.BoundingRect());
+  if (view_box_rect.IsEmpty())
     return absl::nullopt;
 
-  const PhysicalRect view_box_rect(PhysicalOffset(left, top),
-                                   PhysicalSize(right - left, bottom - top));
   const PhysicalRect intrinsic_rect(PhysicalOffset(), intrinsic_size);
   if (view_box_rect == intrinsic_rect)
     return absl::nullopt;
@@ -827,9 +825,9 @@
   NOT_DESTROYED();
   DCHECK(!ShouldApplySizeContainment());
 
-  auto view_box = ComputeObjectViewBoxRect();
-  if (view_box) {
-    intrinsic_sizing_info.size = static_cast<gfx::SizeF>(view_box->size);
+  auto view_box_size = ComputeObjectViewBoxSizeForIntrinsicSizing();
+  if (view_box_size) {
+    intrinsic_sizing_info.size = *view_box_size;
     if (!IsHorizontalWritingMode())
       intrinsic_sizing_info.size.Transpose();
   } else {
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.h b/third_party/blink/renderer/core/layout/layout_replaced.h
index 9f98275..61f21f6 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.h
+++ b/third_party/blink/renderer/core/layout/layout_replaced.h
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "ui/gfx/geometry/size_f.h"
 
 namespace blink {
 
@@ -175,14 +176,21 @@
     return type == kLayoutObjectReplaced || LayoutBox::IsOfType(type);
   }
 
-  // Computes a rect, relative to the element's intrinsic bounds, that should be
-  // used as the content source when rendering this element. This value is
-  // used as the input for layout sizing and object-fit/object-position during
-  // painting.
+  // The intrinsic size for a replaced element is based on its content's natural
+  // size. This computes the size including the modification from
+  // object-view-box for layout.
+  // Note that the intrinsic size for the element can be independent of its
+  // content's natural size. For example, if contain-intrinsic-size is
+  // specified. Returns null for these cases.
+  absl::optional<gfx::SizeF> ComputeObjectViewBoxSizeForIntrinsicSizing() const;
+
+ private:
+  // Computes a rect, relative to the element's content's natural size, that
+  // should be used as the content source when rendering this element. This
+  // value is used as the input for object-fit/object-position during painting.
   absl::optional<PhysicalRect> ComputeObjectViewBoxRect(
       const LayoutSize* overridden_intrinsic_size = nullptr) const;
 
- private:
   PhysicalRect ComputeObjectFitAndPositionRect(
       const LayoutSize* overridden_intrinsic_size) const;
 
diff --git a/third_party/blink/renderer/core/layout/svg/transform_helper.cc b/third_party/blink/renderer/core/layout/svg/transform_helper.cc
index ab77f61..517fed4 100644
--- a/third_party/blink/renderer/core/layout/svg/transform_helper.cc
+++ b/third_party/blink/renderer/core/layout/svg/transform_helper.cc
@@ -82,9 +82,10 @@
   // https://svgwg.org/svg2-draft/coords.html#ObjectBoundingBoxUnits
   TransformationMatrix transform;
   gfx::RectF reference_box = ComputeReferenceBox(layout_object);
-  style.ApplyTransform(transform, reference_box, apply_transform_origin,
-                       ComputedStyle::kIncludeMotionPath,
-                       ComputedStyle::kIncludeIndependentTransformProperties);
+  style.ApplyTransform(
+      transform, reference_box, ComputedStyle::kIncludeTransformOperations,
+      apply_transform_origin, ComputedStyle::kIncludeMotionPath,
+      ComputedStyle::kIncludeIndependentTransformProperties);
   const float zoom = style.EffectiveZoom();
   if (zoom != 1)
     transform.Zoom(1 / zoom);
diff --git a/third_party/blink/renderer/core/paint/background_image_geometry.cc b/third_party/blink/renderer/core/paint/background_image_geometry.cc
index c3aad446..20b9be5 100644
--- a/third_party/blink/renderer/core/paint/background_image_geometry.cc
+++ b/third_party/blink/renderer/core/paint/background_image_geometry.cc
@@ -429,9 +429,10 @@
          layer && !layer->IsRootLayer(); layer = layer->Parent()) {
       // Check LayoutObject::HasTransformRelatedProperty() first to exclude
       // non-applicable transforms and will-change: transform.
-      if (layer->GetLayoutObject().HasTransformRelatedProperty() &&
+      LayoutObject& object = layer->GetLayoutObject();
+      if (object.HasTransformRelatedProperty() &&
           (layer->Transform() ||
-           layer->GetLayoutObject().StyleRef().HasWillChangeTransformHint())) {
+           object.StyleRef().HasWillChangeHintForAnyTransformProperty())) {
         has_background_fixed_to_viewport_ = false;
         break;
       }
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index 0904115..6e239d7b 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -68,6 +68,12 @@
 
   if (style.HasWillChangeTransformHint())
     reasons |= CompositingReason::kWillChangeTransform;
+  if (style.HasWillChangeScaleHint())
+    reasons |= CompositingReason::kWillChangeScale;
+  if (style.HasWillChangeRotateHint())
+    reasons |= CompositingReason::kWillChangeRotate;
+  if (style.HasWillChangeTranslateHint())
+    reasons |= CompositingReason::kWillChangeTranslate;
   if (style.HasWillChangeOpacityHint())
     reasons |= CompositingReason::kWillChangeOpacity;
   if (style.HasWillChangeFilterHint())
@@ -309,20 +315,33 @@
 CompositingReasons
 CompositingReasonFinder::PotentialCompositingReasonsFor3DTransform(
     const ComputedStyle& style) {
-  // Don't composite "trivial" 3D transforms such as translateZ(0).
+  CompositingReasons reasons = CompositingReason::kNone;
+
   if (Platform::Current()->IsLowEndDevice()) {
-    return style.HasNonTrivial3DTransformOperation()
-               ? CompositingReason::k3DTransform
-               : CompositingReason::kNone;
+    // Don't composite "trivial" 3D transforms such as translateZ(0).
+    if (style.Transform().HasNonTrivial3DComponent())
+      reasons |= CompositingReason::k3DTransform;
+  } else {
+    if (style.Transform().HasNonPerspective3DOperation()) {
+      if (style.Transform().HasNonTrivial3DComponent())
+        reasons |= CompositingReason::k3DTransform;
+      else
+        reasons |= CompositingReason::kTrivial3DTransform;
+    }
   }
 
-  if (style.Has3DTransformOperation()) {
-    return style.HasNonTrivial3DTransformOperation()
-               ? CompositingReason::k3DTransform
-               : CompositingReason::kTrivial3DTransform;
+  if (style.Translate() && style.Translate()->Z() != 0)
+    reasons |= CompositingReason::k3DTranslate;
+
+  if (style.Rotate() &&
+      (style.Rotate()->X() != 0 || style.Rotate()->Y() != 0)) {
+    reasons |= CompositingReason::k3DRotate;
   }
 
-  return CompositingReason::kNone;
+  if (style.Scale() && style.Scale()->Z() != 1)
+    reasons |= CompositingReason::k3DScale;
+
+  return reasons;
 }
 
 static bool ObjectTypeSupportsCompositedTransformAnimation(
@@ -347,6 +366,15 @@
   if (style.HasCurrentTransformAnimation() &&
       ObjectTypeSupportsCompositedTransformAnimation(object))
     reasons |= CompositingReason::kActiveTransformAnimation;
+  if (style.HasCurrentScaleAnimation() &&
+      ObjectTypeSupportsCompositedTransformAnimation(object))
+    reasons |= CompositingReason::kActiveScaleAnimation;
+  if (style.HasCurrentRotateAnimation() &&
+      ObjectTypeSupportsCompositedTransformAnimation(object))
+    reasons |= CompositingReason::kActiveRotateAnimation;
+  if (style.HasCurrentTranslateAnimation() &&
+      ObjectTypeSupportsCompositedTransformAnimation(object))
+    reasons |= CompositingReason::kActiveTranslateAnimation;
   if (style.HasCurrentOpacityAnimation())
     reasons |= CompositingReason::kActiveOpacityAnimation;
   if (style.HasCurrentFilterAnimation())
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
index 669f8e51..f3fb833 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder_test.cc
@@ -209,6 +209,9 @@
 
   style->SetSubtreeWillChangeContents(false);
   style->SetHasCurrentTransformAnimation(false);
+  style->SetHasCurrentScaleAnimation(false);
+  style->SetHasCurrentRotateAnimation(false);
+  style->SetHasCurrentTranslateAnimation(false);
   style->SetHasCurrentOpacityAnimation(false);
   style->SetHasCurrentFilterAnimation(false);
   style->SetHasCurrentBackdropFilterAnimation(false);
@@ -218,31 +221,45 @@
       CompositingReason::kNone,
       CompositingReasonFinder::CompositingReasonsForAnimation(*object));
 
-  CompositingReasons expected_compositing_reason_for_transform_animation =
-      supports_transform_animation
-          ? CompositingReason::kActiveTransformAnimation
-          : CompositingReason::kNone;
+  CompositingReasons expected_reason = CompositingReason::kNone;
 
   style->SetHasCurrentTransformAnimation(true);
-  EXPECT_EQ(expected_compositing_reason_for_transform_animation,
+  if (supports_transform_animation)
+    expected_reason |= CompositingReason::kActiveTransformAnimation;
+  EXPECT_EQ(expected_reason,
+            CompositingReasonFinder::CompositingReasonsForAnimation(*object));
+
+  style->SetHasCurrentScaleAnimation(true);
+  if (supports_transform_animation)
+    expected_reason |= CompositingReason::kActiveScaleAnimation;
+  EXPECT_EQ(expected_reason,
+            CompositingReasonFinder::CompositingReasonsForAnimation(*object));
+
+  style->SetHasCurrentRotateAnimation(true);
+  if (supports_transform_animation)
+    expected_reason |= CompositingReason::kActiveRotateAnimation;
+  EXPECT_EQ(expected_reason,
+            CompositingReasonFinder::CompositingReasonsForAnimation(*object));
+
+  style->SetHasCurrentTranslateAnimation(true);
+  if (supports_transform_animation)
+    expected_reason |= CompositingReason::kActiveTranslateAnimation;
+  EXPECT_EQ(expected_reason,
             CompositingReasonFinder::CompositingReasonsForAnimation(*object));
 
   style->SetHasCurrentOpacityAnimation(true);
-  EXPECT_EQ(expected_compositing_reason_for_transform_animation |
-                CompositingReason::kActiveOpacityAnimation,
+  expected_reason |= CompositingReason::kActiveOpacityAnimation;
+  EXPECT_EQ(expected_reason,
             CompositingReasonFinder::CompositingReasonsForAnimation(*object));
 
   style->SetHasCurrentFilterAnimation(true);
-  EXPECT_EQ(expected_compositing_reason_for_transform_animation |
-                CompositingReason::kActiveOpacityAnimation |
-                CompositingReason::kActiveFilterAnimation,
+  expected_reason |= CompositingReason::kActiveFilterAnimation;
+  EXPECT_EQ(expected_reason,
             CompositingReasonFinder::CompositingReasonsForAnimation(*object));
 
   style->SetHasCurrentBackdropFilterAnimation(true);
-  EXPECT_EQ(expected_compositing_reason_for_transform_animation |
-                CompositingReason::kActiveOpacityAnimation |
-                CompositingReason::kActiveFilterAnimation |
-                CompositingReason::kActiveBackdropFilterAnimation,
+  expected_reason |= CompositingReason::kActiveBackdropFilterAnimation;
+  EXPECT_EQ(expected_reason,
             CompositingReasonFinder::CompositingReasonsForAnimation(*object));
 }
 
diff --git a/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc b/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc
index 64e882e..3f1cb03 100644
--- a/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc
+++ b/third_party/blink/renderer/core/paint/hit_testing_transform_state.cc
@@ -43,6 +43,15 @@
   }
 }
 
+void HitTestingTransformState::ApplyTransform(
+    const GeometryMapper::Translation2DOrMatrix& transform) {
+  if (transform.IsIdentityOr2DTranslation()) {
+    Translate(transform.Translation2D());
+  } else {
+    accumulated_transform_.Multiply(transform.Matrix());
+  }
+}
+
 void HitTestingTransformState::Flatten() {
   TransformationMatrix inverse_transform = accumulated_transform_.Inverse();
   last_planar_point_ = inverse_transform.ProjectPoint(last_planar_point_);
diff --git a/third_party/blink/renderer/core/paint/hit_testing_transform_state.h b/third_party/blink/renderer/core/paint/hit_testing_transform_state.h
index 444d42b..b90f5d3 100644
--- a/third_party/blink/renderer/core/paint/hit_testing_transform_state.h
+++ b/third_party/blink/renderer/core/paint/hit_testing_transform_state.h
@@ -27,6 +27,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_HIT_TESTING_TRANSFORM_STATE_H_
 
 #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
 #include "third_party/blink/renderer/platform/transforms/affine_transform.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -54,6 +55,7 @@
 
   void Translate(const gfx::Vector2dF&);
   void ApplyTransform(const TransformPaintPropertyNode&);
+  void ApplyTransform(const GeometryMapper::Translation2DOrMatrix&);
 
   gfx::PointF MappedPoint() const;
   gfx::QuadF MappedQuad() const;
diff --git a/third_party/blink/renderer/core/paint/object_paint_properties.h b/third_party/blink/renderer/core/paint/object_paint_properties.h
index ce8a4d34..3981a441 100644
--- a/third_party/blink/renderer/core/paint/object_paint_properties.h
+++ b/third_party/blink/renderer/core/paint/object_paint_properties.h
@@ -101,9 +101,36 @@
   // +-[ StickyTranslation ]
   //  /    This applies the sticky offset induced by position:sticky.
   // |
+  // +-[ Translate ]
+  //   |   The transform from CSS 'translate' (including the effects of
+  //  /    'transform-origin').
+  // |
+  // +-[ Rotate ]
+  //   |   The transform from CSS 'rotate' (including the effects of
+  //  /    'transform-origin').
+  // |
+  // +-[ Scale ]
+  //   |   The transform from CSS 'scale' (including the effects of
+  //  /    'transform-origin').
+  // |
+  // +-[ Offset ]
+  //   |   The transform from the longhand properties that comprise the CSS
+  //  /    'offset' shorthand (including the effects of 'transform-origin').
+  // |
   // +-[ Transform ]
-  //   |   The space created by CSS transform. This is the local border box
-  //   |   space.
+  //   |   The transform from CSS 'transform' (including the effects of
+  //   |   'transform-origin').
+  //   |
+  //   |   For SVG, this also includes 'translate', 'rotate', 'scale',
+  //   |   'offset-*' (instead of the nodes above) and the effects of
+  //   |   some characteristics of the SVG viewport and the "SVG
+  //   |   additional translation" (for the x and y attributes on
+  //   |   svg:use).
+  //   |
+  //   |   This is the local border box space (see
+  //   |   FragmentData::LocalBorderBoxProperties); the nodes below influence
+  //   |   the transform for the children but not the LayoutObject itself.
+  //   |
   //   +-[ Perspective ]
   //     |   The space created by CSS perspective.
   //     +-[ ReplacedContentTransform ]
@@ -127,12 +154,17 @@
   // https://drafts.csswg.org/css-transforms-2/#accumulated-3d-transformation-matrix-computation
  public:
   bool HasTransformNode() const {
-    return paint_offset_translation_ || sticky_translation_ || transform_ ||
-           perspective_ || replaced_content_transform_ || scroll_translation_ ||
+    return paint_offset_translation_ || sticky_translation_ || translate_ ||
+           rotate_ || scale_ || offset_ || transform_ || perspective_ ||
+           replaced_content_transform_ || scroll_translation_ ||
            transform_isolation_node_;
   }
   ADD_TRANSFORM(PaintOffsetTranslation, paint_offset_translation_);
   ADD_TRANSFORM(StickyTranslation, sticky_translation_);
+  ADD_TRANSFORM(Translate, translate_);
+  ADD_TRANSFORM(Rotate, rotate_);
+  ADD_TRANSFORM(Scale, scale_);
+  ADD_TRANSFORM(Offset, offset_);
   ADD_TRANSFORM(Transform, transform_);
   ADD_TRANSFORM(Perspective, perspective_);
   ADD_TRANSFORM(ReplacedContentTransform, replaced_content_transform_);
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 7d07674..caf12d7 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -360,7 +360,8 @@
     DCHECK(box);
     transform->MakeIdentity();
     box->StyleRef().ApplyTransform(
-        *transform, box->Size(), ComputedStyle::kIncludeTransformOrigin,
+        *transform, box->Size(), ComputedStyle::kIncludeTransformOperations,
+        ComputedStyle::kIncludeTransformOrigin,
         ComputedStyle::kIncludeMotionPath,
         ComputedStyle::kIncludeIndependentTransformProperties);
     if (!box->GetDocument().GetSettings()->GetAcceleratedCompositingEnabled())
@@ -1473,13 +1474,11 @@
     }
   }
 
-  gfx::Vector2dF offset(-transform_container_fragment.PaintOffset());
-  auto offset_translation = GeometryMapper::SourceToDestinationProjection(
-      local_fragment.PreTransform(), *container_transform);
-  DCHECK(offset_translation.IsIdentityOr2DTranslation());
-  offset += offset_translation.Translation2D();
-  offset += gfx::Vector2dF(local_fragment.PaintOffset());
-  transform_state.Translate(offset);
+  transform_state.Translate(
+      gfx::Vector2dF(-transform_container_fragment.PaintOffset()));
+  transform_state.ApplyTransform(GeometryMapper::SourceToDestinationProjection(
+      local_fragment.PreTransform(), *container_transform));
+  transform_state.Translate(gfx::Vector2dF(local_fragment.PaintOffset()));
 
   if (const auto* properties = local_fragment.PaintProperties()) {
     if (const auto* transform = properties->Transform())
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index bf44e1a3..d583c9d 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -159,7 +159,13 @@
     if (properties->Perspective())
       return true;
 
-    if (const auto* transform = properties->Transform()) {
+    const TransformPaintPropertyNode* transform_nodes[] = {
+        properties->Transform(), properties->Offset(), properties->Scale(),
+        properties->Rotate(), properties->Translate()};
+    for (const auto* transform : transform_nodes) {
+      if (!transform)
+        continue;
+
       // A CSS transform can also have perspective like
       // "transform: perspective(100px) rotateY(45deg)". In these cases, we
       // also want to skip cull rect mapping. See http://crbug.com/887558 for
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 18d88c4a..da499ec 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
@@ -245,6 +245,27 @@
   ALWAYS_INLINE void UpdateForObjectLocationAndSize(
       absl::optional<gfx::Vector2d>& paint_offset_translation);
   ALWAYS_INLINE void UpdateStickyTranslation();
+
+  void UpdateIndividualTransform(
+      bool (*needs_property)(const LayoutObject&, CompositingReasons),
+      void (*compute_matrix)(const ComputedStyle& style,
+                             const PhysicalSize& size,
+                             TransformationMatrix& matrix),
+      CompositingReasons active_animation_reason,
+      CompositingReasons compositing_reasons_for_property,
+      CompositorElementIdNamespace compositor_namespace,
+      bool (ComputedStyle::*running_on_compositor_test)() const,
+      const TransformPaintPropertyNode* (ObjectPaintProperties::*getter)()
+          const,
+      PaintPropertyChangeType (ObjectPaintProperties::*updater)(
+          const TransformPaintPropertyNodeOrAlias&,
+          TransformPaintPropertyNode::State&&,
+          const TransformPaintPropertyNode::AnimationState&),
+      bool (ObjectPaintProperties::*clearer)());
+  ALWAYS_INLINE void UpdateTranslate();
+  ALWAYS_INLINE void UpdateRotate();
+  ALWAYS_INLINE void UpdateScale();
+  ALWAYS_INLINE void UpdateOffset();
   ALWAYS_INLINE void UpdateTransform();
   ALWAYS_INLINE void UpdateTransformForSVGChild(CompositingReasons);
   ALWAYS_INLINE bool NeedsEffect() const;
@@ -517,7 +538,9 @@
     return false;
   }
   if (full_context_.direct_compositing_reasons &
-      CompositingReason::kActiveTransformAnimation) {
+      (CompositingReason::kActiveTransformAnimation |
+       CompositingReason::kActiveRotateAnimation |
+       CompositingReason::kActiveScaleAnimation)) {
     if (const auto* element = DynamicTo<Element>(object_.GetNode())) {
       DCHECK(element->GetElementAnimations());
       return element->GetElementAnimations()->IsIdentityOrTranslation();
@@ -751,12 +774,17 @@
   return reasons;
 }
 
+// TODO(crbug.com/1278452): Merge SVG handling into the primary codepath.
 static bool NeedsTransformForSVGChild(
     const LayoutObject& object,
     CompositingReasons direct_compositing_reasons) {
   if (!object.IsSVGChild() || object.IsText())
     return false;
-  if (direct_compositing_reasons & CompositingReasonsForTransformProperty())
+  if (direct_compositing_reasons &
+      (CompositingReasonsForTransformProperty() |
+       CompositingReason::kDirectReasonsForTranslateProperty |
+       CompositingReason::kDirectReasonsForRotateProperty |
+       CompositingReason::kDirectReasonsForScaleProperty))
     return true;
   return !object.LocalToSVGParentTransform().IsIdentity();
 }
@@ -805,6 +833,7 @@
 
 // SVG does not use the general transform update of |UpdateTransform|, instead
 // creating a transform node for SVG-specific transforms without 3D.
+// TODO(crbug.com/1278452): Merge SVG handling into the primary codepath.
 void FragmentPaintPropertyTreeBuilder::UpdateTransformForSVGChild(
     CompositingReasons direct_compositing_reasons) {
   DCHECK(properties_);
@@ -881,9 +910,68 @@
       style.TransformOriginZ());
 }
 
+static bool NeedsIndividualTransform(
+    const LayoutObject& object,
+    CompositingReasons relevant_compositing_reasons,
+    bool (*style_test)(const ComputedStyle&)) {
+  if (object.IsText() || object.IsSVGChild())
+    return false;
+
+  if (relevant_compositing_reasons)
+    return true;
+
+  if (!object.IsBox())
+    return false;
+
+  if (style_test(object.StyleRef()))
+    return true;
+
+  return false;
+}
+
+static bool NeedsTranslate(const LayoutObject& object,
+                           CompositingReasons direct_compositing_reasons) {
+  return NeedsIndividualTransform(
+      object,
+      direct_compositing_reasons &
+          CompositingReason::kDirectReasonsForTranslateProperty,
+      [](const ComputedStyle& style) {
+        return style.Translate() || style.HasCurrentTranslateAnimation();
+      });
+}
+
+static bool NeedsRotate(const LayoutObject& object,
+                        CompositingReasons direct_compositing_reasons) {
+  return NeedsIndividualTransform(
+      object,
+      direct_compositing_reasons &
+          CompositingReason::kDirectReasonsForRotateProperty,
+      [](const ComputedStyle& style) {
+        return style.Rotate() || style.HasCurrentRotateAnimation();
+      });
+}
+
+static bool NeedsScale(const LayoutObject& object,
+                       CompositingReasons direct_compositing_reasons) {
+  return NeedsIndividualTransform(
+      object,
+      direct_compositing_reasons &
+          CompositingReason::kDirectReasonsForScaleProperty,
+      [](const ComputedStyle& style) {
+        return style.Scale() || style.HasCurrentScaleAnimation();
+      });
+}
+
+static bool NeedsOffset(const LayoutObject& object,
+                        CompositingReasons direct_compositing_reasons) {
+  return NeedsIndividualTransform(
+      object, CompositingReason::kNone,
+      [](const ComputedStyle& style) { return style.HasOffset(); });
+}
+
 static bool NeedsTransform(const LayoutObject& object,
                            CompositingReasons direct_compositing_reasons) {
-  if (object.IsText())
+  if (object.IsText() || object.IsSVGChild())
     return false;
 
   if (object.StyleRef().BackfaceVisibility() == EBackfaceVisibility::kHidden)
@@ -892,7 +980,12 @@
   if (direct_compositing_reasons & CompositingReasonsForTransformProperty())
     return true;
 
-  if (object.HasTransform() || object.Preserves3D())
+  if (!object.IsBox())
+    return false;
+
+  if (object.StyleRef().HasTransformOperations() ||
+      object.StyleRef().HasCurrentTransformAnimation() ||
+      object.StyleRef().Preserves3D())
     return true;
 
   return false;
@@ -901,7 +994,10 @@
 static bool UpdateBoxSizeAndCheckActiveAnimationAxisAlignment(
     const LayoutBox& object,
     CompositingReasons compositing_reasons) {
-  if (!(compositing_reasons & CompositingReason::kActiveTransformAnimation))
+  if (!(compositing_reasons & (CompositingReason::kActiveTransformAnimation |
+                               CompositingReason::kActiveScaleAnimation |
+                               CompositingReason::kActiveRotateAnimation |
+                               CompositingReason::kActiveTranslateAnimation)))
     return false;
 
   if (!object.GetNode() || !object.GetNode()->IsElementNode())
@@ -913,12 +1009,24 @@
       gfx::SizeF(object.Size()));
 }
 
-void FragmentPaintPropertyTreeBuilder::UpdateTransform() {
-  if (object_.IsSVGChild()) {
-    UpdateTransformForSVGChild(full_context_.direct_compositing_reasons);
-    return;
-  }
-
+void FragmentPaintPropertyTreeBuilder::UpdateIndividualTransform(
+    bool (*needs_property)(const LayoutObject&, CompositingReasons),
+    void (*compute_matrix)(const ComputedStyle& style,
+                           const PhysicalSize& size,
+                           TransformationMatrix& matrix),
+    CompositingReasons active_animation_reason,
+    CompositingReasons compositing_reasons_for_property,
+    CompositorElementIdNamespace compositor_namespace,
+    bool (ComputedStyle::*running_on_compositor_test)() const,
+    const TransformPaintPropertyNode* (ObjectPaintProperties::*getter)() const,
+    PaintPropertyChangeType (ObjectPaintProperties::*updater)(
+        const TransformPaintPropertyNodeOrAlias&,
+        TransformPaintPropertyNode::State&&,
+        const TransformPaintPropertyNode::AnimationState&),
+    bool (ObjectPaintProperties::*clearer)()) {
+  // TODO(crbug.com/1278452): Merge SVG handling into the primary
+  // codepath (which is this one).
+  DCHECK(!object_.IsSVGChild());
   DCHECK(properties_);
 
   if (NeedsPaintPropertyUpdate()) {
@@ -927,9 +1035,15 @@
     // direct compositing reason. The latter is required because this is the
     // only way to represent compositing both an element and its stacking
     // descendants.
-    if (NeedsTransform(object_, full_context_.direct_compositing_reasons)) {
+    if ((*needs_property)(object_, full_context_.direct_compositing_reasons)) {
       TransformPaintPropertyNode::State state;
 
+      // A few pieces of the code are only for the 'transform' property
+      // and not for the others.
+      bool handling_transform_property =
+          compositor_namespace ==
+          CompositorElementIdNamespace::kPrimaryTransform;
+
       if (object_.IsBox()) {
         auto& box = To<LayoutBox>(object_);
         // Each individual fragment should have its own transform origin, based
@@ -940,19 +1054,15 @@
         else
           size = pre_paint_info_->box_fragment.Size();
         TransformationMatrix matrix;
-        style.ApplyTransform(
-            matrix, size.ToLayoutSize(), ComputedStyle::kExcludeTransformOrigin,
-            ComputedStyle::kIncludeMotionPath,
-            ComputedStyle::kIncludeIndependentTransformProperties);
+        compute_matrix(style, size, matrix);
         // If we are running transform animation on compositor, we should
         // disable 2d translation optimization to ensure that the compositor
         // gets the correct origin (which might be omitted by the optimization)
         // to the compositor, in case later animated values will use the origin.
         // See http://crbug.com/937929 for why we are not using
-        // style.IsRunningTransformAnimationOnCompositor() here.
+        // style.IsRunningTransformAnimationOnCompositor() etc. here.
         bool disable_2d_translation_optimization =
-            full_context_.direct_compositing_reasons &
-            CompositingReason::kActiveTransformAnimation;
+            full_context_.direct_compositing_reasons & active_animation_reason;
         if (!disable_2d_translation_optimization &&
             matrix.IsIdentityOr2DTranslation()) {
           state.transform_and_origin = {matrix.To2DTranslation()};
@@ -978,7 +1088,8 @@
         // does not exist in an existing rendering context, it establishes a
         // new one.
         state.rendering_context_id = context_.rendering_context_id;
-        if (style.Preserves3D() && !state.rendering_context_id) {
+        if (handling_transform_property && style.Preserves3D() &&
+            !state.rendering_context_id) {
           state.rendering_context_id =
               PtrHash<const LayoutObject>::GetHash(&object_);
         }
@@ -993,60 +1104,169 @@
 
       state.direct_compositing_reasons =
           full_context_.direct_compositing_reasons &
-          CompositingReasonsForTransformProperty();
-
-      // If a transform node exists, add an additional direct compositing
-      // reason for 3d transforms and will-change to ensure it is composited.
-      state.direct_compositing_reasons |=
-          (full_context_.direct_compositing_reasons &
-           CompositingReason::kAdditionalCompositingTrigger);
+          compositing_reasons_for_property;
 
       state.flags.flattens_inherited_transform =
           context_.should_flatten_inherited_transform;
-      if (object_.HasHiddenBackface()) {
-        state.backface_visibility =
-            TransformPaintPropertyNode::BackfaceVisibility::kHidden;
-      } else if (state.direct_compositing_reasons != CompositingReason::kNone) {
-        // The above condition fixes a CompositeAfterPaint regression
-        // (crbug.com/1260603) by letting non-directly-composited transforms
-        // inherit parent's backface visibility.
-        // TODO(crbug.com/1261905): Fix the the root cause, and revisit the
-        // above condition and make it at least more web developer friendly.
-        state.backface_visibility =
-            TransformPaintPropertyNode::BackfaceVisibility::kVisible;
+      if (running_on_compositor_test) {
+        state.compositor_element_id =
+            GetCompositorElementId(compositor_namespace);
       }
-      state.compositor_element_id = GetCompositorElementId(
-          CompositorElementIdNamespace::kPrimaryTransform);
+
+      if (handling_transform_property) {
+        // If a transform node exists, add an additional direct compositing
+        // reason for 3d transforms and will-change to ensure it is composited.
+        state.direct_compositing_reasons |=
+            (full_context_.direct_compositing_reasons &
+             CompositingReason::kAdditionalCompositingTrigger);
+
+        if (object_.HasHiddenBackface()) {
+          state.backface_visibility =
+              TransformPaintPropertyNode::BackfaceVisibility::kHidden;
+        } else if (state.direct_compositing_reasons !=
+                   CompositingReason::kNone) {
+          // The above condition fixes a CompositeAfterPaint regression
+          // (crbug.com/1260603) by letting non-directly-composited transforms
+          // inherit parent's backface visibility.
+          // TODO(crbug.com/1261905): Fix the the root cause, and revisit the
+          // above condition and make it at least more web developer friendly.
+          state.backface_visibility =
+              TransformPaintPropertyNode::BackfaceVisibility::kVisible;
+        }
+      }
 
       TransformPaintPropertyNode::AnimationState animation_state;
       animation_state.is_running_animation_on_compositor =
-          style.IsRunningTransformAnimationOnCompositor();
-      auto effective_change_type = properties_->UpdateTransform(
+          running_on_compositor_test && (style.*running_on_compositor_test)();
+      auto effective_change_type = (properties_->*updater)(
           *context_.current.transform, std::move(state), animation_state);
       // We only assume worst-case overlap testing due to animations (see:
       // |PendingLayer::VisualRectForOverlapTesting|) so we can only use the
       // direct transform update (which skips checking for compositing changes)
       // when animations are present.
+      const auto* transform = (properties_->*getter)();
       if (effective_change_type ==
               PaintPropertyChangeType::kChangedOnlySimpleValues &&
-          properties_->Transform()->HasActiveTransformAnimation()) {
+          transform->HasActiveTransformAnimation()) {
         if (auto* paint_artifact_compositor =
                 object_.GetFrameView()->GetPaintArtifactCompositor()) {
-          bool updated = paint_artifact_compositor->DirectlyUpdateTransform(
-              *properties_->Transform());
+          bool updated =
+              paint_artifact_compositor->DirectlyUpdateTransform(*transform);
           if (updated) {
             effective_change_type =
                 PaintPropertyChangeType::kChangedOnlyCompositedValues;
-            properties_->Transform()->CompositorSimpleValuesUpdated();
+            transform->CompositorSimpleValuesUpdated();
           }
         }
       }
       OnUpdateTransform(effective_change_type);
     } else {
-      OnClearTransform(properties_->ClearTransform());
+      OnClearTransform((properties_->*clearer)());
     }
   }
 
+  if (const auto* transform = (properties_->*getter)()) {
+    context_.current.transform = transform;
+    if (!transform->IsIdentityOr2DTranslation() &&
+        !transform->Matrix().Is2dTransform()) {
+      // We need to not flatten from this node through to this element's
+      // transform node.  (If this is the transform node, we'll undo
+      // this in the caller.)
+      context_.should_flatten_inherited_transform = false;
+    }
+  }
+}
+
+void FragmentPaintPropertyTreeBuilder::UpdateTranslate() {
+  UpdateIndividualTransform(
+      &NeedsTranslate,
+      [](const ComputedStyle& style, const PhysicalSize& size,
+         TransformationMatrix& matrix) {
+        if (style.Translate())
+          style.Translate()->Apply(matrix, gfx::SizeF(size));
+      },
+      CompositingReason::kActiveTranslateAnimation,
+      CompositingReason::kDirectReasonsForTranslateProperty,
+      CompositorElementIdNamespace::kTranslateTransform,
+      &ComputedStyle::IsRunningTranslateAnimationOnCompositor,
+      &ObjectPaintProperties::Translate,
+      &ObjectPaintProperties::UpdateTranslate,
+      &ObjectPaintProperties::ClearTranslate);
+}
+
+void FragmentPaintPropertyTreeBuilder::UpdateRotate() {
+  UpdateIndividualTransform(
+      &NeedsRotate,
+      [](const ComputedStyle& style, const PhysicalSize& size,
+         TransformationMatrix& matrix) {
+        if (style.Rotate())
+          style.Rotate()->Apply(matrix, gfx::SizeF(size));
+      },
+      CompositingReason::kActiveRotateAnimation,
+      CompositingReason::kDirectReasonsForRotateProperty,
+      CompositorElementIdNamespace::kRotateTransform,
+      &ComputedStyle::IsRunningRotateAnimationOnCompositor,
+      &ObjectPaintProperties::Rotate, &ObjectPaintProperties::UpdateRotate,
+      &ObjectPaintProperties::ClearRotate);
+}
+
+void FragmentPaintPropertyTreeBuilder::UpdateScale() {
+  UpdateIndividualTransform(
+      &NeedsScale,
+      [](const ComputedStyle& style, const PhysicalSize& size,
+         TransformationMatrix& matrix) {
+        if (style.Scale())
+          style.Scale()->Apply(matrix, gfx::SizeF(size));
+      },
+      CompositingReason::kActiveScaleAnimation,
+      CompositingReason::kDirectReasonsForScaleProperty,
+      CompositorElementIdNamespace::kScaleTransform,
+      &ComputedStyle::IsRunningScaleAnimationOnCompositor,
+      &ObjectPaintProperties::Scale, &ObjectPaintProperties::UpdateScale,
+      &ObjectPaintProperties::ClearScale);
+}
+
+void FragmentPaintPropertyTreeBuilder::UpdateOffset() {
+  UpdateIndividualTransform(
+      &NeedsOffset,
+      [](const ComputedStyle& style, const PhysicalSize& size,
+         TransformationMatrix& matrix) {
+        style.ApplyTransform(
+            matrix, size.ToLayoutSize(),
+            ComputedStyle::kExcludeTransformOperations,
+            ComputedStyle::kExcludeTransformOrigin,
+            ComputedStyle::kIncludeMotionPath,
+            ComputedStyle::kExcludeIndependentTransformProperties);
+      },
+      CompositingReason::kNone, CompositingReason::kNone,
+      // TODO(dbaron): When we support animating offset on the
+      // compositor, we need to use an element ID specific to offset.
+      // This is currently unused.
+      CompositorElementIdNamespace::kPrimary, nullptr,
+      &ObjectPaintProperties::Offset, &ObjectPaintProperties::UpdateOffset,
+      &ObjectPaintProperties::ClearOffset);
+}
+
+void FragmentPaintPropertyTreeBuilder::UpdateTransform() {
+  UpdateIndividualTransform(
+      &NeedsTransform,
+      [](const ComputedStyle& style, const PhysicalSize& size,
+         TransformationMatrix& matrix) {
+        style.ApplyTransform(
+            matrix, size.ToLayoutSize(),
+            ComputedStyle::kIncludeTransformOperations,
+            ComputedStyle::kExcludeTransformOrigin,
+            ComputedStyle::kExcludeMotionPath,
+            ComputedStyle::kExcludeIndependentTransformProperties);
+      },
+      CompositingReason::kActiveTransformAnimation,
+      CompositingReasonsForTransformProperty(),
+      CompositorElementIdNamespace::kPrimaryTransform,
+      &ComputedStyle::IsRunningTransformAnimationOnCompositor,
+      &ObjectPaintProperties::Transform,
+      &ObjectPaintProperties::UpdateTransform,
+      &ObjectPaintProperties::ClearTransform);
+
   // properties_->Transform() is present if a CSS transform is present,
   // and is also present if transform-style: preserve-3d is set.
   // See NeedsTransform.
@@ -2746,7 +2966,16 @@
 
   if (properties_) {
     UpdateStickyTranslation();
-    UpdateTransform();
+    if (object_.IsSVGChild()) {
+      // TODO(crbug.com/1278452): Merge SVG handling into the primary codepath.
+      UpdateTransformForSVGChild(full_context_.direct_compositing_reasons);
+    } else {
+      UpdateTranslate();
+      UpdateRotate();
+      UpdateScale();
+      UpdateOffset();
+      UpdateTransform();
+    }
     UpdateSharedElementTransitionEffect();
     UpdateClipPathClip();
     UpdateEffect();
@@ -3645,6 +3874,10 @@
       (NeedsPaintOffsetTranslation(object_,
                                    context_.direct_compositing_reasons) ||
        NeedsStickyTranslation(object_) ||
+       NeedsTranslate(object_, context_.direct_compositing_reasons) ||
+       NeedsRotate(object_, context_.direct_compositing_reasons) ||
+       NeedsScale(object_, context_.direct_compositing_reasons) ||
+       NeedsOffset(object_, context_.direct_compositing_reasons) ||
        NeedsTransform(object_, context_.direct_compositing_reasons) ||
        NeedsEffectIgnoringClipPath(object_,
                                    context_.direct_compositing_reasons) ||
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 12debec..75668db 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -4989,23 +4989,18 @@
   )HTML");
 
   auto* motion_path = GetLayoutObjectByElementId("motionPath");
-  EXPECT_EQ(gfx::Vector2dF(50, 150), motion_path->FirstFragment()
-                                         .PaintProperties()
-                                         ->Transform()
-                                         ->Translation2D());
+  auto* motion_path_properties = motion_path->FirstFragment().PaintProperties();
+  EXPECT_EQ(motion_path_properties->Transform(), nullptr);
+  EXPECT_EQ(gfx::Vector2dF(50, 150),
+            motion_path_properties->Offset()->Translation2D());
   // We don't need to store origin for 2d-translation.
-  EXPECT_EQ(
-      gfx::Point3F(),
-      motion_path->FirstFragment().PaintProperties()->Transform()->Origin());
+  EXPECT_EQ(gfx::Point3F(), motion_path_properties->Offset()->Origin());
 
   auto* will_change = GetLayoutObjectByElementId("willChange");
-  EXPECT_TRUE(will_change->FirstFragment()
-                  .PaintProperties()
-                  ->Transform()
-                  ->IsIdentity());
-  EXPECT_EQ(
-      gfx::Point3F(),
-      will_change->FirstFragment().PaintProperties()->Transform()->Origin());
+  auto* will_change_properties = will_change->FirstFragment().PaintProperties();
+  EXPECT_EQ(will_change_properties->Offset(), nullptr);
+  EXPECT_TRUE(will_change_properties->Transform()->IsIdentity());
+  EXPECT_EQ(gfx::Point3F(), will_change_properties->Transform()->Origin());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, ChangePositionUpdateDescendantProperties) {
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc b/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
index ad6b4d1..0cfbf8d 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
@@ -84,6 +84,10 @@
       PropertyTreePrinter<TransformPaintPropertyNodeOrAlias>& printer) {
     printer.AddNode(properties.PaintOffsetTranslation());
     printer.AddNode(properties.StickyTranslation());
+    printer.AddNode(properties.Translate());
+    printer.AddNode(properties.Rotate());
+    printer.AddNode(properties.Scale());
+    printer.AddNode(properties.Offset());
     printer.AddNode(properties.Transform());
     printer.AddNode(properties.Perspective());
     printer.AddNode(properties.ReplacedContentTransform());
@@ -227,6 +231,10 @@
   SetDebugName(properties.PaintOffsetTranslation(), "PaintOffsetTranslation",
                object);
   SetDebugName(properties.StickyTranslation(), "StickyTranslation", object);
+  SetDebugName(properties.Translate(), "Translate", object);
+  SetDebugName(properties.Rotate(), "Rotate", object);
+  SetDebugName(properties.Scale(), "Scale", object);
+  SetDebugName(properties.Offset(), "Offset", object);
   SetDebugName(properties.Transform(), "Transform", object);
   SetDebugName(properties.Perspective(), "Perspective", object);
   SetDebugName(properties.ReplacedContentTransform(),
diff --git a/third_party/blink/renderer/core/style/basic_shapes.cc b/third_party/blink/renderer/core/style/basic_shapes.cc
index 6f6856f..df6c37b 100644
--- a/third_party/blink/renderer/core/style/basic_shapes.cc
+++ b/third_party/blink/renderer/core/style/basic_shapes.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/style/basic_shapes.h"
 
 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
+#include "third_party/blink/renderer/platform/geometry/length.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
 #include "third_party/blink/renderer/platform/graphics/path.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -137,9 +138,19 @@
   return wind_rule_ == other.wind_rule_ && values_ == other.values_;
 }
 
-void BasicShapeRectCommon::GetPath(Path& path,
-                                   const gfx::RectF& bounding_box,
-                                   float) {
+bool BasicShapeRectCommon::IsEqualAssumingSameType(const BasicShape& o) const {
+  const BasicShapeRectCommon& other = To<BasicShapeRectCommon>(o);
+  return right_ == other.right_ && top_ == other.top_ &&
+         bottom_ == other.bottom_ && left_ == other.left_ &&
+         top_left_radius_ == other.top_left_radius_ &&
+         top_right_radius_ == other.top_right_radius_ &&
+         bottom_right_radius_ == other.bottom_right_radius_ &&
+         bottom_left_radius_ == other.bottom_left_radius_;
+}
+
+void BasicShapeInset::GetPath(Path& path,
+                              const gfx::RectF& bounding_box,
+                              float) {
   DCHECK(path.IsEmpty());
   float left = FloatValueForLength(left_, bounding_box.width());
   float top = FloatValueForLength(top_, bounding_box.height());
@@ -163,14 +174,49 @@
   path.AddRoundedRect(final_rect);
 }
 
-bool BasicShapeRectCommon::IsEqualAssumingSameType(const BasicShape& o) const {
-  const BasicShapeRectCommon& other = To<BasicShapeRectCommon>(o);
-  return right_ == other.right_ && top_ == other.top_ &&
-         bottom_ == other.bottom_ && left_ == other.left_ &&
-         top_left_radius_ == other.top_left_radius_ &&
-         top_right_radius_ == other.top_right_radius_ &&
-         bottom_right_radius_ == other.bottom_right_radius_ &&
-         bottom_left_radius_ == other.bottom_left_radius_;
+void BasicShapeRect::GetPath(Path& path,
+                             const gfx::RectF& bounding_box,
+                             float) {
+  DCHECK(path.IsEmpty());
+
+  // The following computes |left|, |right| as the inset from the left edge
+  // of the bounding box and |top|, |bottom| as the inset from the top edge of
+  // the bounding box.
+  // auto aligns the inset with the corresponding edge. So |left| and |top|
+  // align with the left and top edge of the bounding box and compute
+  // equivalent to 0%. And |right| and |bottom| align with the right and bottom
+  // edge of the bounding box and compute equivalent to 100%.
+  float left = left_.GetType() == Length::kAuto
+                   ? 0
+                   : FloatValueForLength(left_, bounding_box.width());
+
+  float top = top_.GetType() == Length::kAuto
+                  ? 0
+                  : FloatValueForLength(top_, bounding_box.height());
+
+  float right = right_.GetType() == Length::kAuto
+                    ? bounding_box.width()
+                    : FloatValueForLength(right_, bounding_box.width());
+  right = std::max(right, left);
+
+  float bottom = bottom_.GetType() == Length::kAuto
+                     ? bounding_box.height()
+                     : FloatValueForLength(bottom_, bounding_box.height());
+  bottom = std::max(top, bottom);
+
+  gfx::RectF rect(left + bounding_box.x(), top + bounding_box.y(), right - left,
+                  bottom - top);
+
+  gfx::SizeF box_size = bounding_box.size();
+  auto radii = FloatRoundedRect::Radii(
+      SizeForLengthSize(top_left_radius_, box_size),
+      SizeForLengthSize(top_right_radius_, box_size),
+      SizeForLengthSize(bottom_left_radius_, box_size),
+      SizeForLengthSize(bottom_right_radius_, box_size));
+
+  FloatRoundedRect final_rect(rect, radii);
+  final_rect.ConstrainRadii();
+  path.AddRoundedRect(final_rect);
 }
 
 void BasicShapeXYWH::GetPath(Path& path,
diff --git a/third_party/blink/renderer/core/style/basic_shapes.h b/third_party/blink/renderer/core/style/basic_shapes.h
index 8d41dc2..eb80c2f 100644
--- a/third_party/blink/renderer/core/style/basic_shapes.h
+++ b/third_party/blink/renderer/core/style/basic_shapes.h
@@ -283,15 +283,12 @@
     bottom_left_radius_ = radius;
   }
 
-  void GetPath(Path&, const gfx::RectF&, float) override;
-
  protected:
   BasicShapeRectCommon() = default;
 
  protected:
   bool IsEqualAssumingSameType(const BasicShape&) const override;
 
- private:
   Length right_;
   Length top_;
   Length bottom_;
@@ -310,6 +307,7 @@
   }
 
   ShapeType GetType() const override { return kBasicShapeInsetType; }
+  void GetPath(Path&, const gfx::RectF&, float) override;
 };
 
 template <>
@@ -326,6 +324,7 @@
   }
 
   ShapeType GetType() const override { return kBasicShapeRectType; }
+  void GetPath(Path&, const gfx::RectF&, float) override;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index d02ef6f..ddcfdd0 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -1084,6 +1084,9 @@
     diff.SetBlendModeChanged();
 
   if (HasCurrentTransformAnimation() != other.HasCurrentTransformAnimation() ||
+      HasCurrentScaleAnimation() != other.HasCurrentScaleAnimation() ||
+      HasCurrentRotateAnimation() != other.HasCurrentRotateAnimation() ||
+      HasCurrentTranslateAnimation() != other.HasCurrentTranslateAnimation() ||
       HasCurrentOpacityAnimation() != other.HasCurrentOpacityAnimation() ||
       HasCurrentFilterAnimation() != other.HasCurrentFilterAnimation() ||
       HasCurrentBackdropFilterAnimation() !=
@@ -1231,6 +1234,18 @@
   switch (ResolveCSSPropertyID(property)) {
     case CSSPropertyID::kTransform:
     case CSSPropertyID::kPerspective:
+    case CSSPropertyID::kTransformStyle:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+static bool IsWillChangeHintForAnyTransformProperty(CSSPropertyID property) {
+  switch (ResolveCSSPropertyID(property)) {
+    case CSSPropertyID::kTransform:
+    case CSSPropertyID::kPerspective:
     case CSSPropertyID::kTranslate:
     case CSSPropertyID::kScale:
     case CSSPropertyID::kRotate:
@@ -1245,7 +1260,7 @@
 }
 
 static bool IsWillChangeCompositingHintProperty(CSSPropertyID property) {
-  if (IsWillChangeTransformHintProperty(property))
+  if (IsWillChangeHintForAnyTransformProperty(property))
     return true;
   switch (ResolveCSSPropertyID(property)) {
     case CSSPropertyID::kOpacity:
@@ -1274,6 +1289,12 @@
                      IsWillChangeTransformHintProperty);
 }
 
+bool ComputedStyle::HasWillChangeHintForAnyTransformProperty() const {
+  const auto& properties = WillChangeProperties();
+  return std::any_of(properties.begin(), properties.end(),
+                     IsWillChangeHintForAnyTransformProperty);
+}
+
 bool ComputedStyle::RequireTransformOrigin(
     ApplyTransformOrigin apply_origin,
     ApplyMotionPath apply_motion_path) const {
@@ -1324,17 +1345,20 @@
 void ComputedStyle::ApplyTransform(
     TransformationMatrix& result,
     const LayoutSize& border_box_size,
+    ApplyTransformOperations apply_operations,
     ApplyTransformOrigin apply_origin,
     ApplyMotionPath apply_motion_path,
     ApplyIndependentTransformProperties apply_independent_transform_properties)
     const {
-  ApplyTransform(result, gfx::RectF(gfx::SizeF(border_box_size)), apply_origin,
-                 apply_motion_path, apply_independent_transform_properties);
+  ApplyTransform(result, gfx::RectF(gfx::SizeF(border_box_size)),
+                 apply_operations, apply_origin, apply_motion_path,
+                 apply_independent_transform_properties);
 }
 
 void ComputedStyle::ApplyTransform(
     TransformationMatrix& result,
     const gfx::RectF& bounding_box,
+    ApplyTransformOperations apply_operations,
     ApplyTransformOrigin apply_origin,
     ApplyMotionPath apply_motion_path,
     ApplyIndependentTransformProperties apply_independent_transform_properties)
@@ -1377,8 +1401,10 @@
   if (apply_motion_path == kIncludeMotionPath)
     ApplyMotionPathTransform(origin_x, origin_y, bounding_box, result);
 
-  for (const auto& operation : Transform().Operations())
-    operation->Apply(result, box_size);
+  if (apply_operations == kIncludeTransformOperations) {
+    for (const auto& operation : Transform().Operations())
+      operation->Apply(result, box_size);
+  }
 
   if (apply_transform_origin) {
     result.Translate3d(-origin_x, -origin_y, -origin_z);
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 872aea1..97a2863f 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1455,7 +1455,27 @@
     return WillChangeProperties().Contains(CSSPropertyID::kOpacity) ||
            WillChangeProperties().Contains(CSSPropertyID::kAliasWebkitOpacity);
   }
+  // Do we have a will-change hint for transform, perspective, or
+  // transform-style?
+  // TODO(dbaron): It's not clear that perspective and transform-style belong
+  // here any more than they belong for scale, rotate, translate, or offset-*.
   bool HasWillChangeTransformHint() const;
+  bool HasWillChangeScaleHint() const {
+    return WillChangeProperties().Contains(CSSPropertyID::kScale);
+  }
+  bool HasWillChangeRotateHint() const {
+    return WillChangeProperties().Contains(CSSPropertyID::kRotate);
+  }
+  bool HasWillChangeTranslateHint() const {
+    return WillChangeProperties().Contains(CSSPropertyID::kTranslate);
+  }
+  bool HasWillChangeOffsetHint() const {
+    return WillChangeProperties().Contains(CSSPropertyID::kOffsetPath) ||
+           WillChangeProperties().Contains(CSSPropertyID::kOffsetPosition);
+  }
+  // The union of the above five functions (but faster).
+  bool HasWillChangeHintForAnyTransformProperty() const;
+
   bool HasWillChangeFilterHint() const {
     return WillChangeProperties().Contains(CSSPropertyID::kFilter) ||
            WillChangeProperties().Contains(CSSPropertyID::kAliasWebkitFilter);
@@ -2335,17 +2355,24 @@
   // Animation utility functions.
   bool HasCurrentCompositableAnimation() const {
     return HasCurrentOpacityAnimation() || HasCurrentTransformAnimation() ||
-           HasCurrentFilterAnimation() || HasCurrentBackdropFilterAnimation() ||
+           HasCurrentScaleAnimation() || HasCurrentRotateAnimation() ||
+           HasCurrentTranslateAnimation() || HasCurrentFilterAnimation() ||
+           HasCurrentBackdropFilterAnimation() ||
            (RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled() &&
             HasCurrentBackgroundColorAnimation());
   }
   bool ShouldCompositeForCurrentAnimations() const {
     return HasCurrentOpacityAnimation() || HasCurrentTransformAnimation() ||
-           HasCurrentFilterAnimation() || HasCurrentBackdropFilterAnimation();
+           HasCurrentScaleAnimation() || HasCurrentRotateAnimation() ||
+           HasCurrentTranslateAnimation() || HasCurrentFilterAnimation() ||
+           HasCurrentBackdropFilterAnimation();
   }
   bool RequiresPropertyNodeForAnimation() const {
     return IsRunningOpacityAnimationOnCompositor() ||
            IsRunningTransformAnimationOnCompositor() ||
+           IsRunningScaleAnimationOnCompositor() ||
+           IsRunningRotateAnimationOnCompositor() ||
+           IsRunningTranslateAnimationOnCompositor() ||
            IsRunningFilterAnimationOnCompositor() ||
            IsRunningBackdropFilterAnimationOnCompositor();
   }
@@ -2378,21 +2405,11 @@
            (Rotate() && (Rotate()->X() != 0 || Rotate()->Y() != 0)) ||
            (Scale() && Scale()->Z() != 1);
   }
-  // Returns true if the computed style contains a 3D transform operation with a
-  // non-trivial component in the Z axis. This can be individual operations from
-  // the transform property, or individual values from translate/rotate/scale
-  // properties. Perspective is omitted since it does not, by itself, specify a
-  // 3D transform.
-  bool HasNonTrivial3DTransformOperation() const {
-    return Transform().HasNonTrivial3DComponent() ||
-           (Translate() && Translate()->Z() != 0) ||
-           (Rotate() && Rotate()->Angle() != 0 &&
-            (Rotate()->X() != 0 || Rotate()->Y() != 0)) ||
-           (Scale() && Scale()->Z() != 1);
-  }
   bool HasTransform() const {
     return HasTransformOperations() || HasOffset() ||
-           HasCurrentTransformAnimation() || Translate() || Rotate() || Scale();
+           HasCurrentTransformAnimation() || HasCurrentScaleAnimation() ||
+           HasCurrentRotateAnimation() || HasCurrentTranslateAnimation() ||
+           Translate() || Rotate() || Scale();
   }
   bool HasTransformOperations() const {
     return !Transform().Operations().IsEmpty();
@@ -2422,13 +2439,19 @@
     kIncludeIndependentTransformProperties,
     kExcludeIndependentTransformProperties
   };
+  enum ApplyTransformOperations {
+    kIncludeTransformOperations,
+    kExcludeTransformOperations
+  };
   void ApplyTransform(TransformationMatrix&,
                       const LayoutSize& border_box_data_size,
+                      ApplyTransformOperations,
                       ApplyTransformOrigin,
                       ApplyMotionPath,
                       ApplyIndependentTransformProperties) const;
   void ApplyTransform(TransformationMatrix&,
                       const gfx::RectF& bounding_box,
+                      ApplyTransformOperations,
                       ApplyTransformOrigin,
                       ApplyMotionPath,
                       ApplyIndependentTransformProperties) const;
@@ -2514,10 +2537,10 @@
   // position descendants.
   bool HasTransformRelatedProperty() const {
     return HasTransform() || Preserves3D() || HasPerspective() ||
-           HasWillChangeTransformHint();
+           HasWillChangeHintForAnyTransformProperty();
   }
   bool HasTransformRelatedPropertyForSVG() const {
-    return HasTransform() || HasWillChangeTransformHint();
+    return HasTransform() || HasWillChangeHintForAnyTransformProperty();
   }
 
   // Return true if this style has properties ('filter', 'clip-path' and 'mask')
@@ -3145,7 +3168,16 @@
       UpdatePropertySpecificDifferencesRespectsTransformAnimation);
   FRIEND_TEST_ALL_PREFIXES(
       ComputedStyleTest,
-      UpdatePropertySpecificDifferencesCompositingReasonsTransforom);
+      UpdatePropertySpecificDifferencesCompositingReasonsTransform);
+  FRIEND_TEST_ALL_PREFIXES(
+      ComputedStyleTest,
+      UpdatePropertySpecificDifferencesRespectsScaleAnimation);
+  FRIEND_TEST_ALL_PREFIXES(
+      ComputedStyleTest,
+      UpdatePropertySpecificDifferencesRespectsRotateAnimation);
+  FRIEND_TEST_ALL_PREFIXES(
+      ComputedStyleTest,
+      UpdatePropertySpecificDifferencesRespectsTranslateAnimation);
   FRIEND_TEST_ALL_PREFIXES(
       ComputedStyleTest,
       UpdatePropertySpecificDifferencesCompositingReasonsOpacity);
diff --git a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5 b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
index da2e273..e2c0cfc 100644
--- a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
+++ b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
@@ -374,8 +374,9 @@
             // from just the matrix, including but not limited to animation state.
             method: "HasTransform()",
             field_dependencies: ["transform", "offset-position",
-                    "HasCurrentTransformAnimation", "translate", "rotate",
-                    "scale"]
+                    "HasCurrentTranslateAnimation", "HasCurrentRotateAnimation",
+                    "HasCurrentScaleAnimation", "HasCurrentTransformAnimation",
+                    "translate", "rotate", "scale"]
           },
         ]
     },
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 4de8821..2a5504a 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -656,6 +656,27 @@
       default_value: "false",
     },
     {
+      name: "HasCurrentTranslateAnimation",
+      field_template: "primitive",
+      type_name: "bool",
+      field_group: "*",
+      default_value: "false",
+    },
+    {
+      name: "HasCurrentRotateAnimation",
+      field_template: "primitive",
+      type_name: "bool",
+      field_group: "*",
+      default_value: "false",
+    },
+    {
+      name: "HasCurrentScaleAnimation",
+      field_template: "primitive",
+      type_name: "bool",
+      field_group: "*",
+      default_value: "false",
+    },
+    {
       name: "HasCurrentTransformAnimation",
       field_template: "primitive",
       type_name: "bool",
@@ -718,6 +739,36 @@
       default_value: "false",
     },
     {
+      name: "IsRunningScaleAnimationOnCompositor",
+      field_template: "primitive",
+      type_name: "bool",
+      field_group: "*",
+      // This field just affects how changes of animatable values are handled,
+      // so it doesn't contribute to style differences.
+      custom_compare: true,
+      default_value: "false",
+    },
+    {
+      name: "IsRunningRotateAnimationOnCompositor",
+      field_template: "primitive",
+      type_name: "bool",
+      field_group: "*",
+      // This field just affects how changes of animatable values are handled,
+      // so it doesn't contribute to style differences.
+      custom_compare: true,
+      default_value: "false",
+    },
+    {
+      name: "IsRunningTranslateAnimationOnCompositor",
+      field_template: "primitive",
+      type_name: "bool",
+      field_group: "*",
+      // This field just affects how changes of animatable values are handled,
+      // so it doesn't contribute to style differences.
+      custom_compare: true,
+      default_value: "false",
+    },
+    {
       name: "IsRunningFilterAnimationOnCompositor",
       field_template: "primitive",
       type_name: "bool",
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index 3f30b31..71cc1ed 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -141,7 +141,7 @@
 }
 
 TEST_F(ComputedStyleTest,
-       UpdatePropertySpecificDifferencesCompositingReasonsTransforom) {
+       UpdatePropertySpecificDifferencesCompositingReasonsTransform) {
   scoped_refptr<ComputedStyle> style = CreateComputedStyle();
   scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
 
@@ -162,6 +162,36 @@
 }
 
 TEST_F(ComputedStyleTest,
+       UpdatePropertySpecificDifferencesRespectsScaleAnimation) {
+  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
+  other->SetHasCurrentScaleAnimation(true);
+  StyleDifference diff;
+  style->UpdatePropertySpecificDifferences(*other, diff);
+  EXPECT_TRUE(diff.TransformChanged());
+}
+
+TEST_F(ComputedStyleTest,
+       UpdatePropertySpecificDifferencesRespectsRotateAnimation) {
+  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
+  other->SetHasCurrentRotateAnimation(true);
+  StyleDifference diff;
+  style->UpdatePropertySpecificDifferences(*other, diff);
+  EXPECT_TRUE(diff.TransformChanged());
+}
+
+TEST_F(ComputedStyleTest,
+       UpdatePropertySpecificDifferencesRespectsTranslateAnimation) {
+  scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+  scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
+  other->SetHasCurrentTranslateAnimation(true);
+  StyleDifference diff;
+  style->UpdatePropertySpecificDifferences(*other, diff);
+  EXPECT_TRUE(diff.TransformChanged());
+}
+
+TEST_F(ComputedStyleTest,
        UpdatePropertySpecificDifferencesCompositingReasonsOpacity) {
   scoped_refptr<ComputedStyle> style = CreateComputedStyle();
   scoped_refptr<ComputedStyle> other = ComputedStyle::Clone(*style);
@@ -429,11 +459,17 @@
 TEST_F(ComputedStyleTest, AnimationFlags) {
   Persistent<Document> document = Document::CreateForTest();
   TEST_ANIMATION_FLAG(HasCurrentTransformAnimation, kNonInherited);
+  TEST_ANIMATION_FLAG(HasCurrentScaleAnimation, kNonInherited);
+  TEST_ANIMATION_FLAG(HasCurrentRotateAnimation, kNonInherited);
+  TEST_ANIMATION_FLAG(HasCurrentTranslateAnimation, kNonInherited);
   TEST_ANIMATION_FLAG(HasCurrentOpacityAnimation, kNonInherited);
   TEST_ANIMATION_FLAG(HasCurrentFilterAnimation, kNonInherited);
   TEST_ANIMATION_FLAG(HasCurrentBackdropFilterAnimation, kNonInherited);
   TEST_ANIMATION_FLAG(SubtreeWillChangeContents, kInherited);
   TEST_ANIMATION_FLAG_NO_DIFF(IsRunningTransformAnimationOnCompositor);
+  TEST_ANIMATION_FLAG_NO_DIFF(IsRunningScaleAnimationOnCompositor);
+  TEST_ANIMATION_FLAG_NO_DIFF(IsRunningRotateAnimationOnCompositor);
+  TEST_ANIMATION_FLAG_NO_DIFF(IsRunningTranslateAnimationOnCompositor);
   TEST_ANIMATION_FLAG_NO_DIFF(IsRunningOpacityAnimationOnCompositor);
   TEST_ANIMATION_FLAG_NO_DIFF(IsRunningFilterAnimationOnCompositor);
   TEST_ANIMATION_FLAG_NO_DIFF(IsRunningBackdropFilterAnimationOnCompositor);
diff --git a/third_party/blink/renderer/core/timing/performance_user_timing.cc b/third_party/blink/renderer/core/timing/performance_user_timing.cc
index 2aab859..6b396f6 100644
--- a/third_party/blink/renderer/core/timing/performance_user_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_user_timing.cc
@@ -25,6 +25,7 @@
 
 #include "third_party/blink/renderer/core/timing/performance_user_timing.h"
 
+#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-shared.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_performance_mark_options.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_double_string.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -141,6 +142,11 @@
     return 0.0;
   }
 
+  // Count the usage of PerformanceTiming attribute names in performance
+  // measure. See crbug.com/1318445.
+  blink::UseCounter::Count(performance_->GetExecutionContext(),
+                           WebFeature::kPerformanceMeasureFindExistingName);
+
   return value - timing->navigationStart();
 }
 
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 ef2c0453..b9a4256 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -36,6 +36,7 @@
 
 #include "base/auto_reset.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/web_keyboard_event.h"
 #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-blink.h"
 #include "third_party/blink/public/strings/grit/blink_strings.h"
@@ -4586,16 +4587,21 @@
     return true;
   }
 
-  // If the object is not natively focusable but can be focused using an ARIA
-  // active descendant, perform a native click instead. This will enable Web
-  // apps that set accessibility focus using an active descendant to capture and
-  // act on the click event. Otherwise, there is no other way to inform the app
-  // that an AT has requested the focus to be changed, except if the app is
-  // using AOM. To be extra safe, exclude objects that are clickable themselves.
-  // This won't prevent anyone from having a click handler on the object's
-  // container.
-  if (!IsClickable() && CanBeActiveDescendant()) {
-    return OnNativeClickAction();
+  if (base::FeatureList::IsEnabled(blink::features::kSimulateClickOnAXFocus)) {
+    // If the object is not natively focusable but can be focused using an ARIA
+    // active descendant, perform a native click instead. This will enable Web
+    // apps that set accessibility focus using an active descendant to capture
+    // and act on the click event. Otherwise, there is no other way to inform
+    // the app that an AT has requested the focus to be changed, except if the
+    // app is using AOM. To be extra safe, exclude objects that are clickable
+    // themselves. This won't prevent anyone from having a click handler on the
+    // object's container.
+    //
+    // This code is in the process of being removed. See the comment above
+    // |kSimulateClickOnAXFocus| in `blink/common/features.cc`.
+    if (!IsClickable() && CanBeActiveDescendant()) {
+      return OnNativeClickAction();
+    }
   }
 
   element->Focus();
diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.h b/third_party/blink/renderer/modules/manifest/manifest_parser.h
index 3def05ff..f59fdaa8 100644
--- a/third_party/blink/renderer/modules/manifest/manifest_parser.h
+++ b/third_party/blink/renderer/modules/manifest/manifest_parser.h
@@ -469,7 +469,7 @@
       const String& preference);
 
   // Parse the 'user_preferences' field of the manifest as defined in:
-  // https://github.com/w3c/manifest/issues/975#issuecomment-960222756
+  // https://github.com/WICG/manifest-incubations/blob/gh-pages/user-preferences-explainer.md
   // Returns nullptr if parsing fails.
   mojom::blink::ManifestUserPreferencesPtr ParseUserPreferences(
       const JSONObject* object);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
index 6108b1e..caab5d1 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -286,8 +286,7 @@
     }
   }
 
-  auto context_provider = dawn_control_client_->GetContextProviderWeakPtr();
-  DCHECK(context_provider);
+  DCHECK_NE(dawn_control_client_, nullptr);
 
   WGPURequestAdapterOptions dawn_options = AsDawnType(options);
   auto* callback =
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.cc b/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
index 79d8343..349b5f3 100644
--- a/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
@@ -19,6 +19,9 @@
 
 constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
     {CompositingReason::k3DTransform, "transform3D", "Has a 3d transform"},
+    {CompositingReason::k3DScale, "scale3D", "Has a 3d scale"},
+    {CompositingReason::k3DRotate, "rotate3D", "Has a 3d rotate"},
+    {CompositingReason::k3DTranslate, "translate3D", "Has a 3d translate"},
     {CompositingReason::kTrivial3DTransform, "trivialTransform3D",
      "Has a trivial 3d transform"},
     {CompositingReason::kVideo, "video", "Is an accelerated video"},
@@ -31,6 +34,12 @@
      "Has backface-visibility: hidden"},
     {CompositingReason::kActiveTransformAnimation, "activeTransformAnimation",
      "Has an active accelerated transform animation or transition"},
+    {CompositingReason::kActiveScaleAnimation, "activeScaleAnimation",
+     "Has an active accelerated scale animation or transition"},
+    {CompositingReason::kActiveRotateAnimation, "activeRotateAnimation",
+     "Has an active accelerated rotate animation or transition"},
+    {CompositingReason::kActiveTranslateAnimation, "activeTranslateAnimation",
+     "Has an active accelerated translate animation or transition"},
     {CompositingReason::kActiveOpacityAnimation, "activeOpacityAnimation",
      "Has an active accelerated opacity animation or transition"},
     {CompositingReason::kActiveFilterAnimation, "activeFilterAnimation",
@@ -45,6 +54,12 @@
      "Is a scrollable overflow element"},
     {CompositingReason::kWillChangeTransform, "willChangeTransform",
      "Has a will-change: transform compositing hint"},
+    {CompositingReason::kWillChangeScale, "willChangeScale",
+     "Has a will-change: scale compositing hint"},
+    {CompositingReason::kWillChangeRotate, "willChangeRotate",
+     "Has a will-change: rotate compositing hint"},
+    {CompositingReason::kWillChangeTranslate, "willChangeTranslate",
+     "Has a will-change: translate compositing hint"},
     {CompositingReason::kWillChangeOpacity, "willChangeOpacity",
      "Has a will-change: opacity compositing hint"},
     {CompositingReason::kWillChangeFilter, "willChangeFilter",
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.h b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
index 5c5d19d..0a0b170c 100644
--- a/third_party/blink/renderer/platform/graphics/compositing_reasons.h
+++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
@@ -18,6 +18,9 @@
 #define FOR_EACH_COMPOSITING_REASON(V)                                        \
   /* Intrinsic reasons that can be known right away by the layer. */          \
   V(3DTransform)                                                              \
+  V(3DScale)                                                                  \
+  V(3DRotate)                                                                 \
+  V(3DTranslate)                                                              \
   V(Trivial3DTransform)                                                       \
   V(Video)                                                                    \
   V(Canvas)                                                                   \
@@ -26,6 +29,9 @@
   V(DocumentTransitionPseudoElement)                                          \
   V(BackfaceVisibilityHidden)                                                 \
   V(ActiveTransformAnimation)                                                 \
+  V(ActiveScaleAnimation)                                                     \
+  V(ActiveRotateAnimation)                                                    \
+  V(ActiveTranslateAnimation)                                                 \
   V(ActiveOpacityAnimation)                                                   \
   V(ActiveFilterAnimation)                                                    \
   V(ActiveBackdropFilterAnimation)                                            \
@@ -35,6 +41,9 @@
   V(StickyPosition)                                                           \
   V(OverflowScrolling)                                                        \
   V(WillChangeTransform)                                                      \
+  V(WillChangeScale)                                                          \
+  V(WillChangeRotate)                                                         \
+  V(WillChangeTranslate)                                                      \
   V(WillChangeOpacity)                                                        \
   V(WillChangeFilter)                                                         \
   V(WillChangeBackdropFilter)                                                 \
@@ -110,14 +119,24 @@
     // Various combinations of compositing reasons are defined here also, for
     // more intuitive and faster bitwise logic.
     kComboScrollDependentPosition = kFixedPosition | kStickyPosition,
-    kPreventingSubpixelAccumulationReasons = kWillChangeTransform,
+    // Note that translate is not included, because we care about transforms
+    // that are not IsIdentityOrTranslation().
+    kPreventingSubpixelAccumulationReasons =
+        kWillChangeTransform | kWillChangeScale | kWillChangeRotate,
     kDirectReasonsForPaintOffsetTranslationProperty =
         kComboScrollDependentPosition | kAffectedByOuterViewportBoundsDelta |
         kFixedToViewport | kVideo | kCanvas | kPlugin | kIFrame,
+    // TODO(dbaron): kWillChangeOther probably shouldn't be in this list.
     kDirectReasonsForTransformProperty =
         k3DTransform | kTrivial3DTransform | kWillChangeTransform |
         kWillChangeOther | kPerspectiveWith3DDescendants |
         kPreserve3DWith3DDescendants | kActiveTransformAnimation,
+    kDirectReasonsForScaleProperty =
+        k3DScale | kWillChangeScale | kActiveScaleAnimation,
+    kDirectReasonsForRotateProperty =
+        k3DRotate | kWillChangeRotate | kActiveRotateAnimation,
+    kDirectReasonsForTranslateProperty =
+        k3DTranslate | kWillChangeTranslate | kActiveTranslateAnimation,
     kDirectReasonsForScrollTranslationProperty =
         kRootScroller | kOverflowScrolling,
     kDirectReasonsForEffectProperty =
@@ -135,10 +154,21 @@
     // This is because 3D transforms and incorrect use of will-change
     // are likely indicators that compositing is expected because
     // certain changes will be made.
+    // Note that kWillChangeScale, kWillChangeRotate, and
+    // kWillChangeTranslate are not included since there is no
+    // web-compatibility reason to include them.
     kAdditionalCompositingTrigger =
         k3DTransform | kTrivial3DTransform | kWillChangeTransform |
         kWillChangeOpacity | kWillChangeBackdropFilter | kWillChangeFilter,
 
+    // Cull rect expansion is required if the compositing reasons hint
+    // requirement of high-performance movement, to avoid frequent change of
+    // cull rect.
+    kRequiresCullRectExpansion =
+        kDirectReasonsForTransformProperty | kDirectReasonsForScaleProperty |
+        kDirectReasonsForRotateProperty | kDirectReasonsForTranslateProperty |
+        kDirectReasonsForScrollTranslationProperty,
+
   };
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/compositor_element_id.h b/third_party/blink/renderer/platform/graphics/compositor_element_id.h
index 534f912..09f12ee 100644
--- a/third_party/blink/renderer/platform/graphics/compositor_element_id.h
+++ b/third_party/blink/renderer/platform/graphics/compositor_element_id.h
@@ -23,6 +23,9 @@
   kEffectFilter,
   kEffectMask,
   kEffectClipPath,
+  kScaleTransform,
+  kRotateTransform,
+  kTranslateTransform,
   kVerticalScrollbar,
   kHorizontalScrollbar,
   kSharedElementTransition,
diff --git a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
index 6c4c135..dac8c25c 100644
--- a/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -333,13 +333,18 @@
   }
 
   bool HasDirectCompositingReasonsOtherThan3dTransform() const {
-    return DirectCompositingReasons() & ~CompositingReason::k3DTransform &
-           ~CompositingReason::kTrivial3DTransform;
+    return DirectCompositingReasons() &
+           ~(CompositingReason::k3DTransform | CompositingReason::k3DScale |
+             CompositingReason::k3DRotate | CompositingReason::k3DTranslate |
+             CompositingReason::kTrivial3DTransform);
   }
 
   bool HasActiveTransformAnimation() const {
     return state_.direct_compositing_reasons &
-           CompositingReason::kActiveTransformAnimation;
+           (CompositingReason::kActiveTransformAnimation |
+            CompositingReason::kActiveScaleAnimation |
+            CompositingReason::kActiveRotateAnimation |
+            CompositingReason::kActiveTranslateAnimation);
   }
 
   bool RequiresCompositingForFixedPosition() const {
@@ -369,15 +374,17 @@
 
   bool RequiresCompositingForWillChangeTransform() const {
     return state_.direct_compositing_reasons &
-           CompositingReason::kWillChangeTransform;
+           (CompositingReason::kWillChangeTransform |
+            CompositingReason::kWillChangeScale |
+            CompositingReason::kWillChangeRotate |
+            CompositingReason::kWillChangeTranslate);
   }
 
   // Cull rect expansion is required if the compositing reasons hint requirement
   // of high-performance movement, to avoid frequent change of cull rect.
   bool RequiresCullRectExpansion() const {
     return state_.direct_compositing_reasons &
-           (CompositingReason::kDirectReasonsForTransformProperty |
-            CompositingReason::kDirectReasonsForScrollTranslationProperty);
+           CompositingReason::kRequiresCullRectExpansion;
   }
 
   const CompositorElementId& GetCompositorElementId() const {
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.cc b/third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.cc
index 9a59038..a45ff42 100644
--- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.cc
@@ -146,13 +146,6 @@
   if (display_width >= kLargeDisplayThreshold)
     actual.bytes_limit_when_visible *= 2;
 #endif
-
-  // If the feature `kScaleTileMemoryLimitFactor` is not enabled,
-  // `kScaleTileMemoryLimitFactor` will default to 1.
-  actual.bytes_limit_when_visible =
-      static_cast<size_t>(actual.bytes_limit_when_visible *
-                          features::kScaleTileMemoryLimitFactor.Get());
-
   return actual;
 }
 
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 9fd313a..a835d91c 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
@@ -68,6 +68,7 @@
             "base::i18n::ToUCharPtr",
             'base::Location',
             'base::MakeRefCounted',
+            'base::MatcherStringPattern',
             'base::Microseconds',
             'base::Milliseconds',
             'base::Minutes',
@@ -88,7 +89,6 @@
             'base::ScopedAllowBlocking',
             'base::ScopedFD',
             'base::ScopedClosureRunner',
-            'base::StringPattern',
             'base::StringPiece',
             'base::SubstringSetMatcher',
             'base::SupportsWeakPtr',
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index deb5d68..217ff37 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -457,8 +457,8 @@
             # memory usage may also grow over time, up to a certain point.
             # Relaunching the driver periodically helps keep it under control.
             return 40
-        # The default is infinite batch size.
-        return 0
+        # The default batch size now is 100, to battle against resource leak.
+        return 100
 
     def default_child_processes(self):
         """Returns the number of child processes to use for this port."""
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index da508c2..619285b 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1867,7 +1867,7 @@
 crbug.com/1147859 virtual/dark-mode-increase-text-contrast/dark-mode/colors/selection.html [ Failure ]
 crbug.com/1147859 wpt_internal/css/css-pseudo/target-text-002.html [ Failure ]
 
-crbug.com/1303102 wpt_internal/css/css-images/object-view-box* [ Skip ]
+crbug.com/1303102 external/wpt/css/css-images/object-view-box* [ Skip ]
 
 # only NGHighlightPainter has the ability to paint decorations added by ::target-text
 crbug.com/1223918 wpt_internal/css/css-pseudo/target-text-001.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/highdpi b/third_party/blink/web_tests/FlagExpectations/highdpi
index 13e86cec..e40f684 100644
--- a/third_party/blink/web_tests/FlagExpectations/highdpi
+++ b/third_party/blink/web_tests/FlagExpectations/highdpi
@@ -1830,3 +1830,5 @@
 crbug.com/1310040 virtual/gpu-rasterization-disable-yuv/images/yuv-decode-eligible/color-profile-layer.html [ Failure Pass ]
 
 crbug.com/1318592 external/wpt/resource-timing/object-not-found-after-TAO-cross-origin-redirect.html [ Pass ]
+
+crbug.com/1324618 external/wpt/css/css-images/object-view-box-fit-fill* [ Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c83142d..2a627d8 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6820,3 +6820,4 @@
 
 # Sheriff 2022-05-20
 crbug.com/1291841 external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-security.sub.html [ Timeout Pass ]
+crbug.com/1327764 inspector-protocol/overlay/overlay-persistent-overlays.js [ Failure Pass ]
diff --git a/third_party/blink/web_tests/accessibility/focus-action-clicks-element-in-active-descendant.html b/third_party/blink/web_tests/accessibility/focus-action-clicks-element-in-active-descendant.html
deleted file mode 100644
index f1b1894..0000000
--- a/third_party/blink/web_tests/accessibility/focus-action-clicks-element-in-active-descendant.html
+++ /dev/null
@@ -1,88 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-
-<table role="grid" aria-activedescendant="focused-cell" tabindex="-1" id="container-with-active-descendant">
-  <tr role="row">
-    <td role="gridcell" id="cell-with-active-descendant">Cell1</td>
-    <td role="gridcell" id="clickable-cell-with-active-descendant">Cell2</td>
-    <td role="gridcell" id="focused-cell">Cell3</td>
-  </tr>
-</table>
-<div id="non-focusable-element">Div</div>
-<button id="focusable-element">Button</button>
-
-<script>
-test(() => {
-  if (document.activeElement)
-    document.activeElement.blur();
-
-    var container = document.getElementById('container-with-active-descendant');
-  var accessibilityFocusable = document.getElementById('cell-with-active-descendant');
-  var axFocusable = accessibilityController.accessibleElementById('cell-with-active-descendant');
-
-  var gotEvent = false;
-  container.addEventListener('click', () => {
-  gotEvent = true;
-  });
-  axFocusable.takeFocus();
-
-  assert_true(gotEvent);
-  assert_not_equals(document.activeElement, accessibilityFocusable);
-  accessibilityFocusable.display = 'none';
-}, 'A click event should be dispatched if a non-focusable element can take accessibility focus via aria-activedescendant.');
-
-test(() => {
-  if (document.activeElement)
-    document.activeElement.blur();
-
-  var accessibilityFocusable = document.getElementById('clickable-cell-with-active-descendant');
-  var axFocusable = accessibilityController.accessibleElementById('clickable-cell-with-active-descendant');
-
-  var gotEvent = false;
-  accessibilityFocusable.addEventListener('click', () => {
-    gotEvent = true;
-  });
-  axFocusable.takeFocus();
-
-  assert_false(gotEvent);
-  assert_not_equals(document.activeElement, accessibilityFocusable);
-  accessibilityFocusable.display = 'none';
-}, 'A click event should not be dispatched if a click handler is attached directly to an element that can take accessibility focus via aria-activedescendant.');
-
-test(() => {
-  if (document.activeElement)
-    document.activeElement.blur();
-
-  var nonFocusable = document.getElementById('non-focusable-element');
-  var axNonFocusable = accessibilityController.accessibleElementById('non-focusable-element');
-
-  var gotEvent = false;
-  nonFocusable.addEventListener('click', () => {
-    gotEvent = true;
-  });
-  axNonFocusable.takeFocus();
-
-  assert_false(gotEvent);
-  assert_not_equals(document.activeElement, nonFocusable);
-  nonFocusable.display = 'none';
-}, 'A click event should not be dispatched if an element cannot take accessibility focus.');
-
-test(() => {
-  if (document.activeElement)
-    document.activeElement.blur();
-
-  var focusable = document.getElementById('focusable-element');
-  var axFocusable = accessibilityController.accessibleElementById('focusable-element');
-
-  var gotEvent = false;
-  focusable.addEventListener('click', () => {
-    gotEvent = true;
-  });
-  axFocusable.takeFocus();
-
-  assert_false(gotEvent);
-  assert_equals(document.activeElement, focusable);
-  focusable.display = 'none';
-}, 'A click event should not be dispatched if an element is focusable.');
-</script>
diff --git a/third_party/blink/web_tests/animations/timing/translate-neutral-keyframe-easing-2-expected.html b/third_party/blink/web_tests/animations/timing/translate-neutral-keyframe-easing-2-expected.html
new file mode 100644
index 0000000..3cad0dd
--- /dev/null
+++ b/third_party/blink/web_tests/animations/timing/translate-neutral-keyframe-easing-2-expected.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<style>
+  #target {
+    background: green;
+    height: 100px;
+    width: 100px;
+    will-change: translate;
+  }
+</style>
+<body>
+<div id="target" style="translate: 50px"></div>
+</body>
diff --git a/third_party/blink/web_tests/animations/timing/translate-neutral-keyframe-easing-2.html b/third_party/blink/web_tests/animations/timing/translate-neutral-keyframe-easing-2.html
new file mode 100644
index 0000000..fa7c9cf
--- /dev/null
+++ b/third_party/blink/web_tests/animations/timing/translate-neutral-keyframe-easing-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<style>
+  #target {
+    background: green;
+    height: 100px;
+    width: 100px;
+    will-change: translate;
+  }
+</style>
+<body>
+<div id="target"></div>
+<script>
+target.animate({translate: '100px'}, {duration: 2e10, delay: -1e10});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-contain-intrinsic-size-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-contain-intrinsic-size-ref.html
new file mode 100644
index 0000000..da77fd40
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-contain-intrinsic-size-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with contain-intrinsic-size : ref</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.default {
+  object-fit: fill;
+  width: 100px;
+  height: 200px;
+  position: relative;
+  top: -100px;
+  left: -50px;
+  clip-path: inset(101px 0px 0px 51px);
+}
+</style>
+<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-contain-intrinsic-size.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-contain-intrinsic-size.html
new file mode 100644
index 0000000..411aff1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-contain-intrinsic-size.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with contain-intrinsic-size</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="object-view-box-contain-intrinsic-size-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.default {
+  /* Paint the yellow box at the bottom right corner. The box should be sized
+     based on |contain-intrinsic-size| but the painted content is based on the
+     view box. */
+  object-view-box: inset(50px 0px 0px 25px);
+  object-fit: fill;
+  contain: size;
+  contain-intrinsic-size: 50px 100px;
+  clip-path: inset(1px 0px 0px 1px);
+}
+</style>
+<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-empty-bounds-ref.html
similarity index 77%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-empty-bounds-ref.html
index 9fe9e2e..9cc4819 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-empty-bounds-ref.html
@@ -2,6 +2,7 @@
 <html>
 <title>CSS object-view-box with empty bounds : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-empty-bounds.html
similarity index 82%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-empty-bounds.html
index f8ef3ca..b793182a 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-empty-bounds.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with empty bounds</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-empty-bounds-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-canvas-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-canvas-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-canvas-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-canvas-ref.html
index 29b60a6..b7998653 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-canvas-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-canvas-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-canvas.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-canvas.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-canvas.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-canvas.html
index 3815d14..ad9b33db 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-canvas.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-canvas.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-contain-canvas-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-img-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-img-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-img-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-img-ref.html
index 5292917..93624ff0 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-img-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-img-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-img.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-img.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-img.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-img.html
index 932d1fe..d2e4c08 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-img.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-img.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-contain-img-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-svg-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-svg-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-svg-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-svg-ref.html
index f5dcddd..66ba39e 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-svg-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-svg-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-svg.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-svg.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-svg.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-svg.html
index 1da53296..cfe711f 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-svg.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-svg.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-contain-svg-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-video-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-video-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-video-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-video-ref.html
index efbd08b..9d33d38 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-video-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-video-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-video.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-video.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-video.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-video.html
index 66d79af..bd4184c 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-contain-video.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-contain-video.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-contain-video-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-canvas-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-canvas-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-canvas-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-canvas-ref.html
index cfc7061e..cd1902b6 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-canvas-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-canvas-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-canvas.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-canvas.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-canvas.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-canvas.html
index bb34604..38976c5 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-canvas.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-canvas.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-cover-canvas-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-img-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-img-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-img-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-img-ref.html
index 5861623..dd52c52 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-img-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-img-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-img.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-img.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-img.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-img.html
index 7ea8115..1103788 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-img.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-img.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-cover-img-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-svg-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-svg-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-svg-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-svg-ref.html
index 1ceb9fc6..36f1941 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-svg-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-svg-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-svg.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-svg.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-svg.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-svg.html
index 8d05fd64..4efe285 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-svg.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-svg.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-cover-svg-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-video-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-video-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-video-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-video-ref.html
index 053a119..4ffac13 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-video-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-video-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-video.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-video.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-video.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-video.html
index a84ea78..bc519a6 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-cover-video.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-cover-video.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-cover-video-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-canvas-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-canvas-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-canvas-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-canvas-ref.html
index a907e78d..8878162 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-canvas-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-canvas-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:fill (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
@@ -19,6 +20,7 @@
   height: 50px;
   overflow: clip;
   display: inline-block;
+  clip-path: inset(1px 0px 0px 0px);
 }
 .view_box_subset {
   position: relative;
@@ -92,6 +94,7 @@
   overflow: clip;
   display: inline-block;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 .view_box_intersection {
   width: 50px;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-canvas.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-canvas.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-canvas.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-canvas.html
index 8fa3fa2f..43415641 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-canvas.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-canvas.html
@@ -5,13 +5,18 @@
 <script src="support/testHelper.js"></script>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-fill-canvas-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
+/* The test uses clip-path to avoid comparing edges with minor pixel differences
+   due to differences in scaling on highdpi devices */
+
 .view_box_subset {
   object-view-box: inset(50px 0px 0px 0px);
   object-fit: fill;
   margin: 5px;
+  clip-path: inset(1px 0px 0px 0px);
 }
 
 .view_box_subset_with_position {
@@ -63,6 +68,7 @@
   object-fit: fill;
   margin: 5px;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 
 .view_box_no_intersection {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-img-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-img-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-img-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-img-ref.html
index d5726e08..f6bf841a 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-img-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-img-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:fill (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
@@ -19,6 +20,7 @@
   height: 50px;
   overflow: clip;
   display: inline-block;
+  clip-path: inset(1px 0px 0px 0px);
 }
 .view_box_subset {
   position: relative;
@@ -92,6 +94,7 @@
   overflow: clip;
   display: inline-block;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 .view_box_intersection {
   width: 50px;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-img.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-img.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-img.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-img.html
index 23cd6c03..c9d938b 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-img.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-img.html
@@ -5,13 +5,18 @@
 <script src="support/testHelper.js"></script>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-fill-img-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
+/* The test uses clip-path to avoid comparing edges with minor pixel differences
+   due to differences in scaling on highdpi devices */
+
 .view_box_subset {
   object-view-box: inset(50px 0px 0px 0px);
   object-fit: fill;
   margin: 5px;
+  clip-path: inset(1px 0px 0px 0px);
 }
 
 .view_box_subset_with_position {
@@ -63,6 +68,7 @@
   object-fit: fill;
   margin: 5px;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 
 .view_box_no_intersection {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-svg-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-svg-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-svg-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-svg-ref.html
index 0ba06cb..5b0f9a8 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-svg-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-svg-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:fill (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
@@ -19,6 +20,7 @@
   height: 50px;
   overflow: clip;
   display: inline-block;
+  clip-path: inset(1px 0px 0px 0px);
 }
 .view_box_subset {
   position: relative;
@@ -92,6 +94,7 @@
   overflow: clip;
   display: inline-block;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 .view_box_intersection {
   width: 50px;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-svg.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-svg.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-svg.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-svg.html
index b7604c3..38c54e9 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-svg.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-svg.html
@@ -5,13 +5,18 @@
 <script src="support/testHelper.js"></script>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-fill-svg-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
+/* The test uses clip-path to avoid comparing edges with minor pixel differences
+   due to differences in scaling on highdpi devices */
+
 .view_box_subset {
   object-view-box: inset(50px 0px 0px 0px);
   object-fit: fill;
   margin: 5px;
+  clip-path: inset(1px 0px 0px 0px);
 }
 
 .view_box_subset_with_position {
@@ -63,6 +68,7 @@
   object-fit: fill;
   margin: 5px;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 
 .view_box_no_intersection {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-video-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-video-ref.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-video-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-video-ref.html
index 58de8ea8..6eeb1b2 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-video-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-video-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:fill (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
@@ -19,6 +20,7 @@
   height: 50px;
   overflow: clip;
   display: inline-block;
+  clip-path: inset(1px 0px 0px 0px);
 }
 .view_box_subset {
   position: relative;
@@ -92,6 +94,7 @@
   overflow: clip;
   display: inline-block;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 .view_box_intersection {
   width: 50px;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-video.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-video.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-video.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-video.html
index 8c1c18b..c9f91b6 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-fill-video.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-fill-video.html
@@ -5,13 +5,18 @@
 <script src="support/testHelper.js"></script>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-fill-video-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
+/* The test uses clip-path to avoid comparing edges with minor pixel differences
+   due to differences in scaling on highdpi devices */
+
 .view_box_subset {
   object-view-box: inset(50px 0px 0px 0px);
   object-fit: fill;
   margin: 5px;
+  clip-path: inset(1px 0px 0px 0px);
 }
 
 .view_box_subset_with_position {
@@ -63,6 +68,7 @@
   object-fit: fill;
   margin: 5px;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 
 .view_box_no_intersection {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-canvas-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-canvas-ref.html
similarity index 92%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-canvas-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-canvas-ref.html
index 9263af56..a1b3f5e 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-canvas-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-canvas-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:none (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-canvas.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-canvas.html
similarity index 91%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-canvas.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-canvas.html
index ebb2372..6fe8c16 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-canvas.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-canvas.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-none-canvas-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-img-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-img-ref.html
similarity index 92%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-img-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-img-ref.html
index 916d1237..b988298b 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-img-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-img-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:none (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-img.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-img.html
similarity index 91%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-img.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-img.html
index 53d8447..f4a74122 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-img.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-img.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-none-img-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-svg-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-svg-ref.html
similarity index 92%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-svg-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-svg-ref.html
index 26de617..b15cb62 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-svg-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-svg-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:none (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-svg.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-svg.html
similarity index 91%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-svg.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-svg.html
index 652c935..c103692 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-svg.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-svg.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-none-svg-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-video-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-video-ref.html
similarity index 92%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-video-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-video-ref.html
index 112b32b..2aacfa8 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-video-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-video-ref.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with object-fit:none (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-video.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-video.html
similarity index 91%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-video.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-video.html
index 418ed198..34b0857 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-fit-none-video.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-fit-none-video.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-none-video-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-iframe-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-iframe-ref.html
similarity index 77%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-iframe-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-iframe-ref.html
index e74984b1..e014e10 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-iframe-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-iframe-ref.html
@@ -2,6 +2,7 @@
 <html>
 <title>CSS object-view-box with an svg with no intrinsic size : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-iframe.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-iframe.html
similarity index 81%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-iframe.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-iframe.html
index 7abb800..a144a8f 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-iframe.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-iframe.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box on an iframe should be no-op</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-iframe-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-negative-bounds-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-negative-bounds-ref.html
similarity index 77%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-negative-bounds-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-negative-bounds-ref.html
index 66ed7d9..3b4eaac 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-negative-bounds-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-negative-bounds-ref.html
@@ -2,6 +2,7 @@
 <html>
 <title>CSS object-view-box with a negative size : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-negative-bounds.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-negative-bounds.html
similarity index 82%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-negative-bounds.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-negative-bounds.html
index ed17ca3a..34dd6075 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-negative-bounds.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-negative-bounds.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with a negative size</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-negative-bounds-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-parsing.html
similarity index 92%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-parsing.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-parsing.html
index 37175bcf..da17b793 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-parsing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-parsing.html
@@ -4,6 +4,7 @@
 <meta charset="utf-8">
 <title>CSS object-view-box: computed values</title>
 <link rel="author" title="Khushal Sagar" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-property-changed-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-property-changed-ref.html
similarity index 85%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-property-changed-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-property-changed-ref.html
index b304650b6d..8974dfd 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-property-changed-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-property-changed-ref.html
@@ -2,6 +2,7 @@
 <html>
 <title>Changing CSS object-view-box should trigger relayout : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 div {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-property-changed.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-property-changed.html
similarity index 91%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-property-changed.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-property-changed.html
index 00b959da..2574558e 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-property-changed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-property-changed.html
@@ -3,6 +3,7 @@
 <title>Changing CSS object-view-box should trigger relayout</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-property-changed-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <script src="/common/reftest-wait.js"></script>
 <script src="/common/rendering-utils.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-auto-ref.html
similarity index 69%
copy from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds-ref.html
copy to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-auto-ref.html
index 9fe9e2e..54e9077 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-empty-bounds-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-auto-ref.html
@@ -1,12 +1,11 @@
 <!DOCTYPE html>
 <html>
-<title>CSS object-view-box with empty bounds : ref</title>
+<title>CSS object-view-box on an element with rect() auto : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 
 <style>
 .default {
   object-fit: fill;
-  background-color: black;
 }
 </style>
 <img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-auto.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-auto.html
new file mode 100644
index 0000000..97f18c7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-auto.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with rect() auto</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="object-view-box-rect-auto-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.default {
+  object-view-box: rect(auto auto auto auto);
+  object-fit: fill;
+}
+</style>
+<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-percentage-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-percentage-ref.html
new file mode 100644
index 0000000..3d413fb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-percentage-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with rect() percent values : ref</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+
+<style>
+.container {
+  width: 25px;
+  height: 50px;
+  overflow: hidden;
+}
+.default {
+  position: relative;
+  top: -50px;
+  left: -25px;
+}
+</style>
+<div class="container">
+ <img class="default" src="support/exif-orientation-6-ru.jpg"></img>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-percentage.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-percentage.html
new file mode 100644
index 0000000..45f05938
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-percentage.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with rect() percent values</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="object-view-box-rect-percentage-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.default {
+  object-view-box: rect(50% 100% 100% 50%);
+  object-fit: fill;
+}
+</style>
+<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-ref.html
new file mode 100644
index 0000000..b27a5b8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with rect() : ref</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.container {
+  width: 25px;
+  height: 50px;
+  overflow: hidden;
+}
+.default {
+  position: relative;
+  top: -50px;
+  left: -25px;
+}
+</style>
+<div class="container">
+ <img class="default" src="support/exif-orientation-6-ru.jpg"></img>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect.html
new file mode 100644
index 0000000..2a0a8d30
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-rect.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with rect()</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="object-view-box-rect-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.default {
+  object-view-box: rect(50px 50px 100px 25px);
+  object-fit: fill;
+}
+</style>
+<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-same-size-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-same-size-ref.html
similarity index 78%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-same-size-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-same-size-ref.html
index 78f07a0..9e237a8 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-same-size-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-same-size-ref.html
@@ -2,6 +2,7 @@
 <html>
 <title>CSS object-view-box with same size as intrinsic size : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-same-size.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-same-size.html
similarity index 82%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-same-size.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-same-size.html
index 44ac7f6..0daa360 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-same-size.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-same-size.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with same size as intrinsic size</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-negative-bounds-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-size-containment-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-size-containment-ref.html
similarity index 62%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-size-containment-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-size-containment-ref.html
index 0d3ccd6..24097c6 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-size-containment-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-size-containment-ref.html
@@ -2,13 +2,16 @@
 <html>
 <title>CSS object-view-box on an element with size containment : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
-  contain: size;
   object-fit: fill;
   width: 50px;
-  height: 100px;
+  height: 200px;
+  position: relative;
+  top: -100px;
+  clip-path: inset(101px 0px 0px 0px);
 }
 </style>
 <img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-size-containment.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-size-containment.html
similarity index 78%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-size-containment.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-size-containment.html
index 9cd3097..c58e673 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-size-containment.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-size-containment.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box on an element with size containment</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-size-containment-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
@@ -11,6 +12,7 @@
   contain: size;
   width: 50px;
   height: 100px;
+  clip-path: inset(1px 0px 0px 0px);
 }
 </style>
 <img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-svg-img-no-size-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-svg-img-no-size-ref.html
similarity index 77%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-svg-img-no-size-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-svg-img-no-size-ref.html
index 60d0941..ca1ea59 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-svg-img-no-size-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-svg-img-no-size-ref.html
@@ -2,6 +2,7 @@
 <html>
 <title>CSS object-view-box with an svg with no intrinsic size : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-svg-img-no-size.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-svg-img-no-size.html
similarity index 83%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-svg-img-no-size.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-svg-img-no-size.html
index a531755..1d1c97a6 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-svg-img-no-size.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-svg-img-no-size.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with an svg with no intrinsic size</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-svg-img-no-size-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <style>
 .default {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-canvas-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-canvas-ref.html
similarity index 88%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-canvas-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-canvas-ref.html
index d76ec48..56fa32d5 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-canvas-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-canvas-ref.html
@@ -4,6 +4,7 @@
 <title>CSS object-view-box with vertical writing mode : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-canvas.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-canvas.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-canvas.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-canvas.html
index 7369165..8e0aaea2 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-canvas.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-canvas.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-writing-mode-canvas-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-img-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-img-ref.html
similarity index 88%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-img-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-img-ref.html
index 260abda..8db7bc6 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-img-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-img-ref.html
@@ -4,6 +4,7 @@
 <title>CSS object-view-box with vertical writing mode : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-img.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-img.html
similarity index 88%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-img.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-img.html
index fba8264..547a2ed 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-img.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-img.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-writing-mode-img-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-svg-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-svg-ref.html
similarity index 88%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-svg-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-svg-ref.html
index 772e25b..c15b12a 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-svg-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-svg-ref.html
@@ -4,6 +4,7 @@
 <title>CSS object-view-box with vertical writing mode : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-svg.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-svg.html
similarity index 88%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-svg.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-svg.html
index face4dc9..5d90bb6 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-svg.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-svg.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-writing-mode-svg-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-video-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-video-ref.html
similarity index 88%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-video-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-video-ref.html
index 6e9ffdd..e9b5be9 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-video-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-video-ref.html
@@ -4,6 +4,7 @@
 <title>CSS object-view-box with vertical writing mode : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-video.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-video.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-video.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-video.html
index 8a46157..0dd2e79 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-writing-mode-video.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-writing-mode-video.html
@@ -5,6 +5,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-writing-mode-video-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-percentage-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-percentage-ref.html
new file mode 100644
index 0000000..23d2e06
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-percentage-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with xywh() with percentage values : ref</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+
+<style>
+.container {
+  width: 25px;
+  height: 50px;
+  overflow: hidden;
+}
+.default {
+  position: relative;
+  top: -50px;
+  left: -25px;
+}
+</style>
+<div class="container">
+ <img class="default" src="support/exif-orientation-6-ru.jpg"></img>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-percentage.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-percentage.html
new file mode 100644
index 0000000..0f1d593
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-percentage.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with xywh() with percentage values</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="object-view-box-xywh-percentage-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.default {
+  object-view-box: xywh(50% 50% 50% 50%);
+  object-fit: fill;
+}
+</style>
+<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-ref.html
new file mode 100644
index 0000000..85ca9d3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with xywh() : ref</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.container {
+  width: 25px;
+  height: 50px;
+  overflow: hidden;
+}
+.default {
+  position: relative;
+  top: -50px;
+  left: -25px;
+}
+</style>
+<div class="container">
+ <img class="default" src="support/exif-orientation-6-ru.jpg"></img>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh.html b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh.html
new file mode 100644
index 0000000..258ff6f0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/object-view-box-xywh.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<title>CSS object-view-box on an element with xywh()</title>
+<link rel="author" href="mailto:khushalsagar@chromium.org">
+<link rel="match" href="object-view-box-xywh-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
+
+<style>
+.default {
+  object-view-box: xywh(25px 50px 25px 50px);
+  object-fit: fill;
+}
+</style>
+<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/blue-green-red-yellow-50x100.svg b/third_party/blink/web_tests/external/wpt/css/css-images/support/blue-green-red-yellow-50x100.svg
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/blue-green-red-yellow-50x100.svg
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/blue-green-red-yellow-50x100.svg
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/blue-green-red-yellow-no-size.svg b/third_party/blink/web_tests/external/wpt/css/css-images/support/blue-green-red-yellow-no-size.svg
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/blue-green-red-yellow-no-size.svg
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/blue-green-red-yellow-no-size.svg
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/exif-orientation-6-ru.jpg b/third_party/blink/web_tests/external/wpt/css/css-images/support/exif-orientation-6-ru.jpg
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/exif-orientation-6-ru.jpg
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/exif-orientation-6-ru.jpg
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/generate_object_view_box_tests.py b/third_party/blink/web_tests/external/wpt/css/css-images/support/generate_object_view_box_tests.py
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/generate_object_view_box_tests.py
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/generate_object_view_box_tests.py
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-contain-ref-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-contain-ref-template.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-contain-ref-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-contain-ref-template.html
index 01e88c2..1277b83 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-contain-ref-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-contain-ref-template.html
@@ -2,6 +2,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-contain-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-contain-template.html
similarity index 92%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-contain-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-contain-template.html
index e7297b0..f874e65 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-contain-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-contain-template.html
@@ -4,6 +4,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-contain-__NAME__-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-cover-ref-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-cover-ref-template.html
similarity index 93%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-cover-ref-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-cover-ref-template.html
index e37e62e..9d1fb74 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-cover-ref-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-cover-ref-template.html
@@ -2,6 +2,7 @@
 <title>CSS object-view-box with object-fit:contain (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-cover-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-cover-template.html
similarity index 92%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-cover-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-cover-template.html
index 42e60f7d..85664a2 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-cover-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-cover-template.html
@@ -4,6 +4,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-cover-__NAME__-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-fill-ref-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-fill-ref-template.html
similarity index 94%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-fill-ref-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-fill-ref-template.html
index 38d779e..b26ce3e 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-fill-ref-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-fill-ref-template.html
@@ -2,6 +2,7 @@
 <title>CSS object-view-box with object-fit:fill (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
@@ -18,6 +19,7 @@
   height: 50px;
   overflow: clip;
   display: inline-block;
+  clip-path: inset(1px 0px 0px 0px);
 }
 .view_box_subset {
   position: relative;
@@ -91,6 +93,7 @@
   overflow: clip;
   display: inline-block;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 .view_box_intersection {
   width: 50px;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-fill-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-fill-template.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-fill-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-fill-template.html
index 3ed819d..ee2d83e8 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-fill-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-fill-template.html
@@ -4,13 +4,18 @@
 <script src="support/testHelper.js"></script>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-fill-__NAME__-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
+/* The test uses clip-path to avoid comparing edges with minor pixel differences
+   due to differences in scaling on highdpi devices */
+
 .view_box_subset {
   object-view-box: inset(50px 0px 0px 0px);
   object-fit: fill;
   margin: 5px;
+  clip-path: inset(1px 0px 0px 0px);
 }
 
 .view_box_subset_with_position {
@@ -62,6 +67,7 @@
   object-fit: fill;
   margin: 5px;
   background-color: grey;
+  clip-path: inset(0px 0px 1px 0px);
 }
 
 .view_box_no_intersection {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-none-ref-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-none-ref-template.html
similarity index 91%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-none-ref-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-none-ref-template.html
index 7dabd43..421a4a0 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-none-ref-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-none-ref-template.html
@@ -2,6 +2,7 @@
 <title>CSS object-view-box with object-fit:none (ref)</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-none-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-none-template.html
similarity index 90%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-none-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-none-template.html
index f91d01f..ab0c083 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-fit-none-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-fit-none-template.html
@@ -4,6 +4,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-fit-none-__NAME__-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-writing-mode-ref-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-writing-mode-ref-template.html
similarity index 87%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-writing-mode-ref-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-writing-mode-ref-template.html
index 1015ac3..2986e5ac 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-writing-mode-ref-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-writing-mode-ref-template.html
@@ -3,6 +3,7 @@
 <title>CSS object-view-box with vertical writing mode : ref</title>
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-writing-mode-template.html b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-writing-mode-template.html
similarity index 86%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-writing-mode-template.html
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-writing-mode-template.html
index 1755ffe..a8c1189a 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/support/object-view-box-writing-mode-template.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/support/object-view-box-writing-mode-template.html
@@ -4,6 +4,7 @@
 <link rel="author" href="mailto:khushalsagar@chromium.org">
 <link rel="match" href="object-view-box-writing-mode-__NAME__-ref.html">
 <script src="support/testHelper.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-images-4/#the-object-view-box">
 
 <body>
 <style>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/support/testHelper.js b/third_party/blink/web_tests/external/wpt/css/css-images/support/testHelper.js
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-images/support/testHelper.js
rename to third_party/blink/web_tests/external/wpt/css/css-images/support/testHelper.js
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/rotate-animation-with-will-change-transform-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/rotate-animation-with-will-change-transform-001-ref.html
new file mode 100644
index 0000000..0dd93d3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/rotate-animation-with-will-change-transform-001-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>CSS Test (Transforms): Transform and perspective with w negative</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+
+<style>
+
+div {
+  width: 100px;
+  height: 100px;
+  transform: rotateY(44deg);
+  background: fuchsia;
+  transform-origin: 100px 0;
+  will-change: transform;
+}
+
+</style>
+
+<div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/rotate-animation-with-will-change-transform-001.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/rotate-animation-with-will-change-transform-001.html
new file mode 100644
index 0000000..9d330e15
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/animation/rotate-animation-with-will-change-transform-001.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>CSS Test (Transforms): Transform and perspective with w negative</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=696374">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#individual-transforms">
+<link rel="match" href="rotate-animation-with-will-change-transform-001-ref.html">
+
+<!--
+
+This is a simplified version of one case within the Blink web test
+virtual/threaded-no-composited-antialiasing/animations/composited-animations-rotate-zero-degrees.html
+but with will-change: transform added, so that it fails with the bug
+that is introduced in the intermediate state of fixing
+https://bugs.chromium.org/p/chromium/issues/detail?id=696374
+
+-->
+
+<style>
+
+@keyframes a {
+  from { rotate: 0 1 0 44deg; }
+  to { rotate: 0 1 0 44deg; }
+}
+
+div {
+  width: 100px;
+  height: 100px;
+  animation: a linear 10s infinite;
+  /* rotate: 0 1 0 44deg; */
+  background: fuchsia;
+  transform-origin: 100px 0;
+  will-change: transform;
+}
+
+</style>
+
+<div></div>
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/layer-compositing-reasons.js b/third_party/blink/web_tests/http/tests/devtools/layers/layer-compositing-reasons.js
index 8590ecff..ab0881f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/layers/layer-compositing-reasons.js
+++ b/third_party/blink/web_tests/http/tests/devtools/layers/layer-compositing-reasons.js
@@ -17,7 +17,8 @@
   }
 
   const idsToTest = [
-    'transform3d', 'transform3d-individual', 'backface-visibility', 'animation', 'animation-individual',
+    'transform3d', 'scale3d', 'rotate3d', 'translate3d', 'backface-visibility',
+    'animation', 'animation-scale', 'animation-rotate', 'animation-translate',
     'transformWithCompositedDescendants', 'transformWithCompositedDescendants-individual',
     'opacityWithCompositedDescendants', 'reflectionWithCompositedDescendants', 'perspective', 'preserve3d'
   ];
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/resources/compositing-reasons.html b/third_party/blink/web_tests/http/tests/devtools/layers/resources/compositing-reasons.html
index d67c7080..0e2d869 100644
--- a/third_party/blink/web_tests/http/tests/devtools/layers/resources/compositing-reasons.html
+++ b/third_party/blink/web_tests/http/tests/devtools/layers/resources/compositing-reasons.html
@@ -5,11 +5,21 @@
     100%  { transform: rotate(360deg); }
 }
 
+@keyframes scale-individual {
+    0%  { scale: 1; }
+    50%  { scale: 2; }
+    100%  { scale: 4; }
+}
 @keyframes rotate-individual {
     0%  { rotate: 0deg; }
     50%  { rotate: 180deg; }
     100%  { rotate: 360deg; }
 }
+@keyframes translate-individual {
+    0%  { translate: 0px 0; }
+    50%  { translate: 100px 0; }
+    100%  { translate: 150px 50px; }
+}
 div {
     /* to avoid empty layers */
     min-width: 100px;
@@ -22,10 +32,14 @@
     document.addEventListener('animationstart', () => internals.pauseAnimations(0));
 </script>
 <div id="transform3d" style="transform: translateZ(100px);"></div>
-<div id="transform3d-individual" style="translate: 0px 0px 100px;">translated individual</div>
+<div id="scale3d" style="scale: 1 1 2;">3d scale</div>
+<div id="rotate3d" style="rotate: 0 1 0 45deg;">3d rotate</div>
+<div id="translate3d" style="translate: 0px 0px 100px;">3d translate</div>
 <div id="backface-visibility" style="backface-visibility: hidden">backface hidden</div>
 <div id="animation" style="width: 50px; height: 50px; -webkit-animation-name: rotate; -webkit-animation-iteration-count: infinite; -webkit-animation-duration: 5s;">animated</div>
-<div id="animation-individual" style="width: 50px; height: 50px; animation-name: rotate-individual; animation-iteration-count: infinite; animation-duration: 5s;">animated individual</div>
+<div id="animation-scale" style="width: 50px; height: 50px; animation-name: scale-individual; animation-iteration-count: infinite; animation-duration: 5s;">animated individual</div>
+<div id="animation-rotate" style="width: 50px; height: 50px; animation-name: rotate-individual; animation-iteration-count: infinite; animation-duration: 5s;">animated individual</div>
+<div id="animation-translate" style="width: 50px; height: 50px; animation-name: translate-individual; animation-iteration-count: infinite; animation-duration: 5s;">animated individual</div>
 <div id="transformWithCompositedDescendants" style="transform: rotate(10deg)">
     <div style="transform: scale3d(2, 3, 4);">
     </div>
diff --git a/third_party/blink/web_tests/platform/generic/http/tests/devtools/layers/layer-compositing-reasons-expected.txt b/third_party/blink/web_tests/platform/generic/http/tests/devtools/layers/layer-compositing-reasons-expected.txt
index 40d73a1..ef5b4345 100644
--- a/third_party/blink/web_tests/platform/generic/http/tests/devtools/layers/layer-compositing-reasons-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/http/tests/devtools/layers/layer-compositing-reasons-expected.txt
@@ -1,9 +1,13 @@
 Tests layer compositing reasons in Layers Panel
 Compositing reason ids for div#transform3d: transform3D
-Compositing reason ids for div#transform3d-individual: transform3D
+Compositing reason ids for div#scale3d: scale3D
+Compositing reason ids for div#rotate3d: rotate3D
+Compositing reason ids for div#translate3d: translate3D
 Compositing reason ids for div#backface-visibility: backfaceVisibilityHidden
 Compositing reason ids for div#animation: activeTransformAnimation
-Compositing reason ids for div#animation-individual: activeTransformAnimation
+Compositing reason ids for div#animation-scale: activeScaleAnimation
+Compositing reason ids for div#animation-rotate: activeRotateAnimation
+Compositing reason ids for div#animation-translate: activeTranslateAnimation
 Compositing reason ids for div#transformWithCompositedDescendants: overlap
 Compositing reason ids for div#transformWithCompositedDescendants-individual: overlap
 Compositing reason ids for div#opacityWithCompositedDescendants: overflowScrolling,rootScroller
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-parse-invalid.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-parse-invalid.html
new file mode 100644
index 0000000..6eceb9b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-parse-invalid.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Tests values that are invalid at parse time for the anchor() function</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-pos">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+
+<script>
+// anchor() can only be used in inset properties
+test_invalid_value('margin-top', 'anchor(--foo top)');
+test_invalid_value('height', 'anchor(--foo top)');
+test_invalid_value('font-size', 'anchor(--foo top)');
+
+// Invalid parameter format
+test_invalid_value('top', 'anchor(--foo, top)');
+test_invalid_value('top', 'anchor(--foo top,)');
+test_invalid_value('top', 'anchor(--foo top bottom)');
+test_invalid_value('top', 'anchor(--foo top, 10px 20%)');
+test_invalid_value('top', 'anchor(--foo top, 10px, 20%)');
+
+// Anchor name must be a dashed ident
+test_invalid_value('top', 'anchor(foo top)');
+
+// Invalid anchor side values
+test_invalid_value('top', 'anchor(--foo height)');
+test_invalid_value('top', 'anchor(--foo 10em)');
+test_invalid_value('top', 'anchor(--foo 100s)');
+
+// Invalid fallback values
+test_invalid_value('top', 'anchor(--foo top, 1)');
+test_invalid_value('top', 'anchor(--foo top, 100s)');
+test_invalid_value('top', 'anchor(--foo top, bottom)');
+test_invalid_value('top', 'anchor(--foo top, anchor(bar top))');
+test_invalid_value('top', 'anchor(--foo top, anchor-size(--bar height))');
+
+// Invalid anchor values in calc tree
+test_invalid_value('top', 'calc(anchor(foo top) + 10px + 10%)');
+test_invalid_value('top', 'calc(10px + 100 * anchor(--foo top, anchor(bar bottom)))');
+test_invalid_value('top', 'min(anchor(--foo top), anchor(--bar bottom), anchor-size(--baz height))');
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-parse-valid.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-parse-valid.html
new file mode 100644
index 0000000..3acf2ac8
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-parse-valid.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>Tests parsing of the anchor() function</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-pos">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+
+<script>
+const insetProperties = [
+  'left',
+  'right',
+  'top',
+  'bottom',
+  'inset-block-start',
+  'inset-block-end',
+  'inset-inline-start',
+  'inset-inline-end',
+];
+
+const anchorSides = [
+  'left',
+  'right',
+  'top',
+  'bottom',
+  'start',
+  'end',
+  'self-start',
+  'self-end',
+  'center',
+  '50%',
+];
+
+const fallbacks = [
+  null,
+  '1px',
+  '50%',
+  'calc(1px + 50%)',
+  'anchor(--bar left)',
+  'anchor(--bar left, anchor(--baz right))',
+];
+
+// Tests basic combinations
+for (let property of insetProperties) {
+  // Using a wrong anchor-side (e.g., `top: anchor(--foo left)`) doesn't cause a
+  // parse error, but triggers the fallback when resolved.
+  for (let side of anchorSides) {
+    for (let fallback of fallbacks) {
+      let value = `anchor(--foo ${side}${fallback ? ', ' + fallback : ''})`;
+      test_valid_value(property, value);
+    }
+  }
+}
+
+// Tests that anchor() can be used in a calc tree
+test_valid_value('top', 'calc((anchor(--foo top) + anchor(--bar bottom)) / 2)');
+test_valid_value('top', 'anchor(--foo top, calc(anchor(--bar bottom) * 0.5))');
+test_valid_value('top', 'min(100px, 10%, anchor(--foo top), anchor(--bar bottom))');
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-query-custom-property-registration.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-query-custom-property-registration.html
new file mode 100644
index 0000000..ebb52ca
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-query-custom-property-registration.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>Tests using anchor queries in custom property initial value</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/">
+<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#register-a-custom-property">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+setup(() => assert_own_property(CSS, 'registerProperty'));
+
+// Anchor queries are not computationally independent, so they cannot be used
+// in the initial value of any typed custom property.
+
+test(() => assert_throws_dom(
+  'SyntaxError',
+  () => CSS.registerProperty({
+    name: '--x',
+    syntax: '<length>',
+    inherits: false,
+    initialValue: 'anchor(--foo top)',
+  })), 'anchor() cannot be used as <length> initial value');
+
+test(() => assert_throws_dom(
+  'SyntaxError',
+  () => CSS.registerProperty({
+    name: '--x',
+    syntax: '<length>',
+    inherits: false,
+    initialValue: 'anchor-size(--foo width)',
+  })), 'anchor-size() cannot be used as <length> initial value');
+
+test(() => assert_throws_dom(
+  'SyntaxError',
+  () => CSS.registerProperty({
+    name: '--x',
+    syntax: '<length-percentage>',
+    inherits: false,
+    initialValue: 'anchor(--foo top)',
+  })), 'anchor() cannot be used as <length-percentage> initial value');
+
+test(() => assert_throws_dom(
+  'SyntaxError',
+  () => CSS.registerProperty({
+    name: '--x',
+    syntax: '<length-percentage>',
+    inherits: false,
+    initialValue: 'anchor-size(--foo width)',
+  })), 'anchor-size() cannot be used as <length-percentage> initial value');
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-parse-invalid.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-parse-invalid.html
new file mode 100644
index 0000000..31dea095f
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-parse-invalid.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>Tests values that are invalid at parse time for the anchor-size() function</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-size">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+
+<script>
+// anchor-size() can only be used in sizing properties
+test_invalid_value('margin-top', 'anchor-size(--foo width)');
+test_invalid_value('top', 'anchor-size(--foo width)');
+test_invalid_value('font-size', 'anchor-size(--foo width)');
+
+// Invalid parameter format
+test_invalid_value('width', 'anchor-size(--foo, width)');
+test_invalid_value('width', 'anchor-size(--foo width,)');
+test_invalid_value('width', 'anchor-size(--foo width height)');
+test_invalid_value('width', 'anchor-size(--foo width, 10px 20%)');
+test_invalid_value('width', 'anchor-size(--foo width, 10px, 20%)');
+
+// Anchor name must be a dashed ident
+test_invalid_value('width', 'anchor-size(foo width)');
+
+// Invalid anchor size values
+test_invalid_value('width', 'anchor-size(--foo top)');
+test_invalid_value('width', 'anchor-size(--foo 10em)');
+test_invalid_value('width', 'anchor-size(--foo 100s)');
+test_invalid_value('width', 'anchor-size(--foo 50%)');
+
+// Invalid fallback values
+test_invalid_value('width', 'anchor-size(--foo width, 1)');
+test_invalid_value('width', 'anchor-size(--foo width, 100s)');
+test_invalid_value('width', 'anchor-size(--foo width, height)');
+test_invalid_value('width', 'anchor-size(--foo width, anchor-size(bar width))');
+test_invalid_value('width', 'anchor-size(--foo width, anchor(--bar top))');
+
+// Invalid anchor size values in calc tree
+test_invalid_value('width', 'calc(anchor-size(foo width) + 10px + 10%)');
+test_invalid_value('width', 'calc(10px + 100 * anchor-size(--foo width, anchor-size(bar bottom)))');
+test_invalid_value('width', 'min(anchor-size(--foo width), anchor-size(--bar height), anchor(--baz top))');
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-parse-valid.html b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-parse-valid.html
new file mode 100644
index 0000000..b21deab
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-anchor-position/anchor-size-parse-valid.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<title>Tests parsing of the anchor-size() function</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-size">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+
+<script>
+const sizeProperties = [
+  'width',
+  'min-width',
+  'max-width',
+  'height',
+  'min-height',
+  'max-height',
+  'block-size',
+  'min-block-size',
+  'max-block-size',
+  'inline-size',
+  'min-inline-size',
+  'max-inline-size',
+];
+
+const anchorSizes = [
+  'width',
+  'height',
+  'block',
+  'inline',
+  'self-block',
+  'self-inline',
+];
+
+const fallbacks = [
+  null,
+  '1px',
+  '50%',
+  'calc(1px + 50%)',
+  'anchor-size(--bar block)',
+  'anchor-size(--bar block, anchor-size(--baz inline))',
+];
+
+// Tests basic combinations
+for (let property of sizeProperties) {
+  for (let size of anchorSizes) {
+    for (let fallback of fallbacks) {
+      let value = `anchor-size(--foo ${size}${fallback ? ', ' + fallback : ''})`;
+      test_valid_value(property, value);
+    }
+  }
+}
+
+// Tests that anchor-size() can be used in a calc tree
+test_valid_value('width', 'calc((anchor-size(--foo width) + anchor-size(--bar height)) / 2)');
+test_valid_value('width', 'anchor-size(--foo width, calc(anchor-size(--bar height) * 0.5))');
+test_valid_value('width', 'min(100px, 10%, anchor-size(--foo width), anchor-size(--bar height))');
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-contain-intrinsic-size-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-contain-intrinsic-size-ref.html
deleted file mode 100644
index 206f6f7..0000000
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-contain-intrinsic-size-ref.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>CSS object-view-box on an element with contain-intrinsic-size : ref</title>
-<link rel="author" href="mailto:khushalsagar@chromium.org">
-
-<style>
-.default {
-  object-fit: fill;
-  contain: size;
-  contain-intrinsic-size: 50px 100px;
-}
-</style>
-<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-contain-intrinsic-size.html b/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-contain-intrinsic-size.html
deleted file mode 100644
index f68253c..0000000
--- a/third_party/blink/web_tests/wpt_internal/css/css-images/object-view-box-contain-intrinsic-size.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>CSS object-view-box on an element with contain-intrinsic-size</title>
-<link rel="author" href="mailto:khushalsagar@chromium.org">
-<link rel="match" href="object-view-box-size-containment-ref.html">
-
-<style>
-.default {
-  object-view-box: inset(50px 0px 0px 0px);
-  object-fit: fill;
-  contain: size;
-  contain-intrinsic-size: 50px 100px;
-}
-</style>
-<img class="default" src="support/exif-orientation-6-ru.jpg"></img>
diff --git a/third_party/blink/web_tests/wpt_internal/performance-timeline/performance-user-timing-special-names.html b/third_party/blink/web_tests/wpt_internal/performance-timeline/performance-user-timing-special-names.html
new file mode 100644
index 0000000..561d253
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/performance-timeline/performance-user-timing-special-names.html
@@ -0,0 +1,18 @@
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+  const kPerformanceMeasureFindExistingName = 4236;
+  const kSpecialNameInUserTimingAPI = 'connectStart';
+  function clearUseCounter() {
+    internals.clearUseCounter(document, kPerformanceMeasureFindExistingName);
+  }
+
+  test(t => {
+    t.add_cleanup(clearUseCounter);
+    assert_false(internals.isUseCounted(document, kPerformanceMeasureFindExistingName), "Performance measure processing special names in User Timing API should not happen");
+    performance.mark('curr');
+    performance.measure('test-special-names', kSpecialNameInUserTimingAPI, 'curr');
+    assert_true(internals.isUseCounted(document, kPerformanceMeasureFindExistingName), "Performance measure processing special names in User Timing API should happen");
+  }, 'Performance measure processes special names that are defined in User Timing API');
+</script>
\ No newline at end of file
diff --git a/third_party/ipcz/src/BUILD.gn b/third_party/ipcz/src/BUILD.gn
index 9f214d0..42d492ca 100644
--- a/third_party/ipcz/src/BUILD.gn
+++ b/third_party/ipcz/src/BUILD.gn
@@ -144,10 +144,14 @@
 
   if (enable_ipcz_multiprocess_test_support) {
     public += [
+      "reference_drivers/blob.h",
       "reference_drivers/memory.h",
       "reference_drivers/os_handle.h",
     ]
-    sources += [ "reference_drivers/memory.cc" ]
+    sources += [
+      "reference_drivers/blob.cc",
+      "reference_drivers/memory.cc",
+    ]
 
     if (is_android) {
       sources += [
@@ -217,6 +221,7 @@
   public = [
     "ipcz/api_object.h",
     "ipcz/block_allocator.h",
+    "ipcz/box.h",
     "ipcz/buffer_pool.h",
     "ipcz/driver_memory.h",
     "ipcz/driver_memory_mapping.h",
@@ -247,6 +252,7 @@
     "ipcz/block_allocator.cc",
     "ipcz/block_allocator_pool.cc",
     "ipcz/block_allocator_pool.h",
+    "ipcz/box.cc",
     "ipcz/buffer_id.h",
     "ipcz/buffer_pool.cc",
     "ipcz/driver_memory.cc",
@@ -351,7 +357,12 @@
   ]
 
   if (enable_ipcz_multiprocess_test_support) {
-    sources += [ "reference_drivers/memory_test.cc" ]
+    sources += [
+      # Box tests rely on the Blob driver object, which itself requires
+      # multiprocess test support.
+      "box_test.cc",
+      "reference_drivers/memory_test.cc",
+    ]
   }
 
   deps = [
diff --git a/third_party/ipcz/src/api.cc b/third_party/ipcz/src/api.cc
index 10f7bbe..5d53e48e 100644
--- a/third_party/ipcz/src/api.cc
+++ b/third_party/ipcz/src/api.cc
@@ -7,6 +7,7 @@
 
 #include "api.h"
 #include "ipcz/api_object.h"
+#include "ipcz/box.h"
 #include "ipcz/ipcz.h"
 #include "ipcz/node.h"
 #include "ipcz/node_link_memory.h"
@@ -219,14 +220,37 @@
                uint32_t flags,
                const void* options,
                IpczHandle* handle) {
-  return IPCZ_RESULT_UNIMPLEMENTED;
+  ipcz::Node* node = ipcz::Node::FromHandle(node_handle);
+  if (!node || driver_handle == IPCZ_INVALID_DRIVER_HANDLE || !handle) {
+    return IPCZ_RESULT_INVALID_ARGUMENT;
+  }
+
+  auto box = ipcz::MakeRefCounted<ipcz::Box>(
+      ipcz::DriverObject(ipcz::WrapRefCounted(node), driver_handle));
+  *handle = ipcz::Box::ReleaseAsHandle(std::move(box));
+  return IPCZ_RESULT_OK;
 }
 
 IpczResult Unbox(IpczHandle handle,
                  IpczUnboxFlags flags,
                  const void* options,
                  IpczDriverHandle* driver_handle) {
-  return IPCZ_RESULT_UNIMPLEMENTED;
+  if (!driver_handle) {
+    return IPCZ_RESULT_INVALID_ARGUMENT;
+  }
+
+  ipcz::Ref<ipcz::Box> box = ipcz::Box::TakeFromHandle(handle);
+  if (!box) {
+    return IPCZ_RESULT_INVALID_ARGUMENT;
+  }
+
+  if (flags & IPCZ_UNBOX_PEEK) {
+    *driver_handle = box->object().handle();
+    std::ignore = box.release();
+  } else {
+    *driver_handle = box->object().release();
+  }
+  return IPCZ_RESULT_OK;
 }
 
 constexpr IpczAPI kCurrentAPI = {
diff --git a/third_party/ipcz/src/api_test.cc b/third_party/ipcz/src/api_test.cc
index 8c2fa06..499c99b 100644
--- a/third_party/ipcz/src/api_test.cc
+++ b/third_party/ipcz/src/api_test.cc
@@ -34,11 +34,6 @@
   EXPECT_EQ(IPCZ_RESULT_UNIMPLEMENTED,
             ipcz().EndGet(IPCZ_INVALID_HANDLE, 0, 0, IPCZ_NO_FLAGS, nullptr,
                           nullptr));
-  EXPECT_EQ(IPCZ_RESULT_UNIMPLEMENTED,
-            ipcz().Box(IPCZ_INVALID_HANDLE, IPCZ_INVALID_DRIVER_HANDLE,
-                       IPCZ_NO_FLAGS, nullptr, nullptr));
-  EXPECT_EQ(IPCZ_RESULT_UNIMPLEMENTED,
-            ipcz().Unbox(IPCZ_INVALID_HANDLE, IPCZ_NO_FLAGS, nullptr, nullptr));
 }
 
 TEST_F(APITest, CloseInvalid) {
@@ -320,5 +315,69 @@
   CloseAll({a, b, node});
 }
 
+TEST_F(APITest, BoxInvalid) {
+  IpczDriverHandle transport0, transport1;
+  ASSERT_EQ(IPCZ_RESULT_OK,
+            kDefaultDriver.CreateTransports(
+                IPCZ_INVALID_DRIVER_HANDLE, IPCZ_INVALID_DRIVER_HANDLE,
+                IPCZ_NO_FLAGS, nullptr, &transport0, &transport1));
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            kDefaultDriver.Close(transport1, IPCZ_NO_FLAGS, nullptr));
+
+  IpczHandle node = CreateNode(kDefaultDriver);
+
+  IpczHandle box;
+
+  // Invalid node handle.
+  EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
+            ipcz().Box(IPCZ_INVALID_HANDLE, transport0, IPCZ_NO_FLAGS, nullptr,
+                       &box));
+
+  // Invalid driver handle.
+  EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
+            ipcz().Box(node, IPCZ_INVALID_DRIVER_HANDLE, IPCZ_NO_FLAGS, nullptr,
+                       &box));
+
+  // Null output handle.
+  EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
+            ipcz().Box(node, transport0, IPCZ_NO_FLAGS, nullptr, nullptr));
+
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            kDefaultDriver.Close(transport0, IPCZ_NO_FLAGS, nullptr));
+
+  Close(node);
+}
+
+TEST_F(APITest, UnboxInvalid) {
+  IpczDriverHandle transport0, transport1;
+  ASSERT_EQ(IPCZ_RESULT_OK,
+            kDefaultDriver.CreateTransports(
+                IPCZ_INVALID_DRIVER_HANDLE, IPCZ_INVALID_DRIVER_HANDLE,
+                IPCZ_NO_FLAGS, nullptr, &transport0, &transport1));
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            kDefaultDriver.Close(transport1, IPCZ_NO_FLAGS, nullptr));
+
+  IpczHandle node = CreateNode(kDefaultDriver);
+  IpczHandle box;
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Box(node, transport0, IPCZ_NO_FLAGS, nullptr, &box));
+
+  IpczDriverHandle handle;
+
+  // Null box handle.
+  EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
+            ipcz().Unbox(IPCZ_INVALID_HANDLE, IPCZ_NO_FLAGS, nullptr, &handle));
+
+  // Invalid box handle type (node instead of box).
+  EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
+            ipcz().Unbox(node, IPCZ_NO_FLAGS, nullptr, &handle));
+
+  // Null output handle.
+  EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
+            ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, nullptr));
+
+  CloseAll({box, node});
+}
+
 }  // namespace
 }  // namespace ipcz
diff --git a/third_party/ipcz/src/box_test.cc b/third_party/ipcz/src/box_test.cc
new file mode 100644
index 0000000..57ff3734
--- /dev/null
+++ b/third_party/ipcz/src/box_test.cc
@@ -0,0 +1,204 @@
+// 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 <cstring>
+#include <string_view>
+
+#include "ipcz/ipcz.h"
+#include "reference_drivers/blob.h"
+#include "reference_drivers/memory.h"
+#include "reference_drivers/os_handle.h"
+#include "test/multinode_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+namespace {
+
+using BoxTest = test::MultinodeTestWithDriver;
+
+using Blob = reference_drivers::Blob;
+
+// Creates a test driver Blob object with an inlined data payload and a shared
+// memory object with an embedded message.
+IpczDriverHandle CreateTestBlob(std::string_view inline_message,
+                                std::string_view shm_message) {
+  reference_drivers::Memory memory(shm_message.size());
+  auto mapping = memory.Map();
+  memcpy(mapping.base(), shm_message.data(), shm_message.size());
+  reference_drivers::OSHandle memory_handle = memory.TakeHandle();
+  return Blob::ReleaseAsHandle(
+      MakeRefCounted<Blob>(inline_message, absl::MakeSpan(&memory_handle, 1)));
+}
+
+bool BlobContentsMatch(IpczDriverHandle blob_handle,
+                       std::string_view expected_inline_message,
+                       std::string_view expected_shm_message) {
+  Ref<Blob> blob = Blob::TakeFromHandle(blob_handle);
+  if (expected_inline_message != blob->message()) {
+    return false;
+  }
+
+  ABSL_ASSERT(blob->handles().size() == 1);
+  ABSL_ASSERT(blob->handles()[0].is_valid());
+  reference_drivers::Memory memory = reference_drivers::Memory(
+      std::move(blob->handles()[0]), expected_shm_message.size());
+
+  auto new_mapping = memory.Map();
+  if (expected_shm_message != std::string_view(new_mapping.As<char>())) {
+    return false;
+  }
+
+  return true;
+}
+
+TEST_P(BoxTest, BoxAndUnbox) {
+  IpczHandle node = CreateBrokerNode();
+
+  constexpr const char kMessage[] = "Hello, world?";
+  IpczDriverHandle blob_handle =
+      Blob::ReleaseAsHandle(MakeRefCounted<Blob>(kMessage));
+
+  IpczHandle box;
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Box(node, blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
+
+  blob_handle = IPCZ_INVALID_DRIVER_HANDLE;
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
+
+  Ref<Blob> blob = Blob::TakeFromHandle(blob_handle);
+  EXPECT_EQ(kMessage, blob->message());
+
+  Close(node);
+}
+
+TEST_P(BoxTest, CloseBox) {
+  IpczHandle node = CreateBrokerNode();
+
+  Ref<Blob> blob = MakeRefCounted<Blob>("meh");
+  Ref<Blob::RefCountedFlag> destroyed = blob->destruction_flag_for_testing();
+  IpczDriverHandle blob_handle = Blob::ReleaseAsHandle(std::move(blob));
+
+  IpczHandle box;
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Box(node, blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
+
+  EXPECT_FALSE(destroyed->get());
+  EXPECT_EQ(IPCZ_RESULT_OK, ipcz().Close(box, IPCZ_NO_FLAGS, nullptr));
+  EXPECT_TRUE(destroyed->get());
+
+  Close(node);
+}
+
+TEST_P(BoxTest, Peek) {
+  IpczHandle node = CreateBrokerNode();
+
+  constexpr const char kMessage[] = "Hello, world?";
+  IpczDriverHandle blob_handle =
+      Blob::ReleaseAsHandle(MakeRefCounted<Blob>(kMessage));
+  IpczHandle box;
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Box(node, blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
+
+  blob_handle = IPCZ_INVALID_DRIVER_HANDLE;
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle));
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle));
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Unbox(box, IPCZ_UNBOX_PEEK, nullptr, &blob_handle));
+
+  Blob* blob = Blob::FromHandle(blob_handle);
+  EXPECT_EQ(kMessage, blob->message());
+
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
+
+  Ref<Blob> released_blob = Blob::TakeFromHandle(blob_handle);
+  EXPECT_EQ(blob, released_blob.get());
+
+  Close(node);
+}
+
+TEST_P(BoxTest, TransferBox) {
+  IpczHandle node0 = CreateBrokerNode();
+  IpczHandle node1 = CreateNonBrokerNode();
+  auto [a, b] = ConnectBrokerToNonBroker(node0, node1);
+
+  constexpr const char kMessage1[] = "Hello, world?";
+  constexpr const char kMessage2[] = "Hello, world!";
+  constexpr const char kMessage3[] = "Hello! World!";
+
+  IpczDriverHandle blob_handle = CreateTestBlob(kMessage1, kMessage2);
+  IpczHandle box;
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Box(node0, blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
+
+  Put(a, kMessage3, {&box, 1});
+
+  std::string message;
+  EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message, {&box, 1}));
+  EXPECT_EQ(kMessage3, message);
+
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
+  EXPECT_TRUE(BlobContentsMatch(blob_handle, kMessage1, kMessage2));
+
+  CloseAll({a, b, node1, node0});
+}
+
+TEST_P(BoxTest, TransferBoxBetweenNonBrokers) {
+  IpczHandle node0 = CreateBrokerNode();
+  IpczHandle node1 = CreateNonBrokerNode();
+  IpczHandle node2 = CreateNonBrokerNode();
+
+  auto [a, b] = ConnectBrokerToNonBroker(node0, node1);
+  auto [c, d] = ConnectBrokerToNonBroker(node0, node2);
+
+  // Create a new portal pair and send each end to one of the two non-brokers so
+  // they'll establish a direct link.
+  auto [e, f] = OpenPortals(node0);
+  Put(a, "", {&e, 1});
+  Put(c, "", {&f, 1});
+
+  std::string message;
+  EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(b, &message, {&e, 1}));
+  EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(d, &message, {&f, 1}));
+
+  constexpr const char kMessage1[] = "Hello, world?";
+  constexpr const char kMessage2[] = "Hello, world!";
+  constexpr const char kMessage3[] = "Hello! World!";
+
+  // Send messages end-to-end in each direction from one non-broker to the
+  // other, each carrying a box with some data and a driver object. This covers
+  // message relaying for multinode tests running with forced object brokering
+  // enabled.
+  constexpr size_t kNumIterations = 10;
+  for (size_t i = 0; i < kNumIterations; ++i) {
+    IpczDriverHandle blob_handle = CreateTestBlob(kMessage1, kMessage2);
+    IpczHandle box;
+    EXPECT_EQ(IPCZ_RESULT_OK,
+              ipcz().Box(node0, blob_handle, IPCZ_NO_FLAGS, nullptr, &box));
+
+    const IpczHandle sender = i % 2 ? e : f;
+    const IpczHandle receiver = i % 2 ? f : e;
+
+    Put(sender, kMessage3, {&box, 1});
+
+    EXPECT_EQ(IPCZ_RESULT_OK, WaitToGet(receiver, &message, {&box, 1}));
+    EXPECT_EQ(kMessage3, message);
+
+    EXPECT_EQ(IPCZ_RESULT_OK,
+              ipcz().Unbox(box, IPCZ_NO_FLAGS, nullptr, &blob_handle));
+    EXPECT_TRUE(BlobContentsMatch(blob_handle, kMessage1, kMessage2));
+  }
+
+  CloseAll({a, b, c, d, e, f, node2, node1, node0});
+}
+
+INSTANTIATE_MULTINODE_TEST_SUITE_P(BoxTest);
+
+}  // namespace
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/box.cc b/third_party/ipcz/src/ipcz/box.cc
new file mode 100644
index 0000000..ca4d19e9
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/box.cc
@@ -0,0 +1,27 @@
+// 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 "ipcz/box.h"
+
+#include <utility>
+
+#include "ipcz/ipcz.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz {
+
+Box::Box(DriverObject object) : object_(std::move(object)) {}
+
+Box::~Box() = default;
+
+IpczResult Box::Close() {
+  object_.reset();
+  return IPCZ_RESULT_OK;
+}
+
+bool Box::CanSendFrom(Portal& sender) {
+  return object_.is_valid() && object_.IsSerializable();
+}
+
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/box.h b/third_party/ipcz/src/ipcz/box.h
new file mode 100644
index 0000000..a861405e
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/box.h
@@ -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.
+
+#ifndef IPCZ_SRC_IPCZ_BOX_
+#define IPCZ_SRC_IPCZ_BOX_
+
+#include "ipcz/api_object.h"
+#include "ipcz/driver_object.h"
+
+namespace ipcz {
+
+// Generic handle wrapper around a DriverObject, allowing driver objects to be
+// passed wherever IpczHandles are accepted. More to the point, this allows
+// serializable driver objects to be transferred through portals.
+class Box : public APIObjectImpl<Box, APIObject::kBox> {
+ public:
+  explicit Box(DriverObject object);
+
+  DriverObject& object() { return object_; }
+
+  // APIObject:
+  IpczResult Close() override;
+  bool CanSendFrom(Portal& sender) override;
+
+ private:
+  ~Box() override;
+
+  DriverObject object_;
+};
+
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_IPCZ_BOX_
diff --git a/third_party/ipcz/src/ipcz/handle_type.h b/third_party/ipcz/src/ipcz/handle_type.h
index 78d0069a..a143c5e 100644
--- a/third_party/ipcz/src/ipcz/handle_type.h
+++ b/third_party/ipcz/src/ipcz/handle_type.h
@@ -17,7 +17,11 @@
   // parcel. It does not consume any other data, or any OS handles.
   kPortal = 0,
 
-  // TODO: Add enumerations for relayed and non-relayed boxes.
+  // A box handle consumes the next available element in the parcel's
+  // DriverObject array and wraps it as a Box object.
+  kBox = 1,
+
+  // TODO: Add an enumeration for relayed boxes.
 };
 
 }  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/node_link.cc b/third_party/ipcz/src/ipcz/node_link.cc
index d2185d5..2e3afdae 100644
--- a/third_party/ipcz/src/ipcz/node_link.cc
+++ b/third_party/ipcz/src/ipcz/node_link.cc
@@ -10,6 +10,7 @@
 #include <cstdint>
 #include <utility>
 
+#include "ipcz/box.h"
 #include "ipcz/ipcz.h"
 #include "ipcz/link_type.h"
 #include "ipcz/message.h"
@@ -149,6 +150,7 @@
       accept.GetArrayView<HandleType>(accept.params().handle_types);
   absl::Span<const RouterDescriptor> new_routers =
       accept.GetArrayView<RouterDescriptor>(accept.params().new_routers);
+  auto driver_objects = accept.driver_objects();
 
   // Note that on any validation failure below, we defer rejection at least
   // until any deserialized objects are stored in a new Parcel object. This
@@ -174,13 +176,23 @@
         break;
       }
 
+      case HandleType::kBox: {
+        if (driver_objects.empty()) {
+          return false;
+        }
+
+        objects[i] = MakeRefCounted<Box>(std::move(driver_objects[0]));
+        driver_objects.remove_prefix(1);
+        break;
+      }
+
       default:
         parcel_valid = false;
         break;
     }
   }
 
-  if (!new_routers.empty()) {
+  if (!new_routers.empty() || !driver_objects.empty()) {
     // There should be no unclaimed routers. If there are, it's a validation
     // failure.
     parcel_valid = false;
diff --git a/third_party/ipcz/src/ipcz/remote_router_link.cc b/third_party/ipcz/src/ipcz/remote_router_link.cc
index 60c43e0..9bd1ce4a 100644
--- a/third_party/ipcz/src/ipcz/remote_router_link.cc
+++ b/third_party/ipcz/src/ipcz/remote_router_link.cc
@@ -7,6 +7,7 @@
 #include <sstream>
 #include <utility>
 
+#include "ipcz/box.h"
 #include "ipcz/node_link.h"
 #include "ipcz/node_messages.h"
 #include "ipcz/portal.h"
@@ -50,10 +51,28 @@
   accept.params().sublink = sublink_;
   accept.params().sequence_number = parcel.sequence_number();
 
-  // TODO: Support attaching boxes as well as portals.
-  const size_t num_portals = objects.size();
+  size_t num_portals = 0;
+  absl::InlinedVector<DriverObject, 2> driver_objects;
   for (Ref<APIObject>& object : objects) {
-    ABSL_ASSERT(object->object_type() == APIObject::kPortal);
+    switch (object->object_type()) {
+      case APIObject::kPortal:
+        ++num_portals;
+        break;
+
+      case APIObject::kBox: {
+        Box* box = Box::FromObject(object.get());
+        ABSL_ASSERT(box);
+
+        // TODO: Support object relay when direct transmission is impossible.
+        ABSL_ASSERT(box->object().CanTransmitOn(*node_link()->transport()));
+
+        driver_objects.push_back(std::move(box->object()));
+        break;
+      }
+
+      default:
+        break;
+    }
   }
 
   // Allocate all the arrays in the message. Note that each allocation may
@@ -84,18 +103,28 @@
   for (size_t i = 0; i < objects.size(); ++i) {
     APIObject& object = *objects[i];
 
-    // TODO: Support attaching boxes as well as portals.
-    ABSL_ASSERT(object.object_type() == APIObject::kPortal);
-    handle_types[i] = HandleType::kPortal;
+    switch (object.object_type()) {
+      case APIObject::kPortal: {
+        handle_types[i] = HandleType::kPortal;
 
-    Ref<Router> router = Portal::FromObject(&object)->router();
-    router->SerializeNewRouter(*node_link(), new_routers[i]);
-    routers_to_proxy.push_back(std::move(router));
+        Ref<Router> router = Portal::FromObject(&object)->router();
+        router->SerializeNewRouter(*node_link(), new_routers[i]);
+        routers_to_proxy.push_back(std::move(router));
+        break;
+      }
+
+      case APIObject::kBox:
+        handle_types[i] = HandleType::kBox;
+        break;
+
+      default:
+        DLOG(FATAL) << "Attempted to transmit an invalid object.";
+        break;
+    }
   }
 
-  // TODO: When box attachments are supported, their driver objects will be
-  // appended here.
-  accept.params().driver_objects = {};
+  accept.params().driver_objects =
+      accept.AppendDriverObjects(absl::MakeSpan(driver_objects));
 
   DVLOG(4) << "Transmitting " << parcel.Describe() << " over " << Describe();
 
diff --git a/third_party/ipcz/src/reference_drivers/blob.cc b/third_party/ipcz/src/reference_drivers/blob.cc
new file mode 100644
index 0000000..fe184aa
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/blob.cc
@@ -0,0 +1,44 @@
+// 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 "reference_drivers/blob.h"
+
+#include <iterator>
+
+#include "util/ref_counted.h"
+
+namespace ipcz::reference_drivers {
+
+Blob::RefCountedFlag::RefCountedFlag() = default;
+
+Blob::RefCountedFlag::~RefCountedFlag() = default;
+
+Blob::Blob(std::string_view message, absl::Span<OSHandle> handles)
+    : message_(message),
+      handles_(std::move_iterator(handles.begin()),
+               std::move_iterator(handles.end())) {}
+
+Blob::~Blob() = default;
+
+IpczResult Blob::Close() {
+  destruction_flag_for_testing_->set(true);
+  return IPCZ_RESULT_OK;
+}
+
+// static
+Blob* Blob::FromHandle(IpczDriverHandle handle) {
+  Object* object = Object::FromHandle(handle);
+  if (!object || object->type() != kBlob) {
+    return nullptr;
+  }
+
+  return static_cast<Blob*>(object);
+}
+
+// static
+Ref<Blob> Blob::TakeFromHandle(IpczDriverHandle handle) {
+  return AdoptRef(FromHandle(handle));
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/blob.h b/third_party/ipcz/src/reference_drivers/blob.h
new file mode 100644
index 0000000..3bb9695
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/blob.h
@@ -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.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_BLOB_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_BLOB_H_
+
+#include <cstdint>
+#include <string_view>
+#include <vector>
+
+#include "reference_drivers/object.h"
+#include "reference_drivers/os_handle.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+#include "util/ref_counted.h"
+
+namespace ipcz::reference_drivers {
+
+// A driver-managed object which packages an arbitrary collection of string data
+// and OS handles. Blobs are serializable by both reference drivers and are used
+// to exercise driver object boxing in tests.
+//
+// Note that unlike the transport and memory objects defined by the reference
+// drivers, a blob is not a type of object known to ipcz. Instead it is used to
+// demonstrate how drivers can define arbitrary new types of transferrable
+// objects to extend ipcz.
+class Blob : public ObjectImpl<Blob, Object::kBlob> {
+ public:
+  class RefCountedFlag : public RefCounted {
+   public:
+    RefCountedFlag();
+
+    bool get() const { return flag_; }
+    void set(bool flag) { flag_ = flag; }
+
+   private:
+    ~RefCountedFlag() override;
+    bool flag_ = false;
+  };
+
+  explicit Blob(std::string_view message, absl::Span<OSHandle> handles = {});
+
+  // Object:
+  IpczResult Close() override;
+
+  std::string& message() { return message_; }
+  std::vector<OSHandle>& handles() { return handles_; }
+
+  const Ref<RefCountedFlag>& destruction_flag_for_testing() const {
+    return destruction_flag_for_testing_;
+  }
+
+  static Blob* FromHandle(IpczDriverHandle handle);
+  static Ref<Blob> TakeFromHandle(IpczDriverHandle handle);
+
+ protected:
+  ~Blob() override;
+
+ private:
+  std::string message_;
+  std::vector<OSHandle> handles_;
+  const Ref<RefCountedFlag> destruction_flag_for_testing_{
+      MakeRefCounted<RefCountedFlag>()};
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_BLOB_H_
diff --git a/third_party/ipcz/src/reference_drivers/object.h b/third_party/ipcz/src/reference_drivers/object.h
index 3afd7ccb..6561327 100644
--- a/third_party/ipcz/src/reference_drivers/object.h
+++ b/third_party/ipcz/src/reference_drivers/object.h
@@ -19,6 +19,11 @@
     kTransport,
     kMemory,
     kMapping,
+
+    // A non-standard driver object type, used to exercise more complex, custom
+    // driver object de/serialization via boxing and unboxing in tests. See the
+    // Blob definition in src/reference_drivers/blob.h.
+    kBlob,
   };
 
   explicit Object(Type type);
diff --git a/tools/cast3p/runtime.version b/tools/cast3p/runtime.version
index e8a3290..6cb7b8f1 100644
--- a/tools/cast3p/runtime.version
+++ b/tools/cast3p/runtime.version
@@ -1 +1 @@
-304092
+304500
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 3a4bbeb..43cf1e36 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,8 +35,8 @@
 # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
-CLANG_REVISION = 'llvmorg-15-init-10168-gc2a7904a'
-CLANG_SUB_REVISION = 2
+CLANG_REVISION = 'llvmorg-15-init-10717-ge00cbbec'
+CLANG_SUB_REVISION = 1
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
 RELEASE_VERSION = '15.0.0'
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7374cd51..17238198 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -17308,7 +17308,11 @@
   <int value="16"
       label="Custom property contains different value types in keyframe
              animation"/>
-  <int value="17" label="Compositor property animations have no effect"/>
+  <int value="17" label="Scroll timeline source is not composited"/>
+  <int value="18" label="Compositor property animations have no effect"/>
+  <int value="19" label="Animation affects an !important property"/>
+  <int value="20"
+      label="Target SVG element uses an individual transform property"/>
 </enum>
 
 <enum name="CompositorFrameSinkSubmitResult">
@@ -40128,6 +40132,7 @@
   <int value="4233" label="V8PaymentInstruments_Has_Method"/>
   <int value="4234" label="V8PaymentInstruments_Keys_Method"/>
   <int value="4235" label="V8PaymentInstruments_Set_Method"/>
+  <int value="4236" label="PerformanceMeasureFindExistingName"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -53953,19 +53958,24 @@
   <int value="-2084324285" label="iw"/>
   <int value="-2070680532" label="hi"/>
   <int value="-2070250592" label="zu"/>
+  <int value="-2061822420" label="gv"/>
   <int value="-2042178987" label="si"/>
   <int value="-2041104386" label="mi"/>
   <int value="-2014954614" label="so"/>
+  <int value="-2007848210" label="ay"/>
   <int value="-2005166181" label="ja-Latn"/>
+  <int value="-2001789870" label="sg"/>
   <int value="-1999171202" label="lv"/>
   <int value="-1998337622" label="es-CL"/>
   <int value="-1994870905" label="lt"/>
+  <int value="-1993564971" label="tzm"/>
   <int value="-1986814580" label="om"/>
   <int value="-1920649864" label="an"/>
   <int value="-1898669966" label="zh-CN"/>
   <int value="-1895779836" label="empty string"/>
   <int value="-1872667487" label="rm"/>
   <int value="-1855113037" label="vi"/>
+  <int value="-1835493637" label="ce"/>
   <int value="-1828586117" label="de"/>
   <int value="-1823968882" label="zh"/>
   <int value="-1797669833" label="it-IT"/>
@@ -53981,17 +53991,28 @@
   <int value="-1696927720" label="la"/>
   <int value="-1690250140" label="ja"/>
   <int value="-1683177340" label="qu"/>
+  <int value="-1679839472" label="bem"/>
   <int value="-1662603770" label="es-VE"/>
+  <int value="-1655091207" label="ee"/>
   <int value="-1625830957" label="ln"/>
+  <int value="-1621588434" label="bho"/>
   <int value="-1612285087" label="en-ZA"/>
+  <int value="-1589643944" label="sa"/>
+  <int value="-1578396049" label="ki"/>
   <int value="-1566592213" label="hi-Latn"/>
   <int value="-1558307126" label="es-CO"/>
+  <int value="-1553574832" label="saq"/>
   <int value="-1532548885" label="pa"/>
   <int value="-1515310228" label="ps"/>
+  <int value="-1481549061" label="dv"/>
   <int value="-1453785110" label="rw"/>
   <int value="-1452189651" label="es-CR"/>
+  <int value="-1403061996" label="mgo"/>
   <int value="-1346992143" label="nn"/>
+  <int value="-1344048927" label="naq"/>
+  <int value="-1301970539" label="dsb"/>
   <int value="-1284862637" label="ru"/>
+  <int value="-1283061184" label="bo"/>
   <int value="-1273588087" label="kn"/>
   <int value="-1242903543" label="es-UY"/>
   <int value="-1230368678" label="kok"/>
@@ -54001,15 +54022,21 @@
   <int value="-1117024738" label="zh-Latn"/>
   <int value="-1100371650" label="sn"/>
   <int value="-1086322569" label="es-AR"/>
+  <int value="-1069161714" label="yue"/>
   <int value="-1052948093" label="jw"/>
+  <int value="-1051941586" label="ff"/>
   <int value="-1048980531" label="ur"/>
   <int value="-1042449480" label="cy"/>
   <int value="-1033645590" label="fil"/>
   <int value="-1025520269" label="az"/>
+  <int value="-1022428397" label="kln"/>
   <int value="-1010619188" label="ru-Latn"/>
+  <int value="-1007452579" label="mni"/>
   <int value="-1000295094" label="no"/>
   <int value="-950444864" label="tk"/>
   <int value="-926982267" label="de-AT"/>
+  <int value="-912744476" label="lus"/>
+  <int value="-901764938" label="doi"/>
   <int value="-887258309" label="hu"/>
   <int value="-873154203" label="en-CA"/>
   <int value="-862298602" label="st"/>
@@ -54017,16 +54044,22 @@
   <int value="-846401046" label="chr"/>
   <int value="-838275151" label="xh"/>
   <int value="-812926878" label="ug"/>
+  <int value="-796128841" label="agq"/>
   <int value="-780984386" label="fr-CA"/>
+  <int value="-764225367" label="kri"/>
   <int value="-750267977" label="bg"/>
   <int value="-742603342" label="ca"/>
   <int value="-740941224" label="ku"/>
   <int value="-739605699" label="de-LI"/>
+  <int value="-736352432" label="guz"/>
+  <int value="-735144849" label="bas"/>
   <int value="-734032818" label="km"/>
   <int value="-717751759" label="lb"/>
+  <int value="-678521183" label="teo"/>
   <int value="-659007214" label="el"/>
   <int value="-647197922" label="ceb"/>
   <int value="-645438410" label="my"/>
+  <int value="-645318977" label="smn"/>
   <int value="-644560085" label="su"/>
   <int value="-598481752" label="et"/>
   <int value="-548175087" label="tt"/>
@@ -54043,15 +54076,20 @@
   <int value="-349113427" label="am"/>
   <int value="-342591258" label="sq"/>
   <int value="-302282167" label="ti"/>
+  <int value="-288195501" label="ts"/>
+  <int value="-259045309" label="lu"/>
   <int value="-226066958" label="bg-Latn"/>
   <int value="-219674420" label="ta"/>
   <int value="-183664425" label="pt-PT"/>
   <int value="-181870943" label="bn"/>
+  <int value="-119774173" label="dav"/>
   <int value="-110306666" label="sr"/>
   <int value="-91748674" label="tn"/>
+  <int value="-91654905" label="luo"/>
   <int value="-78164291" label="kk"/>
   <int value="-74147910" label="en"/>
   <int value="-35182995" label="fi"/>
+  <int value="-27733173" label="mfe"/>
   <int value="-5034744" label="ny"/>
   <int value="42532257" label="id"/>
   <int value="64053359" label="eu"/>
@@ -54063,64 +54101,98 @@
   <int value="142304335" label="gn"/>
   <int value="142313505" label="ro"/>
   <int value="145030010" label="gu"/>
+  <int value="158276939" label="gsw"/>
   <int value="162326141" label="sl"/>
   <int value="183952636" label="mg"/>
   <int value="191168946" label="mk"/>
+  <int value="264824286" label="ksh"/>
   <int value="350748440" label="und"/>
   <int value="357286655" label="af"/>
+  <int value="443574411" label="dz"/>
   <int value="461111861" label="mt"/>
   <int value="462869158" label="hy"/>
+  <int value="469297921" label="khq"/>
   <int value="470982931" label="sv"/>
   <int value="522435458" label="hr"/>
   <int value="526531379" label="ml"/>
   <int value="538270200" label="uk"/>
   <int value="549800655" label="de-CH"/>
+  <int value="563029930" label="pcm"/>
   <int value="584710092" label="nb"/>
   <int value="596295208" label="bs"/>
+  <int value="622642106" label="ilo"/>
   <int value="632444664" label="yue-HK"/>
+  <int value="652001481" label="fur"/>
   <int value="673577439" label="cs"/>
   <int value="720107264" label="es-PE"/>
   <int value="729519028" label="or"/>
+  <int value="729888861" label="hsb"/>
   <int value="796588925" label="yo"/>
   <int value="804120371" label="jv"/>
+  <int value="806431033" label="mgh"/>
+  <int value="842939712" label="se"/>
+  <int value="844238123" label="kl"/>
+  <int value="861252498" label="seh"/>
   <int value="873647701" label="th"/>
   <int value="910795716" label="ne"/>
   <int value="911100675" label="de-DE"/>
+  <int value="911759580" label="kab"/>
+  <int value="919128771" label="dua"/>
   <int value="925733725" label="ms"/>
   <int value="1062172280" label="en-IN"/>
+  <int value="1084091282" label="sah"/>
   <int value="1092864716" label="ht"/>
   <int value="1110169461" label="hmn"/>
+  <int value="1117613525" label="ewo"/>
   <int value="1119752109" label="te"/>
+  <int value="1129132659" label="kea"/>
   <int value="1140816756" label="ar"/>
   <int value="1152813109" label="to"/>
   <int value="1166708194" label="is"/>
   <int value="1196096274" label="es-US"/>
   <int value="1199169847" label="en-AU"/>
   <int value="1214473765" label="unknown"/>
+  <int value="1222328168" label="rn"/>
+  <int value="1225967405" label="lg"/>
+  <int value="1227107723" label="nyn"/>
   <int value="1228519432" label="fo"/>
   <int value="1282439493" label="es-MX"/>
   <int value="1311313702" label="pt"/>
   <int value="1312638242" label="pl"/>
+  <int value="1327296767" label="gom"/>
   <int value="1437205305" label="uz"/>
+  <int value="1478556106" label="bm"/>
   <int value="1482920614" label="yi"/>
   <int value="1483760478" label="tg"/>
   <int value="1552733612" label="da"/>
+  <int value="1587354509" label="mzn"/>
   <int value="1628170778" label="pt-BR"/>
+  <int value="1632012012" label="zgh"/>
   <int value="1638257274" label="sk"/>
+  <int value="1650591828" label="kw"/>
   <int value="1670494558" label="ko"/>
   <int value="1704087523" label="ky"/>
   <int value="1704315002" label="fr"/>
   <int value="1708437566" label="it-CH"/>
   <int value="1717583602" label="co"/>
   <int value="1724977738" label="ia"/>
+  <int value="1747828010" label="os"/>
   <int value="1754979806" label="fy"/>
+  <int value="1837633193" label="mai"/>
   <int value="1853848431" label="tr"/>
+  <int value="1865219403" label="nd"/>
+  <int value="1866346468" label="in"/>
+  <int value="1883413742" label="kam"/>
   <int value="1884922344" label="ckb"/>
   <int value="1921155040" label="br"/>
+  <int value="1958157353" label="nso"/>
   <int value="1988450082" label="tw"/>
+  <int value="1995581006" label="kde"/>
   <int value="2039992295" label="ig"/>
+  <int value="2072277196" label="ak"/>
   <int value="2077417554" label="fr-CH"/>
   <int value="2078974161" label="ast"/>
+  <int value="2081074480" label="ii"/>
   <int value="2087142539" label="mn"/>
   <int value="2094171128" label="mo"/>
   <int value="2098003573" label="as"/>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 4b765040..963d81ec 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -353,7 +353,8 @@
     <variant name="StickyApp"
         summary="'Sticky' ARC app in a system/vendor image. This is always 0
                  for opt-out users."/>
-    <variant name="UnknownApp" summary="ARC app whose type is unknown to Ash."/>
+    <variant name="UnknownApp"
+        summary="[Deprecated in M104] ARC app whose type is unknown to Ash."/>
   </token>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml
index 8dfefb5..b3bc84dc 100644
--- a/tools/metrics/histograms/metadata/language/histograms.xml
+++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -148,6 +148,9 @@
 
 <histogram name="LanguageSettings.AppLanguagePrompt.OpenDuration.{ActionType}"
     units="ms" expires_after="2022-06-30">
+  <obsolete>
+    Deleted May 2022
+  </obsolete>
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index d65abda0..02cd1146 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -84,7 +84,7 @@
 </histogram>
 
 <histogram name="Quota.EvictedBucketDaysSinceAccess" units="units"
-    expires_after="2022-06-26">
+    expires_after="2023-05-18">
   <owner>ayui@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
diff --git a/ui/shell_dialogs/BUILD.gn b/ui/shell_dialogs/BUILD.gn
index e1b3041..192251ba 100644
--- a/ui/shell_dialogs/BUILD.gn
+++ b/ui/shell_dialogs/BUILD.gn
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/chromeos/ui_mode.gni")
-import("//build/config/linux/gtk/gtk.gni")
+import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//testing/test.gni")
 
@@ -41,23 +41,28 @@
     "//url",
   ]
 
-  if (is_chromeos || (is_linux && !use_gtk)) {
+  if (is_chromeos || is_chromecast) {
     sources += [ "shell_dialog_stub.cc" ]
-  } else if (is_linux) {
+  }
+
+  if (is_linux && !is_chromecast) {
     sources += [
       "select_file_dialog_linux.cc",
       "select_file_dialog_linux.h",
       "select_file_dialog_linux_kde.cc",
       "select_file_dialog_linux_kde.h",
-      "select_file_dialog_linux_portal.cc",
-      "select_file_dialog_linux_portal.h",
       "shell_dialog_linux.cc",
       "shell_dialog_linux.h",
     ]
-    deps += [
-      "//components/dbus/thread_linux",
-      "//dbus",
-    ]
+    deps += [ "//components/dbus/thread_linux" ]
+    if (use_dbus) {
+      defines += [ "USE_DBUS" ]
+      sources += [
+        "select_file_dialog_linux_portal.cc",
+        "select_file_dialog_linux_portal.h",
+      ]
+      deps += [ "//dbus" ]
+    }
   }
 
   if (is_mac) {
diff --git a/ui/shell_dialogs/shell_dialog_linux.cc b/ui/shell_dialogs/shell_dialog_linux.cc
index 0f5fa2e5..078fd3b 100644
--- a/ui/shell_dialogs/shell_dialog_linux.cc
+++ b/ui/shell_dialogs/shell_dialog_linux.cc
@@ -10,14 +10,26 @@
 #include "build/chromeos_buildflags.h"
 #include "ui/shell_dialogs/select_file_dialog_linux.h"
 #include "ui/shell_dialogs/select_file_dialog_linux_kde.h"
-#include "ui/shell_dialogs/select_file_dialog_linux_portal.h"
 #include "ui/shell_dialogs/select_file_policy.h"
 
+#if defined(USE_DBUS)
+#include "ui/shell_dialogs/select_file_dialog_linux_portal.h"
+#endif
+
+namespace ui {
+
 namespace {
 
-ui::ShellDialogLinux* g_shell_dialog_linux = nullptr;
+ShellDialogLinux* g_shell_dialog_linux = nullptr;
 
-enum FileDialogChoice { kUnknown, kToolkit, kKde, kPortal };
+enum FileDialogChoice {
+  kUnknown,
+  kToolkit,
+  kKde,
+#if defined(USE_DBUS)
+  kPortal,
+#endif
+};
 
 FileDialogChoice dialog_choice_ = kUnknown;
 
@@ -26,14 +38,43 @@
   return *version;
 }
 
-}  // namespace
+FileDialogChoice GetFileDialogChoice() {
+#if defined(USE_DBUS)
+  // Check to see if the portal is available.
+  if (SelectFileDialogLinuxPortal::IsPortalAvailable())
+    return kPortal;
+  // Make sure to kill the portal connection.
+  SelectFileDialogLinuxPortal::DestroyPortalConnection();
+#endif
 
-namespace ui {
+  // Check to see if KDE is the desktop environment.
+  std::unique_ptr<base::Environment> env(base::Environment::Create());
+  base::nix::DesktopEnvironment desktop =
+      base::nix::GetDesktopEnvironment(env.get());
+  if (desktop == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
+      desktop == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
+      desktop == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
+    // Check to see if the user dislikes the KDE file dialog.
+    if (!env->HasVar("NO_CHROME_KDE_FILE_DIALOG")) {
+      // Check to see if the KDE dialog works.
+      if (SelectFileDialogLinux::CheckKDEDialogWorksOnUIThread(
+              KDialogVersion())) {
+        return kKde;
+      }
+    }
+  }
+
+  return kToolkit;
+}
+
+}  // namespace
 
 ShellDialogLinux::ShellDialogLinux() = default;
 
 ShellDialogLinux::~ShellDialogLinux() {
+#if defined(USE_DBUS)
   SelectFileDialogLinuxPortal::DestroyPortalConnection();
+#endif
 }
 
 void ShellDialogLinux::SetInstance(ShellDialogLinux* instance) {
@@ -45,50 +86,27 @@
 }
 
 void ShellDialogLinux::Initialize() {
+#if defined(USE_DBUS)
   SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground();
+#endif
 }
 
 SelectFileDialog* CreateSelectFileDialog(
     SelectFileDialog::Listener* listener,
     std::unique_ptr<SelectFilePolicy> policy) {
-  if (dialog_choice_ == kUnknown) {
-    // Start out assuming we are going to use dialogs from the toolkit.
-    dialog_choice_ = kToolkit;
+  if (dialog_choice_ == kUnknown)
+    dialog_choice_ = GetFileDialogChoice();
 
-    // Check to see if the portal is available.
-    if (SelectFileDialogLinuxPortal::IsPortalAvailable()) {
-      dialog_choice_ = kPortal;
-    } else {
-      // Make sure to kill the portal connection.
-      SelectFileDialogLinuxPortal::DestroyPortalConnection();
-
-      // Check to see if KDE is the desktop environment.
-      std::unique_ptr<base::Environment> env(base::Environment::Create());
-      base::nix::DesktopEnvironment desktop =
-          base::nix::GetDesktopEnvironment(env.get());
-      if (desktop == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
-          desktop == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
-          desktop == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
-        // Check to see if the user dislikes the KDE file dialog.
-        if (!env->HasVar("NO_CHROME_KDE_FILE_DIALOG")) {
-          // Check to see if the KDE dialog works.
-          if (SelectFileDialogLinux::CheckKDEDialogWorksOnUIThread(
-                  KDialogVersion())) {
-            dialog_choice_ = kKde;
-          }
-        }
-      }
-    }
-  }
-
-  const ui::ShellDialogLinux* shell_dialogs = ui::ShellDialogLinux::instance();
+  const ShellDialogLinux* shell_dialogs = ShellDialogLinux::instance();
   switch (dialog_choice_) {
     case kToolkit:
       if (!shell_dialogs)
         break;
       return shell_dialogs->CreateSelectFileDialog(listener, std::move(policy));
+#if defined(USE_DBUS)
     case kPortal:
       return new SelectFileDialogLinuxPortal(listener, std::move(policy));
+#endif
     case kKde: {
       std::unique_ptr<base::Environment> env(base::Environment::Create());
       base::nix::DesktopEnvironment desktop =
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 28d01a9..7ee93666 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -522,7 +522,7 @@
 }
 
 bool DesktopWindowTreeHostWin::IsFullscreen() const {
-  return message_handler_->fullscreen_handler()->fullscreen();
+  return message_handler_->IsFullscreen();
 }
 
 void DesktopWindowTreeHostWin::SetOpacity(float opacity) {
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 15e33123..17e7850 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -540,6 +540,11 @@
 }
 
 gfx::Rect HWNDMessageHandler::GetRestoredBounds() const {
+  // Headless mode window never goes fullscreen, so just return an empty
+  // rectangle here.
+  if (IsHeadless())
+    return gfx::Rect();
+
   // If we're in fullscreen mode, we've changed the normal bounds to the monitor
   // rect, so return the saved bounds instead.
   if (IsFullscreen())
@@ -809,7 +814,10 @@
 }
 
 bool HWNDMessageHandler::IsFullscreen() const {
-  return fullscreen_handler_->fullscreen();
+  // In headless mode report the requested window state instead of the actual
+  // one.
+  return IsHeadless() ? headless_window_fullscreen_state_
+                      : fullscreen_handler_->fullscreen();
 }
 
 bool HWNDMessageHandler::IsAlwaysOnTop() const {
@@ -932,9 +940,12 @@
 }
 
 void HWNDMessageHandler::SetFullscreen(bool fullscreen) {
-  // Avoid setting fullscreen mode when in headless mode.
-  if (IsHeadless())
+  // Avoid setting fullscreen mode when in headless mode, but keep track
+  // of the requested state for IsFullscreen() to report.
+  if (IsHeadless()) {
+    headless_window_fullscreen_state_ = fullscreen;
     return;
+  }
 
   background_fullscreen_hack_ = false;
   auto ref = msg_handler_weak_factory_.GetWeakPtr();
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h
index 3779c50..a46da3e 100644
--- a/ui/views/win/hwnd_message_handler.h
+++ b/ui/views/win/hwnd_message_handler.h
@@ -811,10 +811,11 @@
   // call HandleWindowMinimizedOrRestored() when we get a WM_ACTIVATE message.
   bool notify_restore_on_activate_ = false;
 
-  // This tracks headless window visibility state. In headless mode
-  // the platform window is always hidden, so we use this boolean
-  // to track the window's requested visibility state.
+  // These track headless window visibility and fullscreen states. In headless
+  // mode the platform window is never made visible or fullscreen, so we use
+  // these booleans to track the requested window state.
   bool headless_window_visibility_state_ = false;
+  bool headless_window_fullscreen_state_ = false;
 
   // This is a map of the HMONITOR to full screeen window instance. It is safe
   // to keep a raw pointer to the HWNDMessageHandler instance as we track the
diff --git a/url/url_util.cc b/url/url_util.cc
index 0fe4e30..c9b72d3 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -151,9 +151,9 @@
                                      const char* compare_to) {
   if (!component.is_nonempty())
     return compare_to[0] == 0;  // When component is empty, match empty scheme.
-  return base::LowerCaseEqualsASCII(
-      typename CharToStringPiece<CHAR>::Piece(
-          &spec[component.begin], component.len),
+  return base::EqualsCaseInsensitiveASCII(
+      typename CharToStringPiece<CHAR>::Piece(&spec[component.begin],
+                                              component.len),
       compare_to);
 }
 
@@ -168,9 +168,10 @@
     return false;  // Empty or invalid schemes are non-standard.
 
   for (const SchemeWithType& scheme_with_type : schemes) {
-    if (base::LowerCaseEqualsASCII(typename CharToStringPiece<CHAR>::Piece(
-                                       &spec[scheme.begin], scheme.len),
-                                   scheme_with_type.scheme)) {
+    if (base::EqualsCaseInsensitiveASCII(
+            typename CharToStringPiece<CHAR>::Piece(&spec[scheme.begin],
+                                                    scheme.len),
+            scheme_with_type.scheme)) {
       *type = scheme_with_type.type;
       return true;
     }