diff --git a/DEPS b/DEPS
index df23db5..14be9b1 100644
--- a/DEPS
+++ b/DEPS
@@ -38,7 +38,7 @@
   'checkout_android',
   'checkout_android_prebuilts_build_tools',
   'checkout_android_native_support',
-  'checkout_google_benchmark',
+  'checkout_google_benchmark', # TODO(https://crbug.com/1404759): Remove.
   'checkout_ios_webkit',
   'checkout_nacl',
   'checkout_openxr',
@@ -73,6 +73,13 @@
   # TODO(ehmaldonado): Remove this once the bug in gclient is fixed.
   'checkout_fuchsia': False,
 
+  # For code related to internal Fuchsia images.
+  'checkout_fuchsia_internal': False,
+
+  # Fetches the internal Fuchsia SDK boot images, with the images in a
+  # comma-separated list.
+  'checkout_fuchsia_internal_images': '',
+
   # Used for downloading the Fuchsia SDK without running hooks.
   'checkout_fuchsia_no_hooks': False,
 
@@ -86,6 +93,9 @@
   # By default, do not check out Cast3P.
   'checkout_cast3p': False,
 
+  # Check out all Chrome Cleaner deps. Set on the Chrome Cleaner builders.
+  'checkout_chrome_cleaner_internal': False,
+
   # By default, do not check out Chromium autofill captured sites test
   # dependencies. These dependencies include very large numbers of very
   # large web capture files. Captured sites test dependencies are also
@@ -98,10 +108,12 @@
   # restricted to Googlers only.
   'checkout_chromium_password_manager_test_dependencies': False,
 
-  # By default, do not check out Google Benchmark. The library is only used by a
-  # few specialized benchmarks that most developers do not interact with. Will
-  # be overridden by gclient variables.
-  'checkout_google_benchmark': False,
+  # Checkout fuzz archive. Should not need in builders.
+  'checkout_clusterfuzz_data': False,
+
+  # Always check out Google Benchmark.
+  # TODO(https://crbug.com/1404759): Remove.
+  'checkout_google_benchmark': True,
 
   # By default, checkout JavaScript coverage node modules. These packages
   # are used to post-process raw v8 coverage reports into IstanbulJS compliant
@@ -116,6 +128,9 @@
   # custom_vars.
   'checkout_src_internal': False,
 
+  # Checkout SODA (Speech On-Device API)
+  'checkout_soda': False,
+
   # Fetch the additional packages and files needed to run all of the
   # telemetry tests. This is false by default as some stuff is only
   # privately accessible.
@@ -130,6 +145,9 @@
   # the gn arg 'use_clang_coverage').
   'checkout_clang_coverage_tools': False,
 
+  # For super-internal deps. Set by the official builders.
+  'checkout_google_internal': False,
+
   # Fetch the pgo profiles to optimize official builds.
   'checkout_pgo_profiles': False,
 
@@ -305,7 +323,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '72c62c68dcaa1aa05ee4fae42a03296e9b49481f',
+  'skia_revision': '40065b4358653123705b32691b806ff0619d75fb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -313,7 +331,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '0cb0907436af1d915512bc18713a703085e152c0',
+  'angle_revision': '475025e39ff0b6e2b3d4fdab7f0e53245a5da1bc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -332,7 +350,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:12.20230308.2.1',
+  'fuchsia_version': 'version:12.20230308.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.
@@ -392,7 +410,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '94e4631b33cfe06e099b2fc38fccc2bfde5394c1',
+  'devtools_frontend_revision': '90e0ac67773d7dfeebfc383a2e1cdba97ecd34f3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -432,11 +450,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '03ee23ea9efa2c4d053cb2c23152fc23553a3eaf',
+  'dawn_revision': '9543f74739118a853dd5e5a46297f5442c3352f8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '0e869c4c83bad1904d55d2f33825dbc3c4c5fbe5',
+  'quiche_revision': '01ee3222823e9cbbde4eed546ced2ae2b15f440b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -1127,10 +1145,8 @@
       'condition': 'checkout_src_internal and checkout_chromeos',
   },
 
-  'src/third_party/google_benchmark/src': {
-    'url': Var('chromium_git') + '/external/github.com/google/benchmark.git' + '@' + 'f730846b0a3c0dc0699978846fb14ffb2fad0bdc',
-    'condition': 'checkout_google_benchmark',
-  },
+  'src/third_party/google_benchmark/src':
+    Var('chromium_git') + '/external/github.com/google/benchmark.git' + '@' + 'f730846b0a3c0dc0699978846fb14ffb2fad0bdc',
 
   'src/third_party/boringssl/src':
     Var('boringssl_git') + '/boringssl.git' + '@' +  Var('boringssl_revision'),
@@ -1222,13 +1238,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '638956ac7dfc1422188e3add9f6d5d9fe778785b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2f5bdd5aead47a82c72a7d25f2655054b6dd1c49',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'f83d11816bbccb73095fa2b478f248ad2aed3a63',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'f9472c1bcb5b4823ce42b8cbe83a806086cbdaaa',
     'condition': 'checkout_src_internal',
   },
 
@@ -1885,7 +1901,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '6c8361e98f1daba65902f5e2fc1297893ac14b67',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'edca73c88440c8f603565d46939b2a5199fab2b3',
+    Var('webrtc_git') + '/src.git' + '@' + 'd4f2ccb7c0a3d1a652fe3494ce5c8527f1879fc8',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1955,7 +1971,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@6036b3eeda9102f2d91081a74c6706fe25211cb7',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@c385ab06a660c50d5898dc6ea0fd17c44166cd49',
     'condition': 'checkout_src_internal',
   },
 
@@ -3909,6 +3925,469 @@
     'dep_type': 'cipd',
     'condition': 'checkout_win and checkout_bazel',
   },
+
+  # Repositories from src_internal
+  'src/build/fuchsia/internal': {
+      'url': Var('chrome_git') + '/fuchsia/build.git' + '@' +
+        '16da074bda38d989dbcbee0c7c75e2aa83783bb2',
+      'condition': 'checkout_src_internal and checkout_fuchsia_internal',
+    },
+
+  'src/chrome/app/theme/default_100_percent/google_chrome': {
+      'url':
+        Var('chrome_git') + '/chrome/theme/default_100_percent/google_chrome.git' + '@' +
+        '5e12619276fa0fd41dae5ebd40b807a6c32930c1',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/app/theme/default_200_percent/google_chrome': {
+      'url':
+        Var('chrome_git') + '/chrome/theme/default_200_percent/google_chrome.git' + '@' +
+        'db0cce3e7bfc7489c7213295d56e1c7611f9c43f',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/app/theme/google_chrome': {
+      'url':
+        Var('chrome_git') + '/chrome/theme/google_chrome.git' + '@' +
+        '4155da06db34b5066085eb846e8a10a81528089b',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/enterprise/connectors/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/browser/enterprise/connectors/internal.git' + '@' +
+        '7fd7c8dd496740724d0024408ae7a96298e34aa2',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/google/linkdoctor_internal': {
+      'url':
+        Var('chrome_git') + '/chrome/linkdoctor.git' + '@' +
+        'fe28a8f90c5471f20f8fee9ff7f6c6f8b8d02bed', # from svn revision 32577
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/browser_internal.git' + '@' +
+        'd3a55d714679c55a73ff96a7ea77493b326dae91',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/media/engagement_internal': {
+      'url':
+        Var('chrome_git') + '/chrome/browser/media/engagement_internal.git' + '@' +
+        '14b00ddbb904612ec8805f00718ae3f95c02a076',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/resources/chromeos/quickoffice': {
+      'url': Var('chrome_git') + '/quickoffice/crx.git' + '@' +
+      'b469688ba88433c572ff6ebcc215a67889fadb36',
+      'condition': 'checkout_src_internal and (checkout_chromeos or checkout_linux)',
+    },
+
+  'src/chrome/browser/resources/media_router_internal': {
+      'url':
+        Var('chrome_git') + '/cloudview-team/media-router.git' + '@' +
+        'a4cb277506253e29d683eb7e6873021347191646',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/resources/settings_internal': {
+      'url':
+        Var('chrome_git') + '/chrome/browser/resources/settings_internal.git' + '@' +
+        '5d6316b2434986e6b073e1d24585578bb27da451', # from svn revision 41419
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/spellchecker/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/spellchecker/internal.git' + '@' +
+        'a22002a5b3cf7c6b872b25712af97a5664a350e2', # from svn revision 24388
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/browser/ui/media_router/internal': {
+      'url':
+        Var('chrome_git') + '/cloudview-team/media-router/internal.git' + '@' +
+        '03f38788131e24700362c671f7639b2188b14923',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/chrome_cleaner/internal': {
+      'url': Var('chrome_git') + '/protector/foil.git' + '@' +
+        'f7839edf1403546f1ebc5bff62319d32e21529ab',
+      'condition': 'checkout_src_internal and checkout_chrome_cleaner_internal',
+    },
+
+  # Installer bits used only by Mac, but mapped for all OSes to ease source
+  # grepping.
+  'src/chrome/installer/mac/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/installer/mac/internal.git' + '@' +
+        'e8988b6dc1381cd1b5a033b6c4be771a05dc4f4a',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/data/firefox3_profile/searchplugins': {
+      'url':
+        Var('chrome_git') + '/chrome/data/osdd/firefox3_profile_searchplugins.git' + '@' +
+        '6cf09b86fb9d058453e7d05978ff8e91b5e8e749',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/data/firefox3_searchplugins': {
+      'url':
+        Var('chrome_git') + '/chrome/data/osdd/firefox3_searchplugins.git' + '@' +
+        '490580801915834d72dd8a1e943924c35df45673',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/data/gpu/vt': {
+      'url':
+        Var('chrome_git') + '/chrome/data/vectortown_endurance/vectortownstatic-20121022.git' + '@' +
+        'c34f30f909a414d378a1678eba921e58940708c4',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/data/perf/frame_rate/private': {
+      'url':
+        Var('chrome_git') + '/chrome/data/frame_rate_tests.git' + '@' +
+        '6394c925a272b92a4e9e16d929af049b7aa6e4f8',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/data/perf/private': {
+      'url':
+        Var('chrome_git') + '/chrome/data/perf_tests.git' + '@' +
+        '6f3e320b1fa10910eb1dcbad36afdd1ad00b2c5a',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/data/pdf_private': {
+      'url':
+        Var('chrome_git') + '/chrome/data/pdf_private.git' + '@' +
+        '23b64c03647779d193ee8ccb3f2a1a5560da9c94',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/media_router/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/test/media_router/internal.git' + '@' +
+        '3fd9af874c74874043521709c4f2ee968a0fe94a',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/test/python_tests': {
+      'url':
+        Var('chrome_git') + '/chrome/test/python_tests.git' + '@' +
+        '644bd7703b85f148564cc4038aada81f3a616d8a',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/chrome/tools/memory': {
+      'url': Var('chrome_git') + '/chrome/tools/memory.git' + '@' +
+        '3c9359382236f6d57c91505234a2bc7fd635ba6c',
+      'condition': 'checkout_src_internal and checkout_win',
+    },
+
+  'src/chromeos/assistant/internal': {
+      'url': Var('chrome_git') + '/chrome/assistant.git' + '@' +
+        '7fd2cf66416f39e40d3846bf3a00b527e7738507',
+      'condition': 'checkout_src_internal and checkout_chromeos',
+    },
+
+  'src/chrome/services/speech/internal': {
+      'url': Var('chrome_git') + '/chromeos/speech.git' + '@' + '8d46b1cb1312a0933799e37bde27e6747db8fcc2',
+      'condition': 'checkout_src_internal and checkout_chromeos',
+   },
+
+  'src/components/autofill/core/browser/form_parsing/internal_resources': {
+      'url':
+        Var('chrome_git') + '/chrome/components/autofill_regex_patterns.git' + '@' +
+        '245b93551651baa7fb12a64c6f0a9f8a9dc00657',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/components/ntp_tiles/resources/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/components/ntp_tiles/resources.git' + '@' +
+        '82bb45cb66582350f17bf9ef55c33acbccfeab9c',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/components/optimization_guide/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
+        '8de202c36f052c93df968f81c614c3638af80684',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/components/resources/default_100_percent/google_chrome': {
+      'url':
+        Var('chrome_git') + '/chrome/components/default_100_percent/google_chrome.git' + '@' +
+        '0e25f1d054b3bab0444e0f04c5f9b07c5f5c858b',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/components/resources/default_200_percent/google_chrome': {
+      'url':
+        Var('chrome_git') + '/chrome/components/default_200_percent/google_chrome.git' + '@' +
+        '66e7e3c423b0a795a3706ab63d3e9adfd8c5a646',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/components/resources/default_300_percent/google_chrome': {
+      'url':
+        Var('chrome_git') + '/chrome/components/default_300_percent/google_chrome.git' + '@' +
+        '91bb3da51c938685b899b3bd8a5ba0dda86bb861',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/components/site_isolation/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/components/site_isolation.git' + '@' +
+        'e0d8a7769c1daabb974bf0d229970534a0aede77',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/components/vector_icons/google_chrome': {
+      'url':
+        Var('chrome_git') + '/chrome/vector_icons/google_chrome.git' + '@' +
+        '7652f62446ba634c84d8e04f55ef82d1ed50b692',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/content/test/data/plugin': {
+      'url':
+        Var('chrome_git') + '/chrome/data/chrome_plugin_tests.git' + '@' +
+        '3e80d4d08f5421d6bc9340964834ebc903a318aa',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/google_apis/internal': {
+      'url':
+        Var('chrome_git') + '/chrome/google_apis/internal.git' + '@' +
+        '9161026654df3107269a6d1e4cd9bae8b5589a87',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/ios_internal':  {
+      'url': '{chrome_git}/chrome/ios_internal.git' + '@' +
+        'edb654f7beb07763fd44b0b70bea81c61d06d083',
+      'condition': 'checkout_src_internal and checkout_ios',
+    },
+
+  'src/remoting/android/internal': {
+      'url': Var('chrome_git') + '/chrome/remoting/android/internal.git' + '@' +
+        '6b9f36fb32ddb796f16cc5b5830f58ec1944966a',
+      'condition': 'checkout_src_internal and checkout_android',
+    },
+
+  'src/remoting/host/installer/linux/internal': {
+      'url': Var('chrome_git') + '/chrome/remoting/host/installer/linux/internal.git' + '@' +
+        'e190816de75b14897f1af785eb37d237750460e2',
+      'condition': 'checkout_src_internal and checkout_linux',
+    },
+
+  'src/remoting/internal': {
+      'url': Var('chrome_git') + '/chrome/app-streaming.git' + '@' +
+        'd4e99ab2c43b3301e1d3ba88a7cfccd93511c8b0',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/remoting/test/internal': {
+      'url': Var('chrome_git') + '/chrome/remoting/test/internal.git' + '@' +
+        '34ff3657e2176fc48a57fad555b076a50a409de6',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/remoting/tools/internal': {
+      'url': Var('chrome_git') + '/chrome/remoting/tools/internal.git' + '@' +
+        'acfed9c3a363694f37aadfb5cda4c31109661eb8',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/remoting/webapp/app_remoting/internal': {
+      'url': Var('chrome_git') + '/chrome/remoting/webapp/app_remoting/internal.git' + '@' +
+        '5ad5339af97c3bd193d595de03f34282491ce495',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/signing_keys': {
+      'url': Var('chrome_git') + '/clank/apptestkey.git' + '@' +
+        '5138e684915721cbccbb487ec0764ed05650fcd0',
+      'condition': 'checkout_src_internal and checkout_android and checkout_google_internal',
+    },
+
+  'src/skia/tools/clusterfuzz-data':{
+      'url': Var('chrome_git') + '/chrome/tools/clusterfuzz-data.git' + '@' +
+        'fa1fc4acacddd8d655cfca0bcadef5f7e2259bed',
+      'condition': 'checkout_src_internal and checkout_clusterfuzz_data',
+    },
+
+  'src/third_party/amd': {
+      'url': Var('chrome_git') + '/chrome/deps/amd.git' + '@' +
+        'cbd9811acb6d09f19b880fdbc6f0fc62901c9a5c',
+      'condition': 'checkout_src_internal and checkout_win',
+    },
+
+  'src/third_party/android_tools_internal': {
+      'url': Var('chrome_git') + '/clank/third_party/android_tools.git' + '@' +
+        'ab59dfd133386420a319a194c9ac6f5cae802471',
+      'condition': 'checkout_src_internal and checkout_android',
+  },
+
+  # OpenGL ES 2.0 Conformance tests.
+  'src/third_party/gles2_conform': {
+      'url':
+        Var('chrome_git') + '/chrome/deps/gles2_conform.git' + '@' +
+        '57738bb2cc672cb81ed2ee287fcd0defde968811',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/third_party/googlemac': {
+      'url': Var('chrome_git') + '/chrome/deps/googlemac.git' + '@' +
+        '6ae4175fcf9e37655c5d7c3a7482dfc7436281d2',
+      'condition': 'checkout_src_internal and checkout_mac',
+    },
+
+  # OpenGL ES 3.X Conformance tests.
+  "src/third_party/khronos_glcts": {
+      'url':
+        Var('chrome_git') + '/chrome/deps/khronos_glcts.git' + '@' +
+        'eedb0baca9e88c53596874901ff5f6136102d20d',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/third_party/soda': {
+      'packages': [
+          {
+              'package': 'chrome_internal/third_party/soda',
+              'version': 'StdK8khsivYZXVo2wZuVMnDN_xrVO2a8HV8kvfJ3emwC',
+          },
+      ],
+      'condition': 'checkout_src_internal and checkout_linux and checkout_soda',
+      'dep_type': 'cipd',
+    },
+
+  'src/third_party/soda-mac64': {
+      'packages': [
+          {
+              'package': 'chrome_internal/third_party/soda-mac64',
+              'version': 'bJ-qwdYVguWT0V24YjNZ7Nw_toipv0YnVuadeX5xozEC',
+          },
+      ],
+      'condition': 'checkout_src_internal and checkout_mac and checkout_soda',
+      'dep_type': 'cipd',
+    },
+
+  'src/third_party/soda-win32': {
+      'packages': [
+          {
+              'package': 'chrome_internal/third_party/soda-win32',
+              'version': '977qxBGTKbe2kY9aQch9OkNJ3YE8Nt2mVjetdpWeM0IC',
+          },
+      ],
+      'condition': 'checkout_src_internal and checkout_win and checkout_soda',
+      'dep_type': 'cipd',
+    },
+
+  'src/third_party/soda-win64': {
+      'packages': [
+          {
+              'package': 'chrome_internal/third_party/soda-win64',
+              'version': '1elz1jfCAzy5tZUNBr8FsovjgFxmtu8jdyA8ay9Ta8UC',
+          },
+      ],
+      'condition': 'checkout_src_internal and checkout_win and checkout_soda',
+      'dep_type': 'cipd',
+    },
+
+  'src/third_party/widevine/cdm/chromeos': {
+      'url': Var('chrome_git') + '/chrome/deps/widevine/cdm/chromeos.git' + '@' +
+        'edc81c9e25b47b11d0606a717d7f8cd37378302f',
+      'condition': 'checkout_src_internal and (checkout_chromeos or checkout_linux)',
+    },
+
+  'src/third_party/widevine/cdm/linux': {
+      'url': Var('chrome_git') + '/chrome/deps/widevine/cdm/linux.git' + '@' +
+        'a4fb4719bd4e220c3d2a20dda35d9874373fda4b',
+      'condition': 'checkout_src_internal and checkout_linux',
+    },
+
+  'src/third_party/widevine/cdm/mac': {
+      'url': Var('chrome_git') + '/chrome/deps/widevine/cdm/mac.git' + '@' +
+ '957b3265d2e49662b00b4f90b40cbb0cad7d6058',
+      'condition': 'checkout_src_internal and checkout_mac',
+    },
+
+  'src/third_party/widevine/cdm/win': {
+      'url': Var('chrome_git') + '/chrome/deps/widevine/cdm/win.git' + '@' +
+        '13c9314a405e38298dbb1834780c3fad233bc71e',
+      'condition': 'checkout_src_internal and checkout_win',
+    },
+
+  "src/third_party/widevine/scripts": {
+      'url':
+        Var('chrome_git') + '/chrome/deps/widevine/scripts.git' + '@' +
+        '6ae793a606aeed0d0f1c6f688117653710137744',
+      'condition': 'checkout_src_internal',
+  },
+
+  # Only Linux test license server is available.
+  'src/third_party/widevine/test/license_server': {
+      'url': Var('chrome_git') + '/chrome/deps/widevine/test/license_server.git' + '@' +
+        '8b195ed15e73e2ecc9861afb05e6af0b4bdf7413',
+      'condition': 'checkout_src_internal and checkout_linux',
+    },
+
+  'src/third_party/wix': {
+      'url': Var('chrome_git') + '/chrome/deps/wix/v3_5_2519.git' + '@' +
+        '1cda03778b09bee24389da73daef3de862da37fc',
+      'condition': 'checkout_src_internal and checkout_win',
+    },
+
+  'src/tools/perf/data': {
+      'url': Var('chrome_git') + '/chrome/tools/perf/data.git' + '@' +
+        'c7eaf497f690ee69e832b1530e19877602e65b18',
+      'condition': 'checkout_src_internal',
+  },
+  'src/ui/file_manager/internal': {
+      'url': Var('chrome_git') + '/chrome/file_manager.git' + '@' +
+        'a84801be1d5ef906cc03db7eeadd25ce0245ce44',
+      'condition': 'checkout_src_internal and (checkout_chromeos or checkout_linux)',
+    },
+
+  'src/ui/webui/internal': {
+      'url': Var('chrome_git') + '/chrome/ui-webui-internal.git' + '@' +
+        '4afc450a9363ab44f45c3639d0661daa7dbe5bda',
+      'condition': 'checkout_src_internal and checkout_chromeos',
+    },
+
+  'src/webkit/data/bmp_decoder': {
+      'url':
+        Var('chrome_git') + '/chrome/data/bmp_decoder.git' + '@' +
+        '5a3232a478b8afd0e8403fb8c668baf8c9e25ea3',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/webkit/data/ico_decoder': {
+      'url':
+        Var('chrome_git') + '/chrome/data/ico_decoder.git' + '@' +
+        'aba38604e037bdbeedca9c2780c94502a8a6034d',
+      'condition': 'checkout_src_internal',
+  },
+
+  'src/webkit/data/test_shell/plugins': {
+      'url':
+        Var('chrome_git') + '/chrome/data/webkit_plugin_tests.git' + '@' +
+        'e4bd19f95afa6483a54906c2a3e5d329d2d81690',
+      'condition': 'checkout_src_internal',
+  },
+  # end of src_internal
 }
 
 
@@ -3926,6 +4405,7 @@
   '+library_loaders',
 
   '+testing',
+  '+third_party/google_benchmark/src/include/benchmark/benchmark.h',
   '+third_party/icu/source/common/unicode',
   '+third_party/icu/source/i18n/unicode',
   '+url',
@@ -4657,6 +5137,18 @@
                 '-s', 'src/third_party/gvr-android-sdk/test-libraries/controller_test_api.aar.sha1',
     ],
   },
+  {
+    'name': 'vr_assets',
+    'pattern': '.',
+    'condition': 'checkout_src_internal and checkout_android',
+    'action': ['python3',
+               'src/third_party/depot_tools/download_from_google_storage.py',
+               '--bucket', 'chrome-vr-assets',
+               '--recursive',
+               '--directory',
+               'src/chrome/browser/resources/vr/assets/google_chrome',
+    ],
+  },
   # Download and unpack MediaPipe Integration tests.
   {
     'name': 'mediapipe_integration_testdata',
@@ -4789,6 +5281,19 @@
   },
 
   {
+    'name': 'Download Fuchsia internal system images',
+    'pattern': '.',
+    'condition': 'checkout_src_internal and checkout_fuchsia_internal',
+    'action': ['python3',
+               'src/build/fuchsia/update_images.py',
+               '--default-bucket', 'fuchsia-sdk',
+               '--image-root-dir',
+               'src/third_party/fuchsia-sdk/images-internal',
+               '--boot-images', '{checkout_fuchsia_internal_images}',
+    ],
+  },
+
+  {
     'name': 'cros_simplechrome_artifacts_with_vm',
     'pattern': '.',
     'condition': 'checkout_simplechrome_with_vms and not checkout_src_internal',
@@ -5073,9 +5578,11 @@
   'src/third_party/devtools-frontend-internal',
   'src/third_party/openscreen/src',
   'src/third_party/vulkan-deps',
-  # src-internal has its own DEPS file to pull additional internal repos
-  'src-internal',
   # clank has its own DEPS file, does not need to be in trybot_analyze_config
   # since the roller does not run tests.
   'src/clank',
+  'src/chrome/chrome_cleaner/internal',
+  'src/chromeos/assistant/internal',
+  'src/components/optimization_guide/internal',
+  'src/ios_internal',
 ]
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index a3afa29..c20ad33 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1843,15 +1843,20 @@
       <message name="IDS_ASH_ASSISTANT_QUERY_ACCESSIBILITY_ANNOUNCEMENT" desc="The accessibility announcement that notifies users that the UI is focusing on an assistant query">
         <ph name="QUERY_NAME">$1<ex>Harry Potter</ex></ph>, Google Assistant
       </message>
-      <message name="IDS_ASH_VERSION_IN_LAUNCHER_MESSAGE" desc="Message content for the title of the answer card with the current version in the launcher">
+      <message name="IDS_ASH_VERSION_IN_LAUNCHER_MESSAGE" desc="Message content for the title of the answer card with the current version in the launcher.">
         Version <ph name="VERSION_NAME">$1<ex>109.0.5414.125</ex></ph> (<ph name="OFFICIAL_STATUS">$2<ex>Official Build</ex></ph>) <ph name="CHANNEL_NAME">$3<ex>Beta</ex></ph> <ph name="PROCESSOR_VARIATION">$4<ex>(64-bit)</ex></ph>
       </message>
-      <message name="IDS_ASH_CPU_IN_LAUNCHER_TITLE" desc="Message content for the title of the answer card with the current CPU system information in the launcher">
+      <message name="IDS_ASH_CPU_IN_LAUNCHER_TITLE" desc="Message content for the title of the answer card with the current CPU system information in the launcher.">
         CPU current usage: <ph name="CPU_USEAGE">$1<ex>89</ex></ph>%
       </message>
-      <message name="IDS_ASH_CPU_IN_LAUNCHER_DESCRIPTION" desc="Message content for the description of the answer card with the current CPU system information in the launcher">
+      <message name="IDS_ASH_CPU_IN_LAUNCHER_DESCRIPTION" desc="Message content for the description of the answer card with the current CPU system information in the launcher.">
         Temperature: <ph name="TEMPERATURE">$1<ex>34</ex></ph>°C - Current speed: <ph name="CPU_AVERAGE_CURRENT_FREQUENCY_GHZ">$2<ex>1.52</ex></ph>GHz
       </message>
+      <message name="IDS_ASH_MEMORY_USAGE_IN_LAUNCHER_DESCRIPTION" desc="Message content for the description of the answer card with the current memory usage information in the launcher.">
+        <ph name="USED_MEMORY">$1<ex>2.4 GB</ex></ph> of <ph name="TOTAL_MEMORY">$2<ex>7.6 GB</ex></ph> available
+      </message>
+
+
 
       <message name="IDS_ASH_TOAST_DISMISS_BUTTON" desc="The text button shown in toasts to close the toast immediately without waiting timeout.">
         DISMISS
diff --git a/ash/ash_strings_grd/IDS_ASH_MEMORY_USAGE_IN_LAUNCHER_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_MEMORY_USAGE_IN_LAUNCHER_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..86e62b6
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_MEMORY_USAGE_IN_LAUNCHER_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+1749ecf2ce3a463ce19ccb54f80f36f26a6d6120
\ No newline at end of file
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 2ce93642..77fa0927 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1159,7 +1159,7 @@
 // Enable or disable a new header bar for the ChromeOS virtual keyboard.
 BASE_FEATURE(kVirtualKeyboardNewHeader,
              "VirtualKeyboardNewHeader",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // If enabled, used to configure the heuristic rules for some advanced IME
 // features (e.g. auto-correct).
diff --git a/ash/public/cpp/app_list/app_list_types.cc b/ash/public/cpp/app_list/app_list_types.cc
index 881c0d75..39e1a117 100644
--- a/ash/public/cpp/app_list/app_list_types.cc
+++ b/ash/public/cpp/app_list/app_list_types.cc
@@ -5,6 +5,7 @@
 #include "ash/public/cpp/app_list/app_list_types.h"
 
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "base/check.h"
 
 namespace ash {
 
@@ -217,6 +218,32 @@
 SearchResultIconInfo::~SearchResultIconInfo() = default;
 
 ////////////////////////////////////////////////////////////////////////////////
+// SearchResultSystemInfoAnswerCardInfo:
+
+SystemInfoAnswerCardData::SystemInfoAnswerCardData() = default;
+
+SystemInfoAnswerCardData::SystemInfoAnswerCardData(
+    SystemInfoAnswerCardDisplayType display_type)
+    : display_type(display_type) {}
+
+SystemInfoAnswerCardData::SystemInfoAnswerCardData(double bar_chart_percentage)
+    : display_type(SystemInfoAnswerCardDisplayType::kBarChart),
+      bar_chart_percentage(bar_chart_percentage) {}
+
+SystemInfoAnswerCardData::SystemInfoAnswerCardData(
+    std::map<SearchResultSystemInfoStorageType, int64_t>
+        storage_type_to_size_map)
+    : display_type(SystemInfoAnswerCardDisplayType::kMultiElementBarChart),
+      storage_type_to_size(std::move(storage_type_to_size_map)) {
+  DCHECK(!storage_type_to_size.empty());
+}
+
+SystemInfoAnswerCardData::~SystemInfoAnswerCardData() = default;
+
+SystemInfoAnswerCardData::SystemInfoAnswerCardData(
+    const SystemInfoAnswerCardData& other) = default;
+
+////////////////////////////////////////////////////////////////////////////////
 // SearchResultTag:
 
 SearchResultTag::SearchResultTag() = default;
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index 85da7e2..234d79f 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -436,6 +436,29 @@
   kCircle,
 };
 
+// The storage types that are available within the System Info Card storage type
+// answer card.
+enum class SearchResultSystemInfoStorageType {
+  kMyFiles,
+  kDriveOfflineFiles,
+  kBrowsingData,
+  kAppsExtensions,
+  kCrostini,
+  kOtherUsers,
+  kSystem,
+  kTotal
+};
+
+// The display type of the answer cards created by the System Info Provider. The
+// Text Card provides a similar UI to the omnibox answer cards while the bar
+// chart and multi element bar chart provide an additional bar chart with system
+// information.
+enum class SystemInfoAnswerCardDisplayType {
+  kBarChart,
+  kTextCard,
+  kMultiElementBarChart,
+};
+
 struct ASH_PUBLIC_EXPORT SearchResultIconInfo {
   SearchResultIconInfo();
   // TODO(crbug.com/1232897): Make the search backend explicitly set the shape
@@ -460,6 +483,32 @@
   SearchResultIconShape shape = SearchResultIconShape::kDefault;
 };
 
+// Data required for System Info Answer Card result type.
+struct ASH_PUBLIC_EXPORT SystemInfoAnswerCardData {
+ public:
+  SystemInfoAnswerCardData();
+  explicit SystemInfoAnswerCardData(
+      SystemInfoAnswerCardDisplayType display_type);
+  explicit SystemInfoAnswerCardData(double bar_chart_percentage);
+  explicit SystemInfoAnswerCardData(std::map<SearchResultSystemInfoStorageType,
+                                             int64_t> storage_type_to_size);
+
+  SystemInfoAnswerCardData(const SystemInfoAnswerCardData&);
+
+  ~SystemInfoAnswerCardData();
+
+  SystemInfoAnswerCardDisplayType display_type;
+
+  // This stores the percentage of the bar chart to be filled for System Info
+  // Answer card results which are a bar chart type. This will be a value
+  // between 0 and 100.
+  absl::optional<double> bar_chart_percentage;
+
+  // This stores the amount of space occupied by each storage type if this
+  // answer card is showing storage. All sizes are in bytes.
+  std::map<SearchResultSystemInfoStorageType, int64_t> storage_type_to_size;
+};
+
 // A tagged range in search result text.
 struct ASH_PUBLIC_EXPORT SearchResultTag {
   // Similar to ACMatchClassification::Style, the style values are not
@@ -667,6 +716,10 @@
   // The icon of this result.
   SearchResultIconInfo icon;
 
+  // The details for an answer card result with System Information. This field
+  // is only set for this specific result type.
+  absl::optional<SystemInfoAnswerCardData> system_info_answer_card_data;
+
   // The icon of this result in a smaller dimension to be rendered in suggestion
   // chip view.
   // TODO(crbug.com/1225161): Remove this and replace it with |icon| and an
diff --git a/ash/system/audio/audio_detailed_view.cc b/ash/system/audio/audio_detailed_view.cc
index 67eef5e9..157ff8c5 100644
--- a/ash/system/audio/audio_detailed_view.cc
+++ b/ash/system/audio/audio_detailed_view.cc
@@ -300,8 +300,6 @@
 void AudioDetailedView::CreateItems() {
   CreateScrollableList();
   if (features::IsQsRevampEnabled()) {
-    // TODO(b/264446152): Add the settings button once the audio system settings
-    // page is ready.
     CreateTitleRow(IDS_ASH_STATUS_TRAY_AUDIO_QS_REVAMP);
     // `live_caption_view_` will always shows up in the revamped
     // `AudioDetailedView`.
@@ -310,26 +308,11 @@
     CreateTitleRow(IDS_ASH_STATUS_TRAY_AUDIO);
   }
 
-  if (features::IsAudioSettingsPageEnabled()) {
-    CreateTitleSettingsButton();
-  }
-
   mic_gain_controller_ = std::make_unique<MicGainSliderController>();
   unified_volume_slider_controller_ =
       std::make_unique<UnifiedVolumeSliderController>();
 }
 
-void AudioDetailedView::CreateTitleSettingsButton() {
-  tri_view()->SetContainerVisible(TriView::Container::END, /*visible=*/true);
-  std::unique_ptr<views::Button> settings =
-      base::WrapUnique(CreateSettingsButton(
-          base::BindRepeating(&AudioDetailedView::OnSettingsButtonClicked,
-                              weak_factory_.GetWeakPtr()),
-          IDS_ASH_STATUS_TRAY_AUDIO_SETTINGS));
-  settings_button_ =
-      tri_view()->AddView(TriView::Container::END, std::move(settings));
-}
-
 void AudioDetailedView::CreateLiveCaptionView() {
   auto* live_caption_container =
       scroll_content()->AddChildViewAt(std::make_unique<RoundedContainer>(), 0);
@@ -741,6 +724,19 @@
   }
 }
 
+void AudioDetailedView::CreateExtraTitleRowButtons() {
+  if (features::IsAudioSettingsPageEnabled()) {
+    tri_view()->SetContainerVisible(TriView::Container::END, /*visible=*/true);
+    std::unique_ptr<views::Button> settings =
+        base::WrapUnique(CreateSettingsButton(
+            base::BindRepeating(&AudioDetailedView::OnSettingsButtonClicked,
+                                weak_factory_.GetWeakPtr()),
+            IDS_ASH_STATUS_TRAY_AUDIO_SETTINGS));
+    settings_button_ =
+        tri_view()->AddView(TriView::Container::END, std::move(settings));
+  }
+}
+
 // SodaInstaller::Observer:
 void AudioDetailedView::OnSodaInstalled(speech::LanguageCode language_code) {
   std::u16string message = l10n_util::GetStringUTF16(
diff --git a/ash/system/audio/audio_detailed_view.h b/ash/system/audio/audio_detailed_view.h
index 2180223..49f2f5e 100644
--- a/ash/system/audio/audio_detailed_view.h
+++ b/ash/system/audio/audio_detailed_view.h
@@ -75,8 +75,6 @@
   // Creates the items other than the devices during initialization.
   void CreateItems();
 
-  void CreateTitleSettingsButton();
-
   // For QsRevamp: Creates the `live_caption_view_`.
   void CreateLiveCaptionView();
 
@@ -115,6 +113,7 @@
 
   // TrayDetailedView:
   void HandleViewClicked(views::View* view) override;
+  void CreateExtraTitleRowButtons() override;
 
   // SodaInstaller::Observer:
   void OnSodaInstalled(speech::LanguageCode language_code) override;
diff --git a/ash/system/bluetooth/bluetooth_detailed_view_impl.cc b/ash/system/bluetooth/bluetooth_detailed_view_impl.cc
index efa01b3..ee3eeb0b 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view_impl.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view_impl.cc
@@ -54,7 +54,6 @@
     : BluetoothDetailedView(delegate),
       TrayDetailedView(detailed_view_delegate) {
   CreateTitleRow(IDS_ASH_STATUS_TRAY_BLUETOOTH);
-  CreateTitleSettingsButton();
   CreateScrollableList();
   CreateTopContainer();
   CreateMainContainer();
@@ -151,7 +150,7 @@
       static_cast<BluetoothDeviceListItemView*>(view)->device_properties());
 }
 
-void BluetoothDetailedViewImpl::CreateTitleSettingsButton() {
+void BluetoothDetailedViewImpl::CreateExtraTitleRowButtons() {
   DCHECK(!settings_button_);
 
   tri_view()->SetContainerVisible(TriView::Container::END, /*visible=*/true);
diff --git a/ash/system/bluetooth/bluetooth_detailed_view_impl.h b/ash/system/bluetooth/bluetooth_detailed_view_impl.h
index e3e41ac..be087ea 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view_impl.h
+++ b/ash/system/bluetooth/bluetooth_detailed_view_impl.h
@@ -47,13 +47,11 @@
 
   // TrayDetailedView:
   void HandleViewClicked(views::View* view) override;
+  void CreateExtraTitleRowButtons() override;
 
  private:
   friend class BluetoothDetailedViewImplTest;
 
-  // Creates and configures the title section settings button.
-  void CreateTitleSettingsButton();
-
   // Creates the top rounded container, which contains the main on/off toggle.
   void CreateTopContainer();
 
diff --git a/ash/system/brightness/display_detailed_view.cc b/ash/system/brightness/display_detailed_view.cc
index b3e8b9b..ba68b89 100644
--- a/ash/system/brightness/display_detailed_view.cc
+++ b/ash/system/brightness/display_detailed_view.cc
@@ -47,7 +47,6 @@
       unified_system_tray_controller_(tray_controller) {
   CreateScrollableList();
   CreateTitleRow(IDS_ASH_STATUS_TRAY_DISPLAY);
-  CreateTitleSettingsButton();
   // Sets the margin for `ScrollView` to leave some space for the focus ring.
   scroller()->SetProperty(views::kMarginsKey, kScrollViewMargin);
 
@@ -105,7 +104,7 @@
   return scroll_content();
 }
 
-void DisplayDetailedView::CreateTitleSettingsButton() {
+void DisplayDetailedView::CreateExtraTitleRowButtons() {
   DCHECK(!settings_button_);
 
   tri_view()->SetContainerVisible(TriView::Container::END, /*visible=*/true);
diff --git a/ash/system/brightness/display_detailed_view.h b/ash/system/brightness/display_detailed_view.h
index 560d7c0..f793cac 100644
--- a/ash/system/brightness/display_detailed_view.h
+++ b/ash/system/brightness/display_detailed_view.h
@@ -37,8 +37,8 @@
   views::View* GetScrollContentForTest();
 
  private:
-  // Creates the `settings_button_` on the right end of the title row.
-  void CreateTitleSettingsButton();
+  // TrayDetailedView:
+  void CreateExtraTitleRowButtons() override;
 
   // Callback of the `settings_button_` to open the display system settings
   // page.
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.cc b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
index 898d8df..3138c91 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_impl.cc
+++ b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
@@ -273,9 +273,17 @@
 void InputDeviceSettingsControllerImpl::SetKeyboardSettings(
     DeviceId id,
     mojom::KeyboardSettingsPtr settings) {
-  DCHECK(base::Contains(keyboards_, id));
   DCHECK(active_pref_service_);
-  auto& found_keyboard = *keyboards_.at(id);
+
+  // If a device with the given id does not exist, do nothing.
+  auto found_keyboard_iter = keyboards_.find(id);
+  if (found_keyboard_iter == keyboards_.end()) {
+    return;
+  }
+
+  // TODO(dpad): Validate incoming settings to make sure the settings can apply
+  // to the given device.
+  auto& found_keyboard = *found_keyboard_iter->second;
   found_keyboard.settings = settings.Clone();
   keyboard_pref_handler_->UpdateKeyboardSettings(active_pref_service_,
                                                  found_keyboard);
@@ -294,9 +302,17 @@
 void InputDeviceSettingsControllerImpl::SetTouchpadSettings(
     DeviceId id,
     mojom::TouchpadSettingsPtr settings) {
-  DCHECK(base::Contains(touchpads_, id));
   DCHECK(active_pref_service_);
-  auto& found_touchpad = *touchpads_.at(id);
+
+  // If a device with the given id does not exist, do nothing.
+  auto found_touchpad_iter = touchpads_.find(id);
+  if (found_touchpad_iter == touchpads_.end()) {
+    return;
+  }
+
+  // TODO(dpad): Validate incoming settings to make sure the settings can apply
+  // to the given device.
+  auto& found_touchpad = *found_touchpad_iter->second;
   found_touchpad.settings = settings.Clone();
   touchpad_pref_handler_->UpdateTouchpadSettings(active_pref_service_,
                                                  found_touchpad);
@@ -315,9 +331,17 @@
 void InputDeviceSettingsControllerImpl::SetMouseSettings(
     DeviceId id,
     mojom::MouseSettingsPtr settings) {
-  DCHECK(base::Contains(mice_, id));
   DCHECK(active_pref_service_);
-  auto& found_mouse = *mice_.at(id);
+
+  // If a device with the given id does not exist, do nothing.
+  auto found_mouse_iter = mice_.find(id);
+  if (found_mouse_iter == mice_.end()) {
+    return;
+  }
+
+  // TODO(dpad): Validate incoming settings to make sure the settings can apply
+  // to the given device.
+  auto& found_mouse = *found_mouse_iter->second;
   found_mouse.settings = settings.Clone();
   mouse_pref_handler_->UpdateMouseSettings(active_pref_service_, found_mouse);
   DispatchMouseSettingsChanged(id);
@@ -335,9 +359,17 @@
 void InputDeviceSettingsControllerImpl::SetPointingStickSettings(
     DeviceId id,
     mojom::PointingStickSettingsPtr settings) {
-  DCHECK(base::Contains(pointing_sticks_, id));
   DCHECK(active_pref_service_);
-  auto& found_pointing_stick = *pointing_sticks_.at(id);
+
+  // If a device with the given id does not exist, do nothing.
+  auto found_pointing_stick_iter = pointing_sticks_.find(id);
+  if (found_pointing_stick_iter == pointing_sticks_.end()) {
+    return;
+  }
+
+  // TODO(dpad): Validate incoming settings to make sure the settings can apply
+  // to the given device.
+  auto& found_pointing_stick = *found_pointing_stick_iter->second;
   found_pointing_stick.settings = settings.Clone();
   pointing_stick_pref_handler_->UpdatePointingStickSettings(
       active_pref_service_, found_pointing_stick);
diff --git a/ash/system/input_device_settings/input_device_settings_controller_unittest.cc b/ash/system/input_device_settings/input_device_settings_controller_unittest.cc
index 4bb31b5..e47e1cea 100644
--- a/ash/system/input_device_settings/input_device_settings_controller_unittest.cc
+++ b/ash/system/input_device_settings/input_device_settings_controller_unittest.cc
@@ -266,6 +266,20 @@
   EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 1u);
 }
 
+// Tests that given an invalid id, keyboard settings are not updated and
+// observers are not notified.
+TEST_F(InputDeviceSettingsControllerTest, KeyboardSettingsUpdatedInvalidId) {
+  controller()->OnKeyboardListUpdated({kSampleKeyboardUsb}, {});
+
+  EXPECT_EQ(observer_->num_keyboards_connected(), 1u);
+  EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_initialized(), 1u);
+  controller()->SetKeyboardSettings((DeviceId)kSampleKeyboardUsb.id + 1,
+                                    mojom::KeyboardSettings::New());
+
+  EXPECT_EQ(observer_->num_keyboards_settings_updated(), 0u);
+  EXPECT_EQ(keyboard_pref_handler_->num_keyboard_settings_updated(), 0u);
+}
+
 TEST_F(InputDeviceSettingsControllerTest, KeyboardSettingsUpdateMultiple) {
   // The SetKeyboardSettings call should update both keyboards since they have
   // the same |device_key|.
diff --git a/ash/system/network/network_detailed_view.cc b/ash/system/network/network_detailed_view.cc
index aa73569..993f779 100644
--- a/ash/system/network/network_detailed_view.cc
+++ b/ash/system/network/network_detailed_view.cc
@@ -40,7 +40,6 @@
   CreateTitleRow(list_type_ == ListType::LIST_TYPE_NETWORK
                      ? IDS_ASH_STATUS_TRAY_NETWORK
                      : IDS_ASH_STATUS_TRAY_VPN);
-  CreateTitleRowButtons();
   CreateScrollableList();
   // TODO(b/207089013): add metrics for UI surface displayed.
 }
@@ -63,7 +62,7 @@
       static_cast<NetworkListItemView*>(view)->network_properties());
 }
 
-void NetworkDetailedView::CreateTitleRowButtons() {
+void NetworkDetailedView::CreateExtraTitleRowButtons() {
   DCHECK(!info_button_);
   tri_view()->SetContainerVisible(TriView::Container::END, true);
 
diff --git a/ash/system/network/network_detailed_view.h b/ash/system/network/network_detailed_view.h
index 5b7e87c..075d508d 100644
--- a/ash/system/network/network_detailed_view.h
+++ b/ash/system/network/network_detailed_view.h
@@ -70,13 +70,13 @@
     kSettingsButton = 2,
   };
 
-  void CreateTitleRowButtons();
   void OnInfoClicked();
   bool CloseInfoBubble();
   void OnSettingsClicked();
 
   // TrayDetailedView:
   void HandleViewClicked(views::View* view) override;
+  void CreateExtraTitleRowButtons() override;
 
   // NetworkInfoBubble::Delegate:
   bool ShouldIncludeDeviceAddresses() override;
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index bb022fd..9c5e00fa 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -241,8 +241,6 @@
 constexpr int kUnifiedTopShortcutButtonMinSpacing = 4;
 
 // Constants used in the detailed view in UnifiedSystemTray.
-constexpr auto kUnifiedDetailedViewTitlePadding =
-    gfx::Insets::TLBR(0, 0, 0, 16);
 constexpr int kUnifiedDetailedViewTitleRowHeight = 64;
 
 // Constants used for the status area overflow button and state.
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index 89f4a9b..5f87f57 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -63,6 +63,8 @@
 
 constexpr int kQsScrollViewCornerRadius = 16;
 
+constexpr int kQsTriViewRightPadding = 16;
+
 // Inset the scroll bar to avoid the rounded corners at top and bottom.
 constexpr auto kQsScrollBarInsets =
     gfx::Insets::VH(kQsScrollViewCornerRadius, 0);
@@ -77,9 +79,11 @@
       const int left_padding = container == TriView::Container::START
                                    ? kUnifiedBackButtonLeftPadding
                                    : 0;
+      const int right_padding =
+          container == TriView::Container::END ? kQsTriViewRightPadding : 0;
       layout = std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kHorizontal,
-          gfx::Insets::TLBR(0, left_padding, 0, 0),
+          gfx::Insets::TLBR(0, left_padding, 0, right_padding),
           features::IsQsRevampEnabled() ? kQsItemBetweenSpacing
                                         : kUnifiedTopShortcutSpacing);
       layout->set_main_axis_alignment(
@@ -611,8 +615,6 @@
                                    TrayPopupUtils::FontStyle::kTitle);
   tri_view->AddView(TriView::Container::CENTER, title_label_);
   tri_view->SetContainerVisible(TriView::Container::END, false);
-  tri_view->SetBorder(
-      views::CreateEmptyBorder(kUnifiedDetailedViewTitlePadding));
 
   return tri_view;
 }
diff --git a/ash/webui/camera_app_ui/resources/js/custom_effect.ts b/ash/webui/camera_app_ui/resources/js/custom_effect.ts
index 0d34f24..b8ffd8f 100644
--- a/ash/webui/camera_app_ui/resources/js/custom_effect.ts
+++ b/ash/webui/camera_app_ui/resources/js/custom_effect.ts
@@ -9,6 +9,7 @@
 import * as loadTimeData from './models/load_time_data.js';
 import {speakMessage} from './spoken_msg.js';
 import * as state from './state.js';
+import {PerfEvent} from './type';
 import * as util from './util.js';
 
 /**
@@ -111,12 +112,12 @@
  * modes/cameras.
  */
 export function setup(): void {
-  state.addObserver(state.State.CAMERA_SWITCHING, (val) => {
+  state.addObserver(PerfEvent.CAMERA_SWITCHING, (val) => {
     if (val) {
       hide();
     }
   });
-  state.addObserver(state.State.MODE_SWITCHING, (val) => {
+  state.addObserver(PerfEvent.MODE_SWITCHING, (val) => {
     if (val) {
       hide();
     }
diff --git a/ash/webui/camera_app_ui/resources/js/externs/types.d.ts b/ash/webui/camera_app_ui/resources/js/externs/types.d.ts
index ef334fa..db137d39 100644
--- a/ash/webui/camera_app_ui/resources/js/externs/types.d.ts
+++ b/ash/webui/camera_app_ui/resources/js/externs/types.d.ts
@@ -27,6 +27,11 @@
 interface FileSystemFileHandle {
   createWritable(options?: FileSystemCreateWritableOptions):
       Promise<FileSystemWritableFileStream>;
+
+  // move() is only implemented in Chrome so it's not in upstream type
+  // definitions. Ref:
+  // https://chromestatus.com/feature/5640802622504960
+  move(dir: FileSystemDirectoryHandle, name: string): Promise<void>;
 }
 
 interface FileSystemDirectoryHandle {
diff --git a/ash/webui/camera_app_ui/resources/js/gallerybutton.ts b/ash/webui/camera_app_ui/resources/js/gallerybutton.ts
index 29c4ca0..540c43e 100644
--- a/ash/webui/camera_app_ui/resources/js/gallerybutton.ts
+++ b/ash/webui/camera_app_ui/resources/js/gallerybutton.ts
@@ -244,14 +244,16 @@
   }
 
   async startSaveVideo(videoRotation: number): Promise<VideoSaver> {
-    const file = await filesystem.createVideoFile(VideoType.MP4);
-    return VideoSaver.createForFile(file, videoRotation);
+    return VideoSaver.create(videoRotation);
   }
 
   async finishSaveVideo(video: VideoSaver): Promise<void> {
     const file = await video.endWrite();
     assert(file !== null);
 
+    const videoName = (new Filenamer()).newVideoName(VideoType.MP4);
+    assert(this.directory !== null);
+    await file.moveTo(this.directory, videoName);
     ChromeHelper.getInstance().sendNewCaptureBroadcast(
         {isVideo: true, name: file.name});
     await this.updateCover(file);
diff --git a/ash/webui/camera_app_ui/resources/js/models/file_system.ts b/ash/webui/camera_app_ui/resources/js/models/file_system.ts
index a6d9f33..51ae22fa 100644
--- a/ash/webui/camera_app_ui/resources/js/models/file_system.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/file_system.ts
@@ -3,12 +3,10 @@
 // found in the LICENSE file.
 
 import {assert} from '../assert.js';
-import {VideoType} from '../type.js';
 import {WaitableEvent} from '../waitable_event.js';
 
 import {
   DOCUMENT_PREFIX,
-  Filenamer,
   IMAGE_PREFIX,
   VIDEO_PREFIX,
 } from './file_namer.js';
@@ -52,16 +50,16 @@
 }
 
 /**
- * Temporary directory in the internal file system.
- */
-let internalTempDir: DirectoryAccessEntry|null = null;
-
-/**
  * Camera directory in the external file system.
  */
 let cameraDir: DirectoryAccessEntry|null = null;
 
 /**
+ * Temporary directory which is hidden under the camera directory.
+ */
+let cameraTempDir: DirectoryAccessEntry|null = null;
+
+/**
  * Gets camera directory used by CCA.
  */
 export function getCameraDirectory(): DirectoryAccessEntry|null {
@@ -69,12 +67,13 @@
 }
 
 /**
- * Initializes the temporary directory in the internal file system.
+ * Initializes the temporary directory under camera directory.
  *
  * @return Promise for the directory result.
  */
-async function initInternalTempDir(): Promise<DirectoryAccessEntry> {
-  return new DirectoryAccessEntryImpl(await navigator.storage.getDirectory());
+async function initCameraTempDir(): Promise<DirectoryAccessEntry> {
+  assert(cameraDir !== null);
+  return getMaybeLazyDirectory(cameraDir, '.Temp');
 }
 
 /**
@@ -118,11 +117,11 @@
  * beginning of the app.
  */
 export async function initialize(): Promise<void> {
-  internalTempDir = await initInternalTempDir();
-  assert(internalTempDir !== null);
-
   cameraDir = await initCameraDirectory();
   assert(cameraDir !== null);
+
+  cameraTempDir = await initCameraTempDir();
+  assert(cameraTempDir !== null);
 }
 
 /**
@@ -142,33 +141,19 @@
   return file;
 }
 
-/**
- * Creates a file for saving video recording result.
- *
- * @return Newly created video file.
- * @throws If failed to create video file.
- */
-export async function createVideoFile(videoType: VideoType):
-    Promise<FileAccessEntry> {
-  assert(cameraDir !== null);
-  const name = new Filenamer().newVideoName(videoType);
-  const file = await cameraDir.createFile(name);
-  if (file === null) {
-    throw new Error('Failed to create video temp file.');
-  }
-  return file;
-}
-
-const PRIVATE_TEMPFILE_NAME = 'video-intent.mp4';
+const PRIVATE_TEMPFILE_NAME = 'video-tmp.mp4';
 
 /**
  * @return Newly created temporary file.
  * @throws If failed to create video temp file.
  */
 export async function createPrivateTempVideoFile(): Promise<FileAccessEntry> {
-  // TODO(inker): Handles running out of space case.
-  const dir = internalTempDir;
+  const dir = cameraTempDir;
   assert(dir !== null);
+
+  // Delete the previous temporary file if there is any.
+  await dir.removeEntry(PRIVATE_TEMPFILE_NAME);
+
   const file = await dir.createFile(PRIVATE_TEMPFILE_NAME);
   if (file === null) {
     throw new Error('Failed to create private video temp file.');
diff --git a/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.ts b/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.ts
index 633c70a..48b214b3 100644
--- a/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/file_system_access_entry.ts
@@ -71,6 +71,14 @@
     return this.parent.removeEntry(this.name);
   }
 
+  /**
+   * Moves the file to given directory and given name.
+   */
+  async moveTo(dir: DirectoryAccessEntry, name: string): Promise<void> {
+    const dirHandle = await dir.getHandle();
+    await this.handle.move(dirHandle, name);
+  }
+
   get name(): string {
     return this.handle.name;
   }
@@ -91,6 +99,11 @@
   readonly name: string;
 
   /**
+   * Gets the handle of the directory.
+   */
+  getHandle(): Promise<FileSystemDirectoryHandle>;
+
+  /**
    * Gets files in this directory.
    */
   getFiles(): Promise<FileAccessEntry[]>;
@@ -153,6 +166,10 @@
     return this.handle.name;
   }
 
+  async getHandle(): Promise<FileSystemDirectoryHandle> {
+    return this.handle;
+  }
+
   async getFiles(): Promise<FileAccessEntry[]> {
     const results = [];
     for await (const handle of this.handle.values()) {
@@ -226,6 +243,8 @@
   }
 
   async removeEntry(name: string): Promise<void> {
-    return this.handle.removeEntry(name);
+    if (await this.isExist(name)) {
+      await this.handle.removeEntry(name);
+    }
   }
 }
diff --git a/ash/webui/camera_app_ui/resources/js/models/lazy_directory_entry.ts b/ash/webui/camera_app_ui/resources/js/models/lazy_directory_entry.ts
index c3227e7..bedcd69 100644
--- a/ash/webui/camera_app_ui/resources/js/models/lazy_directory_entry.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/lazy_directory_entry.ts
@@ -41,6 +41,11 @@
   constructor(
       private readonly parent: DirectoryAccessEntry, readonly name: string) {}
 
+  async getHandle(): Promise<FileSystemDirectoryHandle> {
+    const dir = await this.getRealDirectory();
+    return dir.getHandle();
+  }
+
   async getFiles(): Promise<FileAccessEntry[]> {
     if (this.directory === null) {
       return [];
diff --git a/ash/webui/camera_app_ui/resources/js/models/video_saver.ts b/ash/webui/camera_app_ui/resources/js/models/video_saver.ts
index 33a690e..d760baa 100644
--- a/ash/webui/camera_app_ui/resources/js/models/video_saver.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/video_saver.ts
@@ -97,10 +97,13 @@
   }
 
   /**
-   * Creates video saver for the given file.
+   * Creates video saver which saves video into a temporary file.
+   * TODO(b/184583382): Saves to the target file directly once the File System
+   * Access API supports cleaning temporary file when leaving the page without
+   * closing the file stream.
    */
-  static async createForFile(file: FileAccessEntry, videoRotation: number):
-      Promise<VideoSaver> {
+  static async create(videoRotation: number): Promise<VideoSaver> {
+    const file = await createPrivateTempVideoFile();
     const writer = await file.getWriter();
     const processor = await createVideoProcessor(writer, videoRotation);
     return new VideoSaver(file, processor);
diff --git a/ash/webui/camera_app_ui/resources/js/state.ts b/ash/webui/camera_app_ui/resources/js/state.ts
index 64e5b4d..ff005a0d 100644
--- a/ash/webui/camera_app_ui/resources/js/state.ts
+++ b/ash/webui/camera_app_ui/resources/js/state.ts
@@ -13,7 +13,6 @@
 
 export enum State {
   CAMERA_CONFIGURING = 'camera-configuring',
-  CAMERA_SWITCHING = 'camera-switching',
   DOC_MODE_REVIEWING = 'doc-mode-reviewing',
   ENABLE_GIF_RECORDING = 'enable-gif-recording',
   ENABLE_PTZ = 'enable-ptz',
@@ -36,7 +35,6 @@
   MAX_WND = 'max-wnd',
   MIC = 'mic',
   MIRROR = 'mirror',
-  MODE_SWITCHING = 'mode-switching',
   MULTI_CAMERA = 'multi-camera',
   PLAYING_RESULT_VIDEO = 'playing-result-video',
   RECORD_TYPE_GIF = 'record-type-gif',
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera.ts b/ash/webui/camera_app_ui/resources/js/views/camera.ts
index 97cb09f1..30338e8 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera.ts
@@ -287,9 +287,9 @@
           const mode = util.assertEnumVariant(Mode, el.dataset['mode']);
           this.updateModeUI(mode);
           this.updateShutterLabel(mode);
-          state.set(state.State.MODE_SWITCHING, true);
+          state.set(PerfEvent.MODE_SWITCHING, true);
           const isSuccess = await this.cameraManager.switchMode(mode);
-          state.set(state.State.MODE_SWITCHING, false, {hasError: !isSuccess});
+          state.set(PerfEvent.MODE_SWITCHING, false, {hasError: !isSuccess});
         }
       });
     }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/options.ts b/ash/webui/camera_app_ui/resources/js/views/camera/options.ts
index 04f5d282..219c95fb 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/options.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/options.ts
@@ -54,15 +54,13 @@
    */
   private audioTrack: MediaStreamTrack|null = null;
 
-  private cameraAvailable = false;
-
   /**
    * @param cameraManager Camera manager instance.
    */
   constructor(private readonly cameraManager: CameraManager) {
     this.cameraManager.registerCameraUI(this);
     this.switchDeviceButton.addEventListener('click', async () => {
-      if (state.get(state.State.TAKING) || !this.cameraAvailable) {
+      if (state.get(state.State.TAKING)) {
         return;
       }
       const switching = this.cameraManager.switchCamera();
@@ -84,10 +82,6 @@
     this.mirroringToggles =
         localStorage.getObject(LocalStorageKey.MIRRORING_TOGGLES);
 
-    state.addObserver(state.State.TAKING, () => {
-      this.updateOptionAvailability();
-    });
-
     util.bindElementAriaLabelWithState({
       element: this.toggleMic,
       state: state.State.MIC,
@@ -243,20 +237,11 @@
   onUpdateConfig(config: CameraConfig): void {
     this.currentConfig = config;
     this.updateMirroring();
+    this.updateOptionAvailability();
     this.audioTrack = this.cameraManager.getAudioTrack();
     this.updateAudioByMic();
   }
 
-  onCameraAvailable(): void {
-    this.cameraAvailable = true;
-    this.updateOptionAvailability();
-  }
-
-  onCameraUnavailable(): void {
-    this.cameraAvailable = false;
-    this.updateOptionAvailability();
-  }
-
   private updateOptionAvailability(): void {
     this.openMirrorPanel.disabled = !this.allowModifyMirrorState();
   }
diff --git a/ash/webui/common/resources/multidevice_setup/start_setup_page.html b/ash/webui/common/resources/multidevice_setup/start_setup_page.html
index ad02938a..1294a40 100644
--- a/ash/webui/common/resources/multidevice_setup/start_setup_page.html
+++ b/ash/webui/common/resources/multidevice_setup/start_setup_page.html
@@ -92,7 +92,7 @@
   }
 </style>
 
-<ui-page header-text="[[i18nDynamic(locale, 'startSetupPageHeader')]]"
+<ui-page header-text="[[i18nDynamic(locale, headerTextId)]]"
     icon-name="google-g">
   <span slot="message" id="multidevice-summary-message" inner-h-t-m-l=
       "[[i18nAdvancedDynamic_(locale, 'startSetupPageMessage')]]">
diff --git a/ash/webui/common/resources/multidevice_setup/start_setup_page.js b/ash/webui/common/resources/multidevice_setup/start_setup_page.js
index cc12221..8f3e3f7 100644
--- a/ash/webui/common/resources/multidevice_setup/start_setup_page.js
+++ b/ash/webui/common/resources/multidevice_setup/start_setup_page.js
@@ -17,6 +17,7 @@
 import {ConnectivityStatus} from 'chrome://resources/mojo/chromeos/ash/services/device_sync/public/mojom/device_sync.mojom-webui.js';
 import {HostDevice} from 'chrome://resources/mojo/chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-webui.js';
 
+import {MojoInterfaceProvider, MojoInterfaceProviderImpl} from './mojo_api.js';
 import {MultiDeviceSetupDelegate} from './multidevice_setup_delegate.js';
 import {getTemplate} from './start_setup_page.html.js';
 import {UiPageContainerBehavior} from './ui_page_container_behavior.js';
@@ -40,6 +41,17 @@
   is: 'start-setup-page',
 
   properties: {
+    /* The localized loadTimeData string for the
+     * StartSetupPage header text, dependent on whether
+     * a user is on OOBE and previously connected their phone during Quick
+     * Start.
+     * */
+    headerTextId: {
+      type: String,
+      value: 'startSetupPageHeader',
+      notify: true,
+    },
+
     /** Overridden from UiPageContainerBehavior. */
     forwardButtonTextId: {
       type: String,
@@ -112,6 +124,12 @@
       type: Boolean,
       value: false,
     },
+
+    /**
+     * Provider of an interface to the MultiDeviceSetup Mojo service.
+     * @private {!MojoInterfaceProvider}
+     */
+    mojoInterfaceProvider_: Object,
   },
 
   behaviors: [
@@ -120,6 +138,11 @@
   ],
 
   /** @override */
+  created() {
+    this.mojoInterfaceProvider_ = MojoInterfaceProviderImpl.getInstance();
+  },
+
+  /** @override */
   attached() {
     this.addWebUIListener(
         'multidevice_setup.initializeSetupFlow',
@@ -154,6 +177,19 @@
       helpArticleLinks[i].onclick = this.fire.bind(
           this, 'open-learn-more-webview-requested', helpArticleLinks[i].href);
     }
+
+    this.mojoInterfaceProvider_.getMojoServiceRemote()
+        .getQuickStartPhoneInstanceID()
+        .then((responseParams) => {
+          if (!responseParams.qsPhoneInstanceId) {
+            return;
+          }
+
+          this.headerTextId = 'startSetupPageAfterQuickStartHeader';
+        })
+        .catch((error) => {
+          console.warn('Mojo service failure: ' + error);
+        });
   },
 
   /**
diff --git a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
index 4be7b1e..38b56d5 100644
--- a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
+++ b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
@@ -80,6 +80,12 @@
   bool assistant_debug_info_allowed;
   // Whether or not the feedback app is opened from OS Settings Search.
   bool from_settings_search;
+  // Whether or not the feedback app is opened from autofill context menu.
+  bool from_autofill;
+  // Autofill metadata provided by source CrOS application by
+  // setting the autofill_metadata query parameter when opening the CrOS
+  // Feedback app.
+  string? autofill_metadata;
   // The URL of the page that this issue was being experienced on.
   url.mojom.Url?  page_url;
   // Extra diagnostics information provided by source CrOS application by
@@ -139,6 +145,9 @@
   // Whether or not the report has sendBluetoothLogs
   // flag set true by internal google account only.
   bool send_bluetooth_logs;
+  // Whether or not to send autofill metadata with this report. Set by internal
+  // google account only.
+  bool include_autofill_metadata;
 };
 
 // The first action taken on the confirmation page.
diff --git a/ash/webui/os_feedback_ui/os_feedback_ui.cc b/ash/webui/os_feedback_ui/os_feedback_ui.cc
index f49e676..f610312d 100644
--- a/ash/webui/os_feedback_ui/os_feedback_ui.cc
+++ b/ash/webui/os_feedback_ui/os_feedback_ui.cc
@@ -112,6 +112,8 @@
       {"includeAssistantLogsCheckboxLabel",
        IDS_FEEDBACK_TOOL_ASSISTANT_LOGS_CHECKBOX},
       {"assistantLogsMessage", IDS_FEEDBACK_TOOL_ASSISTANT_LOGS_MESSAGE},
+      {"includeAutofillCheckboxLabel",
+       IDS_FEEDBACK_TOOL_AUTOFILL_LOGS_CHECKBOX},
   };
 
   source->AddLocalizedStrings(kLocalizedStrings);
diff --git a/ash/webui/os_feedback_ui/resources/fake_data.js b/ash/webui/os_feedback_ui/resources/fake_data.js
index e411fc70..1e648f1 100644
--- a/ash/webui/os_feedback_ui/resources/fake_data.js
+++ b/ash/webui/os_feedback_ui/resources/fake_data.js
@@ -84,6 +84,8 @@
   fromAssistant: false,
   assistantDebugInfoAllowed: false,
   fromSettingsSearch: false,
+  fromAutofill: false,
+  autofillMetadata: '',
   traceId: 1,
 };
 
@@ -95,6 +97,8 @@
   fromAssistant: false,
   assistantDebugInfoAllowed: false,
   fromSettingsSearch: false,
+  fromAutofill: false,
+  autofillMetadata: '',
   traceId: 0,
 };
 
@@ -106,6 +110,8 @@
   fromAssistant: true,
   assistantDebugInfoAllowed: false,
   fromSettingsSearch: true,
+  fromAutofill: false,
+  autofillMetadata: '',
   traceId: 1,
 };
 
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.html b/ash/webui/os_feedback_ui/resources/feedback_flow.html
index 27942af3..89467cf 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.html
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.html
@@ -9,6 +9,7 @@
     </search-page>
     <share-data-page id="shareDataPage" feedback-context="[[feedbackContext_]]"
         should-show-assistant-checkbox="[[shouldShowAssistantCheckbox_]]"
+        should-show-autofill-checkbox="[[shouldShowAutofillCheckbox_]]"
         should-show-bluetooth-checkbox="[[shouldShowBluetoothCheckbox_]]"
         on-continue-click="handleContinueClick_"
         on-go-back-click="handleGoBackClick_">
diff --git a/ash/webui/os_feedback_ui/resources/feedback_flow.js b/ash/webui/os_feedback_ui/resources/feedback_flow.js
index 0c968b3..8f1424a 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_flow.js
+++ b/ash/webui/os_feedback_ui/resources/feedback_flow.js
@@ -59,6 +59,8 @@
   PAGE_URL: 'page_url',
   FROM_ASSISTANT: 'from_assistant',
   FROM_SETTINGS_SEARCH: 'from_settings_search',
+  FROM_AUTOFILL: 'from_autofill',
+  AUTOFILL_METADATA: 'autofill_metadata',
 };
 
 /**
@@ -186,6 +188,13 @@
     this.shouldShowBluetoothCheckbox_;
 
     /**
+     * Whether to show the autofill checkbox in share data page.
+     * @type {boolean}
+     * @protected
+     */
+    this.shouldShowAutofillCheckbox_;
+
+    /**
      * Whether to show the assistant checkbox in share data page.
      * @type {boolean}
      */
@@ -264,6 +273,8 @@
       this.shouldShowAssistantCheckbox_ = !!this.feedbackContext_ &&
           this.feedbackContext_.isInternalAccount &&
           this.feedbackContext_.fromAssistant;
+      this.shouldShowAutofillCheckbox_ =
+          !!this.feedbackContext_ && this.feedbackContext_.fromAutofill;
     });
 
     window.addEventListener('message', event => {
@@ -385,6 +396,14 @@
     const fromSettingsSearch =
         params.get(AdditionalContextQueryParam.FROM_SETTINGS_SEARCH);
     this.set('feedbackContext_.fromSettingsSearch', !!fromSettingsSearch);
+
+    const fromAutofill = params.get(AdditionalContextQueryParam.FROM_AUTOFILL);
+    this.feedbackContext_.fromAutofill = !!fromAutofill;
+    const autofillMetadata =
+        params.get(AdditionalContextQueryParam.AUTOFILL_METADATA);
+    if (autofillMetadata) {
+      this.feedbackContext_.autofillMetadata = autofillMetadata;
+    }
   }
 
   /**
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 20aa18c0..ba4d7f5a 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.html
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -260,6 +260,16 @@
           </div>
         </cr-checkbox>
       </div>
+      <!-- Autofill Metadata (Googler Internal Only) -->
+      <div id="autofillCheckboxContainer" class="checkbox-field-container"
+          hidden="[[!shouldShowAutofillCheckbox]]">
+        <cr-checkbox id="autofillCheckbox"
+            aria-labelledby="autofillCheckboxLabel" checked>
+          <div id="autofillCheckboxLabel"
+            inner-h-t-m-l="[[autofillCheckboxLabel_]]" class="checkbox-label">
+          </div>
+        </cr-checkbox>
+      </div>
       <!-- System Information -->
       <div id="sysInfoContainer" class="checkbox-field-container">
         <cr-checkbox id="sysInfoCheckbox" aria-labelledby="sysInfoCheckboxLabel"
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 d533e19..f058b7b6 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -55,6 +55,8 @@
           {type: Boolean, readOnly: false, notify: true},
       shouldShowAssistantCheckbox:
           {type: Boolean, readOnly: false, notify: true},
+      shouldShowAutofillCheckbox:
+          {type: Boolean, readOnly: false, notify: true},
     };
   }
 
@@ -82,6 +84,11 @@
     this.shouldShowAssistantCheckbox;
 
     /**
+     * @type {boolean}
+     */
+    this.shouldShowAutofillCheckbox;
+
+    /**
      * @type {string}
      * @protected
      */
@@ -103,6 +110,12 @@
      * @type {string}
      * @protected
      */
+    this.autofillCheckboxLabel_;
+
+    /**
+     * @type {string}
+     * @protected
+     */
     this.bluetoothLogsCheckboxLabel_;
 
     /**
@@ -122,6 +135,7 @@
     this.setPerformanceTraceCheckboxLabel_();
     this.setAssistantLogsCheckboxLabelAndAttributes_();
     this.setBluetoothLogsCheckboxLabelAndAttributes_();
+    this.setAutofillCheckboxLabelAndAttributes_();
     // Set the aria description works the best for screen reader.
     // It reads the description when the checkbox is focused, and when it is
     // checked and unchecked.
@@ -241,6 +255,20 @@
    * @param {!Event} e
    * @protected
    */
+  handleOpenAutofillMetadataDialog_(e) {
+    // The default behavior of clicking on an anchor tag
+    // with href="#" is a scroll to the top of the page.
+    // This link opens a dialog, so we want to prevent
+    // this default behavior.
+    e.preventDefault();
+
+    // TODO(crbug.com/1407646): Open autofill metadata dialog.
+  }
+
+  /**
+   * @param {!Event} e
+   * @protected
+   */
   handleOpenBluetoothLogsInfoDialog_(e) {
     // The default behavior of clicking on an anchor tag
     // with href="#" is a scroll to the top of the page.
@@ -372,6 +400,14 @@
       report.sendBluetoothLogs = false;
     }
 
+    if (this.feedbackContext.fromAutofill &&
+        !this.getElement_('#autofillCheckboxContainer').hidden &&
+        this.getElement_('#autofillCheckbox').checked) {
+      report.includeAutofillMetadata = true;
+      report.feedbackContext.autofillMetadata =
+          this.feedbackContext.autofillMetadata;
+    }
+
     if (this.getElement_('#performanceTraceCheckbox').checked) {
       report.feedbackContext.traceId = this.feedbackContext.traceId;
     } else {
@@ -442,6 +478,18 @@
   }
 
   /** @private */
+  setAutofillCheckboxLabelAndAttributes_() {
+    this.autofillCheckboxLabel_ =
+        this.i18nAdvanced('includeAutofillCheckboxLabel', {attrs: ['id']});
+
+    const assistantLogsLink = this.getElement_('#autofillMetadataUrl');
+    // Setting href causes <a> tag to display as link.
+    assistantLogsLink.setAttribute('href', '#');
+    assistantLogsLink.addEventListener(
+        'click', (e) => void this.handleOpenAutofillMetadataDialog_(e));
+  }
+
+  /** @private */
   setPerformanceTraceCheckboxLabel_() {
     this.performanceTraceCheckboxLabel_ = this.i18nAdvanced(
         'includePerformanceTraceCheckboxLabel', {attrs: ['id']});
diff --git a/base/base_paths_android.cc b/base/base_paths_android.cc
index aefdd9f..428c73a 100644
--- a/base/base_paths_android.cc
+++ b/base/base_paths_android.cc
@@ -15,6 +15,7 @@
 #include "base/base_paths.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/logging.h"
 #include "base/notreached.h"
 #include "base/process/process_metrics.h"
 
@@ -25,7 +26,9 @@
     case base::FILE_EXE: {
       FilePath bin_dir;
       if (!ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) {
-        NOTREACHED() << "Unable to resolve " << kProcSelfExe << ".";
+        // This fails for some devices (maybe custom OEM selinux policy?)
+        // https://crbug.com/1416753
+        LOG(ERROR) << "Unable to resolve " << kProcSelfExe << ".";
         return false;
       }
       *result = bin_dir;
diff --git a/base/process/launch.h b/base/process/launch.h
index d93e52c..6b5285297 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -447,8 +447,8 @@
 // GetAppOutputInternal() to join a process. GetAppOutputInternal() can't itself
 // be a friend of ScopedAllowBaseSyncPrimitives because it is in the anonymous
 // namespace.
-class GetAppOutputScopedAllowBaseSyncPrimitives
-    : public base::ScopedAllowBaseSyncPrimitives {};
+class [[maybe_unused, nodiscard]] GetAppOutputScopedAllowBaseSyncPrimitives
+    : public base::ScopedAllowBaseSyncPrimitives{};
 
 }  // namespace internal
 
diff --git a/base/process/process_unittest.cc b/base/process/process_unittest.cc
index ae7b17bf..5af7466 100644
--- a/base/process/process_unittest.cc
+++ b/base/process/process_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/process/process.h"
 
+#include <memory>
 #include <string>
 #include <utility>
 
@@ -250,6 +251,7 @@
 }
 
 class ThreadLocalObject {
+ public:
   ~ThreadLocalObject() {
     // Thread-local storage should not be destructed at
     // Process::TerminateCurrentProcessImmediately.
@@ -258,7 +260,8 @@
 };
 
 MULTIPROCESS_TEST_MAIN(TerminateCurrentProcessImmediatelyWithCode0) {
-  base::ThreadLocalPointer<ThreadLocalObject> object;
+  base::ThreadLocalOwnedPointer<ThreadLocalObject> object;
+  object.Set(std::make_unique<ThreadLocalObject>());
   base::AtExitManager::RegisterCallback(&AtExitHandler, nullptr);
   Process::TerminateCurrentProcessImmediately(0);
 }
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 957f5fb7..699a5145 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -546,6 +546,7 @@
 
   // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting
   // in unit tests to avoid the friend requirement.
+  // Sorted by class name (with namespace), #if blocks at the bottom.
   friend class ::BrowserThemePack;  // http://crbug.com/80206
   friend class ::DesktopNotificationBalloon;
   friend class ::FirefoxProfileLock;
@@ -553,8 +554,10 @@
   friend class ::ProfileImpl;
   friend class ::ScopedAllowBlockingForProfile;
   friend class ::StartupTabProviderImpl;
+  friend class ::WebEngineBrowserMainParts;
   friend class android_webview::ScopedAllowInitGLBindings;
   friend class ash::BrowserDataBackMigrator;
+  friend class ash::LoginEventRecorder;
   friend class ash::MojoUtils;                     // http://crbug.com/1055467
   friend class ash::StartupCustomizationDocument;  // http://crosbug.com/11103
   friend class ash::StartupUtils;
@@ -570,20 +573,15 @@
   friend class base::win::ScopedAllowBlockingForUserAccountControl;
   friend class blink::DiskDataAllocator;
   friend class chromecast::CrashUtil;
-  friend class ash::LoginEventRecorder;
   friend class content::BrowserProcessIOThread;
   friend class content::DWriteFontProxyImpl;
   friend class content::NetworkServiceInstancePrivate;
   friend class content::PepperPrintSettingsManagerImpl;
   friend class content::RenderProcessHostImpl;
   friend class content::RenderWidgetHostViewMac;  // http://crbug.com/121917
-  friend class content::ShellPathProvider;
   friend class content::
       ScopedAllowBlockingForViewAura;  // http://crbug.com/332579
-#if BUILDFLAG(IS_WIN)
-  friend class base::win::OSInfo;
-  friend class content::WebContentsImpl;  // http://crbug.com/1262162
-#endif
+  friend class content::ShellPathProvider;
   friend class content::WebContentsViewMac;
   friend class cronet::CronetContext;
   friend class cronet::CronetPrefsManager;
@@ -606,16 +604,13 @@
   friend class net::
       ScopedAllowBlockingForSettingGetter;  // http://crbug.com/69057
   friend class printing::LocalPrinterHandlerDefault;
-#if BUILDFLAG(IS_MAC)
-  friend class printing::PrintBackendServiceImpl;
-#endif
   friend class printing::PrintBackendServiceManager;
   friend class printing::PrinterQuery;
   friend class remote_cocoa::
       DroppedScreenShotCopierMac;  // https://crbug.com/1148078
-  friend class ::WebEngineBrowserMainParts;
   friend class remote_cocoa::SelectFileDialogBridge;
-  friend class remoting::ScopedBypassIOThreadRestrictions;  // crbug.com/1144161
+  friend class remoting::
+      ScopedBypassIOThreadRestrictions;  // http://crbug.com/1144161
   friend class ui::DrmDisplayHostManager;
   friend class ui::ScopedAllowBlockingForGbmSurface;
   friend class ui::SelectFileDialogLinux;
@@ -624,26 +619,34 @@
   friend class weblayer::ContentBrowserClientImpl;
   friend class weblayer::ProfileImpl;
   friend class weblayer::WebLayerPathProvider;
+#if BUILDFLAG(IS_MAC)
+  friend class printing::PrintBackendServiceImpl;
+#endif
+#if BUILDFLAG(IS_WIN)
+  friend class base::win::OSInfo;
+  friend class content::WebContentsImpl;  // http://crbug.com/1262162
+#endif
 
-  // Sorting with function name (with namespace), ignoring the return type.
-  friend void base::GetNSExecutablePath(base::FilePath*);
-  friend base::File content::CreateFileForDrop(
-      base::FilePath* file_path);         // http://crbug.com/110709
+  // Sorted by function name (with namespace), ignoring the return type.
   friend bool ::EnsureBrowserStateDirectoriesCreated(const base::FilePath&,
                                                      const base::FilePath&,
                                                      const base::FilePath&);
-  friend Profile* ::GetLastProfileMac();  // crbug.com/1176734
-  friend bool gl::init::InitializeStaticGLBindings(gl::GLImplementationParts);
-  friend bool ::HasWaylandDisplay(base::Environment* env);  // crbug.com/1246928
+  friend Profile* ::GetLastProfileMac();  // http://crbug.com/1176734
+  friend bool ::HasWaylandDisplay(
+      base::Environment* env);  // http://crbug.com/1246928
+  friend bool ash::CameraAppUIShouldEnableLocalOverride(const std::string&);
+  friend void base::GetNSExecutablePath(base::FilePath*);
+  friend bool base::internal::ReadProcFile(const FilePath& file,
+                                           std::string* buffer);
   friend bool chrome::PathProvider(int,
                                    base::FilePath*);  // http://crbug.com/259796
   friend void chrome::SessionEnding();
-  friend bool ash::CameraAppUIShouldEnableLocalOverride(const std::string&);
-  friend bool base::internal::ReadProcFile(const FilePath& file,
-                                           std::string* buffer);
   friend bool chromeos::system::IsCoreSchedulingAvailable();
   friend int chromeos::system::NumberOfPhysicalCores();
+  friend base::File content::CreateFileForDrop(
+      base::FilePath* file_path);  // http://crbug.com/110709
   friend bool disk_cache::CleanupDirectorySync(const base::FilePath&);
+  friend bool gl::init::InitializeStaticGLBindings(gl::GLImplementationParts);
 
   ScopedAllowBlocking(const Location& from_here = Location::Current());
   ~ScopedAllowBlocking();
@@ -708,10 +711,12 @@
                            ScopedAllowBaseSyncPrimitivesWithBlockingDisallowed);
 
   // Allowed usage:
+  // Sorted by class name (with namespace).
   friend class ::ChromeNSSCryptoModuleDelegate;
+  friend class ::tracing::FuchsiaPerfettoProducerConnector;
   friend class android_webview::JsSandboxIsolate;
-  friend class base::internal::GetAppOutputScopedAllowBaseSyncPrimitives;
   friend class base::SimpleThread;
+  friend class base::internal::GetAppOutputScopedAllowBaseSyncPrimitives;
   friend class blink::CategorizedWorkerPoolImpl;
   friend class blink::CategorizedWorkerPoolJob;
   friend class blink::IdentifiabilityActiveSampler;
@@ -723,8 +728,8 @@
   friend class chrome_cleaner::SystemReportComponent;
   friend class content::BrowserMainLoop;
   friend class content::BrowserProcessIOThread;
-  friend class content::RendererBlinkPlatformImpl;
   friend class content::DWriteFontCollectionProxy;
+  friend class content::RendererBlinkPlatformImpl;
   friend class content::ServiceWorkerContextClient;
   friend class device::UsbContext;
   friend class enterprise_connectors::LinuxKeyRotationCommand;
@@ -748,9 +753,9 @@
   friend class updater::SystemctlLauncherScopedAllowBaseSyncPrimitives;
   friend class viz::ClientGpuMemoryBufferManager;
   friend class webrtc::DesktopConfigurationMonitor;
-  friend class ::tracing::FuchsiaPerfettoProducerConnector;
 
   // Usage that should be fixed:
+  // Sorted by class name (with namespace).
   friend class ::NativeBackendKWallet;  // http://crbug.com/125331
   friend class ::ash::system::
       StatisticsProviderImpl;                      // http://crbug.com/125385
@@ -783,6 +788,7 @@
       ScopedAllowBaseSyncPrimitivesOutsideBlockingScopeResetsState);
 
   // Allowed usage:
+  // Sorted by class name (with namespace).
   friend class ::BrowserProcessImpl;  // http://crbug.com/125207
   friend class ::KeyStorageLinux;
   friend class ::NativeDesktopMediaList;
@@ -792,29 +798,29 @@
   friend class android_webview::CookieManager;
   friend class android_webview::VizCompositorThreadRunnerWebView;
   friend class audio::OutputDevice;
-  friend class base::sequence_manager::internal::TaskQueueImpl;
   friend class base::FileDescriptorWatcher;
-  friend class base::internal::JobTaskSource;
   friend class base::ScopedAllowThreadRecallForStackSamplingProfiler;
   friend class base::StackSamplingProfiler;
+  friend class base::internal::JobTaskSource;
+  friend class base::sequence_manager::internal::TaskQueueImpl;
   friend class blink::CategorizedWorkerPoolImpl;
   friend class blink::CategorizedWorkerPoolJob;
   friend class blink::CategorizedWorkerPool;
+  friend class blink::LegacyWebRtcVideoFrameAdapter;
   friend class blink::RTCVideoDecoderAdapter;
   friend class blink::RTCVideoEncoder;
   friend class blink::WebRtcVideoFrameAdapter;
-  friend class blink::LegacyWebRtcVideoFrameAdapter;
   friend class cc::TileTaskManagerImpl;
   friend class content::DesktopCaptureDevice;
   friend class content::EmergencyTraceFinalisationCoordinator;
   friend class content::InProcessUtilityThread;
+  friend class content::RenderProcessHost;
   friend class content::RTCVideoDecoder;
   friend class content::SandboxHostLinux;
   friend class content::ScopedAllowWaitForDebugURL;
   friend class content::SynchronousCompositor;
   friend class content::SynchronousCompositorHost;
   friend class content::SynchronousCompositorSyncCallBridge;
-  friend class content::RenderProcessHost;
   friend class media::AudioInputDevice;
   friend class media::AudioOutputDevice;
   friend class media::PaintCanvasVideoRenderer;
@@ -831,16 +837,17 @@
   friend class cc::CompletionEvent;               // http://crbug.com/902653
   friend class content::
       BrowserGpuChannelHostFactory;                 // http://crbug.com/125248
+  friend class content::TextInputClientMac;         // http://crbug.com/121917
   friend class dbus::Bus;                           // http://crbug.com/125222
   friend class discardable_memory::
       ClientDiscardableSharedMemoryManager;         // http://crbug.com/1396355
   friend class disk_cache::BackendImpl;             // http://crbug.com/74623
   friend class disk_cache::InFlightIO;              // http://crbug.com/74623
   friend class midi::TaskService;                   // https://crbug.com/796830
-  friend class net::internal::AddressTrackerLinux;  // http://crbug.com/125097
   friend class net::
       MultiThreadedProxyResolverScopedAllowJoinOnIO;  // http://crbug.com/69710
   friend class net::NetworkChangeNotifierMac;         // http://crbug.com/125097
+  friend class net::internal::AddressTrackerLinux;    // http://crbug.com/125097
   friend class proxy_resolver::
       ScopedAllowThreadJoinForProxyResolverV8Tracing;  // http://crbug.com/69710
   friend class remoting::AutoThread;  // https://crbug.com/944316
@@ -850,8 +857,7 @@
       ScopedAllowThreadJoinForWebRtcTransport;  // http://crbug.com/660081
   // Not used in production yet, https://crbug.com/844078.
   friend class service_manager::ServiceProcessLauncher;
-  friend class ui::WindowResizeHelperMac;    // http://crbug.com/902829
-  friend class content::TextInputClientMac;  // http://crbug.com/121917
+  friend class ui::WindowResizeHelperMac;  // http://crbug.com/902829
 
   ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(
       const Location& from_here = Location::Current());
@@ -963,6 +969,7 @@
   PermanentThreadAllowance() = delete;
 
  private:
+  // Sorted by class name (with namespace)
   friend class base::TestCustomDisallow;
   friend class content::BrowserMainLoop;
   friend class content::BrowserTestBase;
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 03155ba..c8755f2 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-12.20230308.2.1
+12.20230309.0.1
diff --git a/chrome/VERSION b/chrome/VERSION
index 0669606..5163498 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=113
 MINOR=0
-BUILD=5640
+BUILD=5641
 PATCH=0
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c9266ef7..b54cad1 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -3417,6 +3417,12 @@
   <message name="IDS_NETWORK_UI_NETWORK_LOGS_DEBUGGING_UNKNOWN" desc="Text displayed when the Shill debugging mode is unknown.">
     Unknown
   </message>
+  <message name="IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_TEXT" desc="Text displayed on the button for rendering network select.">
+    Render Network Select
+  </message>
+  <message name="IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_DESCRIPTION" desc="Text displayed above the render button to inform users the impact of rendering network select.">
+    Render network select list will trigger WiFi to scan frequently; this may affect your WiFi network performance.
+  </message>
   <message name="IDS_NETWORK_UI_NETWORK_METRICS_LABEL" desc="Title for the section for displaying WiFi performance metrics.">
     WiFi Performance Metrics
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_DESCRIPTION.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..d96b425
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+0614f14cc2348630421c8fe588ef0a8160a226ac
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_TEXT.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..002523f56
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+a99fda5c9e964058073a75fe77bdd258a6fddb42
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5329d93..a3a5b2b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5134,6 +5134,7 @@
       "//chromeos/ash/components/memory",
       "//chromeos/ash/components/network",
       "//chromeos/ash/components/settings",
+      "//chromeos/ash/components/standalone_browser",
       "//chromeos/ash/components/sync_wifi",
       "//chromeos/ash/components/system",
       "//chromeos/ash/components/timezone",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4fb09af..d6d7f2fa 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5014,6 +5014,13 @@
      SINGLE_VALUE_TYPE(ui_devtools::switches::kEnableUiDevTools)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+    {"enable-autofill-virtual-cards-on-touch-to-fill",
+     flag_descriptions::kAutofillVirtualCardsOnTouchToFillAndroidName,
+     flag_descriptions::kAutofillVirtualCardsOnTouchToFillAndroidDescription,
+     kOsAll,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillVirtualCardsOnTouchToFillAndroid)},
+
 #if BUILDFLAG(IS_ANDROID)
     {"enable-autofill-manual-fallback",
      flag_descriptions::kAutofillManualFallbackAndroidName,
@@ -6676,6 +6683,10 @@
      flag_descriptions::kEnableNeuralStylusPalmRejectionDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ui::kEnableNeuralPalmDetectionFilter)},
 
+    {"enable-edge-detection", flag_descriptions::kEnableEdgeDetectionName,
+     flag_descriptions::kEnableEdgeDetectionDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ui::kEnableEdgeDetection)},
+
     {"enable-os-feedback", flag_descriptions::kEnableOsFeedbackName,
      flag_descriptions::kEnableOsFeedbackDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kOsFeedback)},
diff --git a/chrome/browser/apps/app_service/app_icon/app_icon_decoder.cc b/chrome/browser/apps/app_service/app_icon/app_icon_decoder.cc
index f1e00aed..cd8e3b9 100644
--- a/chrome/browser/apps/app_service/app_icon/app_icon_decoder.cc
+++ b/chrome/browser/apps/app_service/app_icon/app_icon_decoder.cc
@@ -27,8 +27,6 @@
 
 namespace apps {
 
-bool g_decode_request_for_testing = false;
-
 AppIconDecoder::ImageSource::ImageSource(int32_t size_in_dip)
     : size_in_dip_(size_in_dip) {}
 
@@ -68,38 +66,6 @@
   host_.DiscardDecodeRequest();
 }
 
-AppIconDecoder::FakeDecodeRequestForTesting::FakeDecodeRequestForTesting(
-    ui::ResourceScaleFactor scale_factor,
-    AppIconDecoder& host,
-    gfx::ImageSkia& image_skia,
-    std::set<ui::ResourceScaleFactor>& incomplete_scale_factors)
-    : scale_factor_(scale_factor),
-      host_(host),
-      image_skia_(image_skia),
-      incomplete_scale_factors_(incomplete_scale_factors) {}
-
-void AppIconDecoder::FakeDecodeRequestForTesting::Start(
-    std::vector<uint8_t> icon_data) {
-  CompressedDataToSkBitmap(
-      std::move(icon_data),
-      base::BindOnce(
-          &AppIconDecoder::FakeDecodeRequestForTesting::DecodeRequestReply,
-          weak_ptr_factory_.GetWeakPtr()));
-}
-
-AppIconDecoder::FakeDecodeRequestForTesting::~FakeDecodeRequestForTesting() =
-    default;
-
-void AppIconDecoder::FakeDecodeRequestForTesting::DecodeRequestReply(
-    SkBitmap bitmap) {
-  if (!bitmap.isNull()) {
-    host_.UpdateImageSkia(scale_factor_, bitmap, image_skia_,
-                          incomplete_scale_factors_);
-  } else {
-    host_.DiscardDecodeRequest();
-  }
-}
-
 AppIconDecoder::AppIconDecoder(
     const base::FilePath& base_path,
     const std::string& app_id,
@@ -204,14 +170,6 @@
     std::vector<uint8_t> icon_data,
     gfx::ImageSkia& image_skia,
     std::set<ui::ResourceScaleFactor>& incomplete_scale_factors) {
-  if (g_decode_request_for_testing) {
-    fake_decode_requests_for_testing_.emplace_back(
-        std::make_unique<FakeDecodeRequestForTesting>(
-            scale_factor, *this, image_skia, incomplete_scale_factors));
-    fake_decode_requests_for_testing_.back().get()->Start(std::move(icon_data));
-    return;
-  }
-
   decode_requests_.emplace_back(std::make_unique<DecodeRequest>(
       scale_factor, *this, image_skia, incomplete_scale_factors));
   ImageDecoder::Start(decode_requests_.back().get(), std::move(icon_data));
@@ -271,12 +229,4 @@
   std::move(callback_).Run(this, std::move(iv));
 }
 
-ScopedDecodeRequestForTesting::ScopedDecodeRequestForTesting() {
-  g_decode_request_for_testing = true;
-}
-
-ScopedDecodeRequestForTesting::~ScopedDecodeRequestForTesting() {
-  g_decode_request_for_testing = false;
-}
-
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/app_icon/app_icon_decoder.h b/chrome/browser/apps/app_service/app_icon/app_icon_decoder.h
index a0baf03f..8f9a2e2 100644
--- a/chrome/browser/apps/app_service/app_icon/app_icon_decoder.h
+++ b/chrome/browser/apps/app_service/app_icon/app_icon_decoder.h
@@ -25,11 +25,16 @@
 namespace apps {
 
 // AppIconDecoder reads app icons from the icon image files in the local
-// disk and provides an uncompressed icon, ImageSkia, for UI code to use.
+// disk and provides an uncompressed icon, ImageSkia, for UI code to use. All
+// decoding happens in a sandboxed process.
 //
-// AppIconDecoder is used to decode for one icon uncompressed image only, and
+// AppIconDecoder is used to decode for one icon uncompressed image only, and is
 // owned by AppIconReader. AppIconReader is responsible to free AppIconDecoder's
 // objects once the decode is done.
+//
+// Unit tests which need to decode icons can use
+// data_decoder::test::InProcessDataDecoder to run all decode operations
+// in-process.
 class AppIconDecoder {
  public:
   AppIconDecoder(const base::FilePath& base_path,
@@ -85,32 +90,6 @@
     std::set<ui::ResourceScaleFactor>& incomplete_scale_factors_;
   };
 
-  class FakeDecodeRequestForTesting {
-   public:
-    FakeDecodeRequestForTesting(
-        ui::ResourceScaleFactor scale_factor,
-        AppIconDecoder& host,
-        gfx::ImageSkia& image_skia,
-        std::set<ui::ResourceScaleFactor>& incomplete_scale_factors);
-
-    FakeDecodeRequestForTesting(const FakeDecodeRequestForTesting&) = delete;
-    FakeDecodeRequestForTesting& operator=(const FakeDecodeRequestForTesting&) =
-        delete;
-
-    ~FakeDecodeRequestForTesting();
-
-    void Start(std::vector<uint8_t> icon_data);
-
-   private:
-    void DecodeRequestReply(SkBitmap bitmap);
-
-    ui::ResourceScaleFactor scale_factor_;
-    AppIconDecoder& host_;
-    gfx::ImageSkia& image_skia_;
-    std::set<ui::ResourceScaleFactor>& incomplete_scale_factors_;
-    base::WeakPtrFactory<FakeDecodeRequestForTesting> weak_ptr_factory_{this};
-  };
-
   bool SetScaleFactors(
       const std::map<ui::ResourceScaleFactor, IconValuePtr>& icon_datas);
 
@@ -150,23 +129,9 @@
   // Contains pending image decode requests.
   std::vector<std::unique_ptr<DecodeRequest>> decode_requests_;
 
-  std::vector<std::unique_ptr<FakeDecodeRequestForTesting>>
-      fake_decode_requests_for_testing_;
-
   base::WeakPtrFactory<AppIconDecoder> weak_ptr_factory_{this};
 };
 
-// This class is used for testing only to disable the out-of process icon
-// decoding.
-class ScopedDecodeRequestForTesting {
- public:
-  ScopedDecodeRequestForTesting();
-  ScopedDecodeRequestForTesting(const ScopedDecodeRequestForTesting&) = delete;
-  ScopedDecodeRequestForTesting& operator=(
-      const ScopedDecodeRequestForTesting&) = delete;
-  ~ScopedDecodeRequestForTesting();
-};
-
 }  // namespace apps
 
 #endif  // CHROME_BROWSER_APPS_APP_SERVICE_APP_ICON_APP_ICON_DECODER_H_
diff --git a/chrome/browser/apps/app_service/app_icon/app_icon_factory_unittest.cc b/chrome/browser/apps/app_service/app_icon/app_icon_factory_unittest.cc
index 0be0380..e772689 100644
--- a/chrome/browser/apps/app_service/app_icon/app_icon_factory_unittest.cc
+++ b/chrome/browser/apps/app_service/app_icon/app_icon_factory_unittest.cc
@@ -52,6 +52,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h"
 #include "components/services/app_service/public/cpp/features.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #endif
 
 namespace apps {
@@ -383,8 +384,6 @@
     ash::CiceroneClient::InitializeFake();
     profile_ = std::make_unique<TestingProfile>();
     proxy_ = AppServiceProxyFactory::GetForProfile(profile_.get());
-    scoped_decode_request_for_testing_ =
-        std::make_unique<ScopedDecodeRequestForTesting>();
 
     crostini_test_helper_ =
         std::make_unique<crostini::CrostiniTestHelper>(profile_.get());
@@ -442,8 +441,7 @@
   std::unique_ptr<TestingProfile> profile_;
   raw_ptr<AppServiceProxy> proxy_;
   std::unique_ptr<apps::FakePublisherForIconTest> fake_publisher_;
-  std::unique_ptr<ScopedDecodeRequestForTesting>
-      scoped_decode_request_for_testing_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
 
   std::unique_ptr<crostini::CrostiniTestHelper> crostini_test_helper_;
 
diff --git a/chrome/browser/apps/app_service/app_icon/arc_apps_icon_unittest.cc b/chrome/browser/apps/app_service/app_icon/arc_apps_icon_unittest.cc
index 4410537d5..22b7625 100644
--- a/chrome/browser/apps/app_service/app_icon/arc_apps_icon_unittest.cc
+++ b/chrome/browser/apps/app_service/app_icon/arc_apps_icon_unittest.cc
@@ -17,6 +17,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/services/app_service/public/cpp/features.h"
 #include "content/public/test/browser_task_environment.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/utility/utility.h"
 #include "ui/base/layout.h"
@@ -103,8 +104,6 @@
     ArcAppsIconFactoryTest::SetUp();
 
     proxy_ = AppServiceProxyFactory::GetForProfile(profile());
-    scoped_decode_request_for_testing_ =
-        std::make_unique<ScopedDecodeRequestForTesting>();
   }
 
   void GenerateArcAppUncompressedIcon(const std::string app_id,
@@ -179,8 +178,7 @@
 
  private:
   raw_ptr<AppServiceProxy> proxy_;
-  std::unique_ptr<ScopedDecodeRequestForTesting>
-      scoped_decode_request_for_testing_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
 };
 
 TEST_F(AppServiceArcAppIconTest, GetCompressedIconDataForUncompressedIcon) {
diff --git a/chrome/browser/apps/app_service/app_icon/chrome_apps_icon_unittest.cc b/chrome/browser/apps/app_service/app_icon/chrome_apps_icon_unittest.cc
index 5b00ccb3..23badbce 100644
--- a/chrome/browser/apps/app_service/app_icon/chrome_apps_icon_unittest.cc
+++ b/chrome/browser/apps/app_service/app_icon/chrome_apps_icon_unittest.cc
@@ -21,6 +21,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/image_loader.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/codec/png_codec.h"
@@ -200,8 +201,6 @@
     proxy_ = AppServiceProxyFactory::GetForProfile(profile());
     fake_icon_loader_ = std::make_unique<apps::FakeIconLoader>(proxy_);
     OverrideAppServiceProxyInnerIconLoader(fake_icon_loader_.get());
-    scoped_decode_request_for_testing_ =
-        std::make_unique<ScopedDecodeRequestForTesting>();
   }
 
   void OverrideAppServiceProxyInnerIconLoader(apps::IconLoader* icon_loader) {
@@ -252,8 +251,7 @@
  private:
   raw_ptr<AppServiceProxy> proxy_;
   std::unique_ptr<apps::FakeIconLoader> fake_icon_loader_;
-  std::unique_ptr<ScopedDecodeRequestForTesting>
-      scoped_decode_request_for_testing_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
 };
 
 TEST_F(AppServiceChromeAppIconTest, GetCompressedIconDataForCompressedIcon) {
diff --git a/chrome/browser/apps/app_service/app_icon/guest_os_apps_icon_unittest.cc b/chrome/browser/apps/app_service/app_icon/guest_os_apps_icon_unittest.cc
index 8364600..135c4de 100644
--- a/chrome/browser/apps/app_service/app_icon/guest_os_apps_icon_unittest.cc
+++ b/chrome/browser/apps/app_service/app_icon/guest_os_apps_icon_unittest.cc
@@ -24,6 +24,7 @@
 #include "components/services/app_service/public/cpp/features.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "content/public/test/browser_task_environment.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -65,9 +66,6 @@
     crostini_test_helper_ =
         std::make_unique<crostini::CrostiniTestHelper>(profile());
     crostini_test_helper()->ReInitializeAppServiceIntegration();
-
-    scoped_decode_request_for_testing_ =
-        std::make_unique<ScopedDecodeRequestForTesting>();
   }
 
   void TearDown() override {
@@ -142,8 +140,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   base::raw_ptr<ash::FakeCiceroneClient> fake_cicerone_client_;
   std::unique_ptr<TestingProfile> profile_;
-  std::unique_ptr<ScopedDecodeRequestForTesting>
-      scoped_decode_request_for_testing_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
   base::raw_ptr<AppServiceProxy> proxy_;
   std::unique_ptr<crostini::CrostiniTestHelper> crostini_test_helper_;
 };
diff --git a/chrome/browser/apps/app_service/app_icon/web_app_icon_unittest.cc b/chrome/browser/apps/app_service/app_icon/web_app_icon_unittest.cc
index ace3f9f9..fe52cbc0 100644
--- a/chrome/browser/apps/app_service/app_icon/web_app_icon_unittest.cc
+++ b/chrome/browser/apps/app_service/app_icon/web_app_icon_unittest.cc
@@ -64,12 +64,13 @@
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "components/services/app_service/public/cpp/icon_loader.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "ui/base/resource/resource_scale_factor.h"
 #endif
 
 namespace apps {
 
-class WebAppIconFactoryTest : public ChromeRenderViewHostTestHarness {
+class WebAppIconFactoryTest : public testing::Test {
  public:
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   WebAppIconFactoryTest() {
@@ -83,7 +84,13 @@
   ~WebAppIconFactoryTest() override = default;
 
   void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
+    testing::Test::SetUp();
+
+    TestingProfile::Builder builder;
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    builder.SetIsMainProfile(true);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+    profile_ = builder.Build();
 
     web_app_provider_ = web_app::WebAppProvider::GetForWebApps(profile());
     ASSERT_TRUE(web_app_provider_);
@@ -262,8 +269,12 @@
 
   web_app::WebAppSyncBridge& sync_bridge() { return *sync_bridge_; }
 
+  Profile* profile() { return profile_.get(); }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<TestingProfile> profile_;
   raw_ptr<web_app::WebAppProvider> web_app_provider_;
   raw_ptr<web_app::WebAppIconManager> icon_manager_;
   raw_ptr<web_app::WebAppSyncBridge> sync_bridge_;
@@ -912,8 +923,6 @@
     OverrideAppServiceProxyInnerIconLoader(fake_icon_loader_.get());
     fake_publisher_ =
         std::make_unique<apps::FakePublisherForIconTest>(proxy_, AppType::kWeb);
-    scoped_decode_request_for_testing_ =
-        std::make_unique<ScopedDecodeRequestForTesting>();
   }
 
   void OverrideAppServiceProxyInnerIconLoader(apps::IconLoader* icon_loader) {
@@ -1015,14 +1024,11 @@
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
 
   raw_ptr<AppServiceProxy> proxy_;
   std::unique_ptr<apps::FakeIconLoader> fake_icon_loader_;
   std::unique_ptr<apps::FakePublisherForIconTest> fake_publisher_;
-  std::unique_ptr<ScopedDecodeRequestForTesting>
-      scoped_decode_request_for_testing_;
-
-  base::WeakPtrFactory<AppServiceWebAppIconTest> weak_ptr_factory_{this};
 };
 
 TEST_F(AppServiceWebAppIconTest, GetNonMaskableCompressedIconData) {
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
index 1ee8077..480639ca 100644
--- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -54,9 +54,12 @@
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "components/services/app_service/public/cpp/app_capability_access_cache.h"
 #include "components/services/app_service/public/cpp/capability_access_update.h"
 #include "components/user_manager/scoped_user_manager.h"
+
+using ash::standalone_browser::BrowserSupport;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace {
@@ -670,25 +673,25 @@
   VerifyAppTypeIsInitialized(AppType::kBuiltIn);
 }
 
-class LegacyPackagedAppLacorsNotPrimaryPublisherTest : public PublisherTest {
+class LegacyPackagedAppLacrosNotPrimaryPublisherTest : public PublisherTest {
  public:
-  LegacyPackagedAppLacorsNotPrimaryPublisherTest() {
+  LegacyPackagedAppLacrosNotPrimaryPublisherTest() {
     scoped_feature_list_.Reset();
     scoped_feature_list_.InitAndDisableFeature(ash::features::kLacrosPrimary);
   }
 
-  LegacyPackagedAppLacorsNotPrimaryPublisherTest(
-      const LegacyPackagedAppLacorsNotPrimaryPublisherTest&) = delete;
-  LegacyPackagedAppLacorsNotPrimaryPublisherTest& operator=(
-      const LegacyPackagedAppLacorsNotPrimaryPublisherTest&) = delete;
-  ~LegacyPackagedAppLacorsNotPrimaryPublisherTest() override = default;
+  LegacyPackagedAppLacrosNotPrimaryPublisherTest(
+      const LegacyPackagedAppLacrosNotPrimaryPublisherTest&) = delete;
+  LegacyPackagedAppLacrosNotPrimaryPublisherTest& operator=(
+      const LegacyPackagedAppLacrosNotPrimaryPublisherTest&) = delete;
+  ~LegacyPackagedAppLacrosNotPrimaryPublisherTest() override = default;
 
  private:
-  const base::AutoReset<bool> resettter_ =
-      crosapi::browser_util::SetLacrosEnabledForTest(true);
+  const base::AutoReset<bool> resetter_ =
+      BrowserSupport::SetLacrosEnabledForTest(true);
 };
 
-TEST_F(LegacyPackagedAppLacorsNotPrimaryPublisherTest,
+TEST_F(LegacyPackagedAppLacrosNotPrimaryPublisherTest,
        LegacyPackagedAppsOnApps) {
   ASSERT_FALSE(crosapi::browser_util::IsLacrosPrimaryBrowser());
 
@@ -718,25 +721,25 @@
   VerifyAppTypeIsInitialized(AppType::kChromeApp);
 }
 
-class LegacyPackagedAppLacorsPrimaryPublisherTest : public PublisherTest {
+class LegacyPackagedAppLacrosPrimaryPublisherTest : public PublisherTest {
  public:
-  LegacyPackagedAppLacorsPrimaryPublisherTest() {
+  LegacyPackagedAppLacrosPrimaryPublisherTest() {
     scoped_feature_list_.Reset();
     scoped_feature_list_.InitAndEnableFeature(ash::features::kLacrosPrimary);
   }
 
-  LegacyPackagedAppLacorsPrimaryPublisherTest(
-      const LegacyPackagedAppLacorsNotPrimaryPublisherTest&) = delete;
-  LegacyPackagedAppLacorsPrimaryPublisherTest& operator=(
-      const LegacyPackagedAppLacorsNotPrimaryPublisherTest&) = delete;
-  ~LegacyPackagedAppLacorsPrimaryPublisherTest() override = default;
+  LegacyPackagedAppLacrosPrimaryPublisherTest(
+      const LegacyPackagedAppLacrosNotPrimaryPublisherTest&) = delete;
+  LegacyPackagedAppLacrosPrimaryPublisherTest& operator=(
+      const LegacyPackagedAppLacrosNotPrimaryPublisherTest&) = delete;
+  ~LegacyPackagedAppLacrosPrimaryPublisherTest() override = default;
 
  private:
   base::AutoReset<bool> set_lacros_enabled_ =
-      crosapi::browser_util::SetLacrosEnabledForTest(true);
+      BrowserSupport::SetLacrosEnabledForTest(true);
 };
 
-TEST_F(LegacyPackagedAppLacorsPrimaryPublisherTest, LegacyPackagedAppsOnApps) {
+TEST_F(LegacyPackagedAppLacrosPrimaryPublisherTest, LegacyPackagedAppsOnApps) {
   ASSERT_TRUE(crosapi::browser_util::IsLacrosPrimaryBrowser());
 
   // Re-init AppService to verify the init process.
@@ -843,7 +846,7 @@
 
  private:
   base::AutoReset<bool> set_lacros_enabled_ =
-      crosapi::browser_util::SetLacrosEnabledForTest(true);
+      BrowserSupport::SetLacrosEnabledForTest(true);
   std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
 };
 
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 291ea068..c649c25 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -4767,6 +4767,8 @@
     "app_list/search/keyboard_shortcut_result_unittest.cc",
     "app_list/search/local_images/annotation_storage_unittest.cc",
     "app_list/search/local_images/image_annotation_worker_unittest.cc",
+    "app_list/search/local_images/local_image_search_test_util.cc",
+    "app_list/search/local_images/local_image_search_test_util.h",
     "app_list/search/local_images/sql_database_unittest.cc",
     "app_list/search/omnibox/omnibox_answer_result_unittest.cc",
     "app_list/search/omnibox/omnibox_lacros_provider_unittest.cc",
diff --git a/chrome/browser/ash/account_manager/account_apps_availability_unittest.cc b/chrome/browser/ash/account_manager/account_apps_availability_unittest.cc
index 4493762..62866531 100644
--- a/chrome/browser/ash/account_manager/account_apps_availability_unittest.cc
+++ b/chrome/browser/ash/account_manager/account_apps_availability_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "base/test/test_future.h"
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/mock_account_manager_facade.h"
@@ -63,16 +64,10 @@
 
 base::flat_set<account_manager::Account> GetAccountsAvailableInArcSync(
     AccountAppsAvailability* availability) {
-  base::flat_set<account_manager::Account> result;
-  base::RunLoop run_loop;
-  availability->GetAccountsAvailableInArc(base::BindLambdaForTesting(
-      [&result,
-       &run_loop](const base::flat_set<account_manager::Account>& accounts) {
-        result = accounts;
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-  return result;
+  base::test::TestFuture<const base::flat_set<account_manager::Account>&>
+      future;
+  availability->GetAccountsAvailableInArc(future.GetCallback());
+  return future.Get();
 }
 
 MATCHER_P(AccountEqual, other, "") {
@@ -82,11 +77,13 @@
 }  // namespace
 
 class AccountAppsAvailabilityTest : public testing::Test {
- protected:
-  AccountAppsAvailabilityTest() = default;
+ public:
   AccountAppsAvailabilityTest(const AccountAppsAvailabilityTest&) = delete;
   AccountAppsAvailabilityTest& operator=(const AccountAppsAvailabilityTest&) =
       delete;
+
+ protected:
+  AccountAppsAvailabilityTest() = default;
   ~AccountAppsAvailabilityTest() override = default;
 
   void SetUp() override {
@@ -180,19 +177,13 @@
   // Make secondary account unavailable in ARC.
   account_apps_availability->SetIsAccountAvailableInArc(secondary_account,
                                                         false);
-  base::flat_set<account_manager::Account> result;
-  base::RunLoop run_loop;
-  account_apps_availability->GetAccountsAvailableInArc(
-      base::BindLambdaForTesting(
-          [&result, &run_loop](
-              const base::flat_set<account_manager::Account>& accounts) {
-            result = accounts;
-            run_loop.Quit();
-          }));
 
+  base::test::TestFuture<const base::flat_set<account_manager::Account>&>
+      future;
   // Wait for initialization and for `GetAccountsAvailableInArc` call
   // completion.
-  run_loop.Run();
+  account_apps_availability->GetAccountsAvailableInArc(future.GetCallback());
+  base::flat_set<account_manager::Account> result = future.Get();
   EXPECT_TRUE(account_apps_availability->IsInitialized());
 
   // Only primary account is available, secondary account was removed.
diff --git a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
index 0f31918..7eb7c752 100644
--- a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
+++ b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/constants/ash_pref_names.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
+#include "base/test/test_future.h"
 #include "base/values.h"
 #include "chrome/browser/ash/child_accounts/edu_coexistence_tos_store_utils.h"
 #include "chrome/browser/profiles/profile.h"
@@ -132,19 +133,9 @@
 
 bool AccountManagerEducoexistenceControllerTest::HasInvalidGaiaToken(
     const ::account_manager::Account& account) {
-  base::RunLoop run_loop;
-  bool is_dummy_return = false;
-  account_manager()->HasDummyGaiaToken(
-      account.key, base::BindOnce(
-                       [](const base::RepeatingClosure& run_loop_callback,
-                          bool* out, bool is_invalid) {
-                         *out = is_invalid;
-                         run_loop_callback.Run();
-                       },
-                       run_loop.QuitClosure(), &is_dummy_return));
-  run_loop.Run();
-
-  return is_dummy_return;
+  base::test::TestFuture<bool> future;
+  account_manager()->HasDummyGaiaToken(account.key, future.GetCallback());
+  return future.Get();
 }
 
 TEST_F(AccountManagerEducoexistenceControllerTest,
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
index f1b758f..112c5664 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
@@ -8,8 +8,10 @@
 #include <vector>
 
 #include "base/files/scoped_temp_dir.h"
+#include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/test_future.h"
 #include "build/build_config.h"
 #include "chrome/browser/ash/account_manager/account_manager_policy_controller.h"
 #include "chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h"
@@ -116,19 +118,12 @@
   }
 
   std::vector<::account_manager::Account> GetAccountManagerAccounts() {
-    DCHECK(account_manager_);
+    CHECK(account_manager_facade_);
 
-    std::vector<::account_manager::Account> accounts;
-    base::RunLoop run_loop;
-    account_manager_facade_->GetAccounts(base::BindLambdaForTesting(
-        [&accounts, &run_loop](
-            const std::vector<::account_manager::Account>& stored_accounts) {
-          accounts = stored_accounts;
-          run_loop.Quit();
-        }));
-    run_loop.Run();
-
-    return accounts;
+    base::test::TestFuture<const std::vector<::account_manager::Account>&>
+        future;
+    account_manager_facade_->GetAccounts(future.GetCallback());
+    return future.Get();
   }
 
   Profile* profile() { return profile_.get(); }
@@ -140,10 +135,9 @@
 
  private:
   base::ScopedTempDir temp_dir_;
-  // Non-owning pointer.
-  account_manager::AccountManager* account_manager_ = nullptr;
-  // Non-owning pointer.
-  account_manager::AccountManagerFacade* account_manager_facade_ = nullptr;
+  raw_ptr<account_manager::AccountManager> account_manager_ = nullptr;
+  raw_ptr<account_manager::AccountManagerFacade> account_manager_facade_ =
+      nullptr;
   std::unique_ptr<Profile> profile_;
   std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
       identity_test_environment_adaptor_;
diff --git a/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc
index 2a1228f..f89dc36 100644
--- a/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc
@@ -164,10 +164,7 @@
 
   base::CreateDirectory(icon_path.DirName());
 
-  int wrote = base::WriteFile(icon_path,
-                              reinterpret_cast<const char*>(&icon_png_data[0]),
-                              icon_png_data.size());
-  if (wrote != static_cast<int>(icon_png_data.size())) {
+  if (!base::WriteFile(icon_path, icon_png_data)) {
     VLOG(2) << "Failed to write ARC icon file: " << icon_path.MaybeAsASCII()
             << ".";
     if (!base::DeleteFile(icon_path)) {
diff --git a/chrome/browser/ash/app_list/arc/arc_app_unittest.cc b/chrome/browser/ash/app_list/arc/arc_app_unittest.cc
index 013682ab..a36314d 100644
--- a/chrome/browser/ash/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ash/app_list/arc/arc_app_unittest.cc
@@ -101,6 +101,7 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_constants.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/types/display_constants.h"
@@ -956,9 +957,6 @@
   void SetUp() override {
     ArcAppModelBuilderRecreate::SetUp();
 
-    scoped_decode_request_for_testing_ =
-        std::make_unique<apps::ScopedDecodeRequestForTesting>();
-
     std::vector<ui::ResourceScaleFactor> supported_scale_factors;
     supported_scale_factors.push_back(ui::k100Percent);
     supported_scale_factors.push_back(ui::k200Percent);
@@ -1227,8 +1225,7 @@
   }
 
  private:
-  std::unique_ptr<apps::ScopedDecodeRequestForTesting>
-      scoped_decode_request_for_testing_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
   std::unique_ptr<ui::test::ScopedSetSupportedResourceScaleFactors>
       scoped_supported_scale_factors_;
   std::unique_ptr<base::RunLoop> run_loop_;
diff --git a/chrome/browser/ash/app_list/search/chrome_search_result.cc b/chrome/browser/ash/app_list/search/chrome_search_result.cc
index f4b5651..ac0d6ea 100644
--- a/chrome/browser/ash/app_list/search/chrome_search_result.cc
+++ b/chrome/browser/ash/app_list/search/chrome_search_result.cc
@@ -196,6 +196,12 @@
   SetSearchResultMetadata();
 }
 
+void ChromeSearchResult::SetSystemInfoAnswerCardData(
+    ash::SystemInfoAnswerCardData answer_card_info) {
+  metadata_->system_info_answer_card_data = answer_card_info;
+  SetSearchResultMetadata();
+}
+
 void ChromeSearchResult::SetSearchResultMetadata() {
   AppListModelUpdater* updater = model_updater();
   if (updater)
diff --git a/chrome/browser/ash/app_list/search/chrome_search_result.h b/chrome/browser/ash/app_list/search/chrome_search_result.h
index b151591..a999aa6 100644
--- a/chrome/browser/ash/app_list/search/chrome_search_result.h
+++ b/chrome/browser/ash/app_list/search/chrome_search_result.h
@@ -101,6 +101,10 @@
   const IconInfo& icon() const { return metadata_->icon; }
   const gfx::ImageSkia& chip_icon() const { return metadata_->chip_icon; }
   const ui::ImageModel& badge_icon() const { return metadata_->badge_icon; }
+  const absl::optional<ash::SystemInfoAnswerCardData>
+  system_info_answer_card_data() const {
+    return metadata_->system_info_answer_card_data;
+  }
 
   // The following methods set Chrome side data here, and call model updater
   // interface to update Ash.
@@ -135,6 +139,8 @@
   void SetChipIcon(const gfx::ImageSkia& icon);
   void SetBadgeIcon(const ui::ImageModel& badge_icon);
   void SetUseBadgeIconBackground(bool use_badge_icon_background);
+  void SetSystemInfoAnswerCardData(
+      ash::SystemInfoAnswerCardData answer_card_info);
 
   void SetSearchResultMetadata();
 
@@ -209,6 +215,7 @@
   // Only used when the categorical search flag is enabled.
   app_list::Scoring scoring_;
 
+  // This field specifies the omnibox answer card type.
   crosapi::mojom::SearchResult::AnswerType answer_type_;
 
   // Relevance scores keyed by a string describing the ranking method it was
diff --git a/chrome/browser/ash/app_list/search/local_images/annotation_storage.cc b/chrome/browser/ash/app_list/search/local_images/annotation_storage.cc
index 6c619e4..8e4deb3 100644
--- a/chrome/browser/ash/app_list/search/local_images/annotation_storage.cc
+++ b/chrome/browser/ash/app_list/search/local_images/annotation_storage.cc
@@ -88,65 +88,37 @@
 FileSearchResult::FileSearchResult(const FileSearchResult&) = default;
 
 AnnotationStorage::AnnotationStorage(
-    const base::FilePath& path,
+    const base::FilePath& path_to_db,
     const std::string& histogram_tag,
     int current_version_number,
     std::unique_ptr<ImageAnnotationWorker> annotation_worker)
     : annotation_worker_(std::move(annotation_worker)),
       sql_database_(
-          std::make_unique<SqlDatabase>(path,
+          std::make_unique<SqlDatabase>(path_to_db,
                                         histogram_tag,
                                         current_version_number,
                                         base::BindRepeating(CreateNewSchema),
-                                        base::BindRepeating(MigrateSchema))),
-      background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
-           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {
-  DETACH_FROM_SEQUENCE(sequence_checker_);
+                                        base::BindRepeating(MigrateSchema))) {
   DVLOG(1) << "Construct AnnotationStorage";
 }
 
-AnnotationStorage::~AnnotationStorage() {
-  // Closes the worker in the same sequence it was initialized.
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          [](std::unique_ptr<ImageAnnotationWorker> annotation_worker,
-             std::unique_ptr<SqlDatabase> sql_database) {
-            annotation_worker.reset();
-            sql_database->Close();
-          },
-          std::move(annotation_worker_), std::move(sql_database_)));
-}
+AnnotationStorage::~AnnotationStorage() = default;
 
-void AnnotationStorage::InitializeAsync() {
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AnnotationStorage::InitializeOnBackgroundSequence, this));
-}
-
-void AnnotationStorage::InitializeOnBackgroundSequence() {
+void AnnotationStorage::Initialize() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!sql_database_->Initialize()) {
     LOG(ERROR) << "Failed to initialize the db.";
     return;
   }
   if (annotation_worker_ != nullptr) {
+    // Owns `annotation_worker_`.
     annotation_worker_->Run(this);
   }
 }
 
-void AnnotationStorage::InsertOrReplaceAsync(ImageInfo image_info) {
-  DVLOG(1) << "InsertOrReplaceAsync";
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          base::IgnoreResult(&AnnotationStorage::InsertOnBackgroundSequence),
-          this, std::move(image_info)));
-}
-
-void AnnotationStorage::InsertOnBackgroundSequence(ImageInfo image_info) {
+void AnnotationStorage::Insert(const ImageInfo& image_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DVLOG(1) << "Insert";
 
   static constexpr char kQuery[] =
       // clang-format off
@@ -170,17 +142,9 @@
   return;
 }
 
-void AnnotationStorage::RemoveAsync(base::FilePath image_path) {
-  DVLOG(1) << "RemoveAsync";
-  background_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          base::IgnoreResult(&AnnotationStorage::RemoveOnBackgroundSequence),
-          this, std::move(image_path)));
-}
-
-void AnnotationStorage::RemoveOnBackgroundSequence(base::FilePath image_path) {
+void AnnotationStorage::Remove(const base::FilePath& image_path) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DVLOG(1) << "Remove";
 
   static constexpr char kQuery[] = "DELETE FROM annotations WHERE image_path=?";
 
@@ -191,19 +155,9 @@
   statement.Run();
 }
 
-void AnnotationStorage::GetAllAnnotationsAsync(
-    base::OnceCallback<void(std::vector<ImageInfo>)> callback) {
-  DVLOG(1) << "GetAllAnnotationsAsync";
-  background_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&AnnotationStorage::GetAllAnnotationsOnBackgroundSequence,
-                     this),
-      std::move(callback));
-}
-
-std::vector<ImageInfo>
-AnnotationStorage::GetAllAnnotationsOnBackgroundSequence() {
+std::vector<ImageInfo> AnnotationStorage::GetAllAnnotations() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DVLOG(1) << "GetAllAnnotations";
 
   static constexpr char kQuery[] =
       // clang-format off
@@ -224,24 +178,15 @@
     matched_paths.push_back(
         {{statement.ColumnString(0)}, std::move(path), std::move(time)});
   }
+
   return matched_paths;
 }
 
-void AnnotationStorage::FindImagePathAsync(
-    base::FilePath image_path,
-    base::OnceCallback<void(std::vector<ImageInfo>)> callback) {
-  DVLOG(1) << "FindImagePathAsync " << image_path;
-  background_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&AnnotationStorage::FindImagePathOnBackgroundSequence,
-                     this, image_path),
-      std::move(callback));
-}
-
-std::vector<ImageInfo> AnnotationStorage::FindImagePathOnBackgroundSequence(
-    base::FilePath image_path) {
+std::vector<ImageInfo> AnnotationStorage::FindImagePath(
+    const base::FilePath& image_path) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!image_path.empty());
+  DVLOG(1) << "FindImagePath " << image_path;
 
   static constexpr char kQuery[] =
       // clang-format off
@@ -264,25 +209,14 @@
     matched_paths.push_back(
         {{statement.ColumnString(0)}, std::move(path), std::move(time)});
   }
+
   return matched_paths;
 }
 
-void AnnotationStorage::LinearSearchAnnotationsAsync(
-    std::u16string query,
-    base::OnceCallback<void(std::vector<FileSearchResult>)> callback) {
-  DVLOG(1) << "LinearSearchAnnotationsAsync";
-  background_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(
-          &AnnotationStorage::LinearSearchAnnotationsOnBackgroundSequence, this,
-          std::move(query)),
-      std::move(callback));
-}
-
-std::vector<FileSearchResult>
-AnnotationStorage::LinearSearchAnnotationsOnBackgroundSequence(
-    std::u16string query) {
+std::vector<FileSearchResult> AnnotationStorage::LinearSearchAnnotations(
+    const std::u16string& query) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DVLOG(1) << "LinearSearchAnnotationsAsync";
   using TokenizedString = ash::string_matching::TokenizedString;
 
   static constexpr char kQuery[] =
diff --git a/chrome/browser/ash/app_list/search/local_images/annotation_storage.h b/chrome/browser/ash/app_list/search/local_images/annotation_storage.h
index 740ed30..8225377 100644
--- a/chrome/browser/ash/app_list/search/local_images/annotation_storage.h
+++ b/chrome/browser/ash/app_list/search/local_images/annotation_storage.h
@@ -13,6 +13,7 @@
 #include "base/functional/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 
@@ -59,9 +60,9 @@
 };
 
 // A persistent storage to efficiently store, retrieve and search annotations.
-// It maintains and runs tasks on its own background task runner.
-// Constructor and all *Async() methods can be called on any sequence.
-class AnnotationStorage : public base::RefCountedThreadSafe<AnnotationStorage> {
+// Creates or opens a database under `path_to_db`. If `annotation_worker` is
+// not null, it updates the database on file changes.
+class AnnotationStorage {
  public:
   enum class TableColumnName {
     kLabel,
@@ -69,67 +70,39 @@
     kLastModifiedTime,
   };
 
-  AnnotationStorage(const base::FilePath& path,
+  AnnotationStorage(const base::FilePath& path_to_db,
                     const std::string& histogram_tag,
                     int current_version_number,
                     std::unique_ptr<ImageAnnotationWorker> annotation_worker);
   AnnotationStorage(const AnnotationStorage&) = delete;
   AnnotationStorage& operator=(const AnnotationStorage&) = delete;
+  ~AnnotationStorage();
 
   // Initializes the db. Must be called before any other method.
-  // Can be called from any sequence.
-  void InitializeAsync();
+  void Initialize();
 
-  // Adds a new image to the storage. Can be called from any sequence.
-  void InsertOrReplaceAsync(ImageInfo image_info);
+  // Adds a new image to the storage.
+  void Insert(const ImageInfo& image_info);
 
   // Removes an image from the storage. It does nothing if the file does not
-  // exist. Can be called from any sequence.
-  void RemoveAsync(base::FilePath image_path);
+  void Remove(const base::FilePath& image_path);
 
   // TODO(b/260646344): Remove after implementing a more efficient search.
-  // Returns all the stored annotations. Can be called from any sequence.
-  void GetAllAnnotationsAsync(
-      base::OnceCallback<void(std::vector<ImageInfo>)> callback);
+  // Returns all the stored annotations.
+  std::vector<ImageInfo> GetAllAnnotations();
 
   // Searches the database for a desired `image_path`.
-  // Can be called from any sequence.
-  void FindImagePathAsync(
-      base::FilePath image_path,
-      base::OnceCallback<void(std::vector<ImageInfo>)> callback);
+  std::vector<ImageInfo> FindImagePath(const base::FilePath& image_path);
 
   // Searches for annotations using FuzzyTokenizedStringMatch with relevance to
-  // `query` above a fixed threshold. Can be called from any sequence.
-  void LinearSearchAnnotationsAsync(
-      std::u16string query,
-      base::OnceCallback<void(std::vector<FileSearchResult>)> callback);
+  // `query` above a fixed threshold.
+  std::vector<FileSearchResult> LinearSearchAnnotations(
+      const std::u16string& query);
 
  private:
-  friend class base::RefCountedThreadSafe<AnnotationStorage>;
-  ~AnnotationStorage();
-  // Runs the worker in the background.
-  void InitializeOnBackgroundSequence();
-  void InsertOnBackgroundSequence(ImageInfo image_info);
-  void RemoveOnBackgroundSequence(base::FilePath image_path);
-
-  // Yields all annotations in the db.
-  std::vector<ImageInfo> GetAllAnnotationsOnBackgroundSequence();
-
-  // Searches the database for a desired `image_path`.
-  std::vector<ImageInfo> FindImagePathOnBackgroundSequence(
-      base::FilePath image_path);
-
-  // Searches annotations using FuzzyTokenizedStringMatch.
-  std::vector<FileSearchResult> LinearSearchAnnotationsOnBackgroundSequence(
-      std::u16string query);
-
-  // Initialized and operates in the background sequence.
   std::unique_ptr<ImageAnnotationWorker> annotation_worker_;
-
   std::unique_ptr<SqlDatabase> sql_database_;
 
-  const scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/chrome/browser/ash/app_list/search/local_images/annotation_storage_unittest.cc b/chrome/browser/ash/app_list/search/local_images/annotation_storage_unittest.cc
index de83a771..9de2add 100644
--- a/chrome/browser/ash/app_list/search/local_images/annotation_storage_unittest.cc
+++ b/chrome/browser/ash/app_list/search/local_images/annotation_storage_unittest.cc
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include <iostream>
+#include <memory>
 
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/ash/app_list/search/local_images/annotation_storage.h"
 #include "chrome/browser/ash/app_list/search/local_images/image_annotation_worker.h"
+#include "chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -24,176 +26,120 @@
 
     test_directory_ = temp_dir.GetPath();
     base::FilePath test_db = test_directory_.AppendASCII("test.db");
-    storage_ = base::MakeRefCounted<AnnotationStorage>(
+    storage_ = std::make_unique<AnnotationStorage>(
         std::move(test_db), /*histogram_tag=*/"test",
         /*current_version_number=*/2, /*annotation_worker=*/nullptr);
   }
 
   base::test::TaskEnvironment task_environment_;
-  scoped_refptr<AnnotationStorage> storage_;
+  std::unique_ptr<AnnotationStorage> storage_;
   base::FilePath test_directory_;
 };
 
-bool Matcher(std::vector<ImageInfo> arg,
-             std::vector<ImageInfo> expected_images) {
-  for (const auto& expect_image : expected_images) {
-    for (const auto& test_image : arg) {
-      if (test_image.path == expect_image.path &&
-          test_image.annotations == expect_image.annotations &&
-          test_image.last_modified == expect_image.last_modified) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-MATCHER_P(OneOfImages, image1, "") {
-  return Matcher(arg, {image1});
-}
-
-MATCHER_P2(OneOfImages, image1, image2, "") {
-  return Matcher(arg, {image1, image2});
-}
-
-MATCHER_P2(OneOfFileSearchResult, result1, result2, "") {
-  for (const auto& expect_result : {result1, result2}) {
-    for (const auto& test_result : arg) {
-      if (test_result.path == expect_result.path &&
-          test_result.last_modified == expect_result.last_modified &&
-          std::abs(test_result.relevance - expect_result.relevance) <
-              0.0000001) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
 TEST_F(AnnotationStorageTest, EmptyStorage) {
-  storage_->InitializeAsync();
+  storage_->Initialize();
   task_environment_.RunUntilIdle();
 
-  auto expect_empty = base::BindLambdaForTesting(
-      [](std::vector<ImageInfo> images) { ASSERT_EQ(images.size(), 0u); });
-
-  storage_->GetAllAnnotationsAsync(expect_empty);
+  auto annotations = storage_->GetAllAnnotations();
+  EXPECT_TRUE(annotations.empty());
 
   task_environment_.RunUntilIdle();
 }
 
-TEST_F(AnnotationStorageTest, InsertOrReplaceAsync) {
-  storage_->InitializeAsync();
+TEST_F(AnnotationStorageTest, InsertOrReplace) {
+  storage_->Initialize();
   task_environment_.RunUntilIdle();
 
   ImageInfo bar_image({"test"}, test_directory_.AppendASCII("bar.jpg"),
                       base::Time::Now());
-  auto expect_one =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images, OneOfImages(bar_image));
-      });
 
-  storage_->InsertOrReplaceAsync(bar_image);
+  storage_->Insert(bar_image);
 
-  storage_->GetAllAnnotationsAsync(expect_one);
+  EXPECT_THAT(storage_->GetAllAnnotations(),
+              testing::ElementsAreArray({bar_image}));
   task_environment_.RunUntilIdle();
 
   ImageInfo foo_image({"test1"}, test_directory_.AppendASCII("foo.png"),
                       base::Time::Now());
-  auto expect_two =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images, OneOfImages(bar_image, foo_image));
-      });
 
-  storage_->InsertOrReplaceAsync(foo_image);
+  storage_->Insert(foo_image);
 
-  storage_->GetAllAnnotationsAsync(expect_two);
+  EXPECT_THAT(storage_->GetAllAnnotations(),
+              testing::UnorderedElementsAreArray({bar_image, foo_image}));
   task_environment_.RunUntilIdle();
 }
 
-TEST_F(AnnotationStorageTest, RemoveAsync) {
-  storage_->InitializeAsync();
+TEST_F(AnnotationStorageTest, Remove) {
+  storage_->Initialize();
   task_environment_.RunUntilIdle();
 
   ImageInfo bar_image({"test"}, test_directory_.AppendASCII("bar.jpg"),
                       base::Time::Now());
   ImageInfo foo_image({"test1"}, test_directory_.AppendASCII("foo.png"),
                       base::Time::Now());
-  storage_->InsertOrReplaceAsync(bar_image);
-  storage_->InsertOrReplaceAsync(foo_image);
+  storage_->Insert(bar_image);
+  storage_->Insert(foo_image);
 
-  storage_->RemoveAsync(test_directory_.AppendASCII("bar.jpg"));
+  storage_->Remove(test_directory_.AppendASCII("bar.jpg"));
 
-  auto expect_callback =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images, OneOfImages(foo_image));
-      });
-  storage_->GetAllAnnotationsAsync(expect_callback);
+  EXPECT_THAT(storage_->GetAllAnnotations(),
+              testing::ElementsAreArray({foo_image}));
 
-  storage_->RemoveAsync(test_directory_.AppendASCII("bar.jpg"));
-  storage_->GetAllAnnotationsAsync(expect_callback);
+  storage_->Remove(test_directory_.AppendASCII("bar.jpg"));
 
-  auto expect_empty = base::BindLambdaForTesting(
-      [](std::vector<ImageInfo> images) { ASSERT_EQ(images.size(), 0u); });
-  storage_->RemoveAsync(test_directory_.AppendASCII("foo.png"));
-  storage_->GetAllAnnotationsAsync(expect_empty);
+  EXPECT_THAT(storage_->GetAllAnnotations(),
+              testing::ElementsAreArray({foo_image}));
 
-  storage_->RemoveAsync(test_directory_.AppendASCII("foo.png"));
-  storage_->GetAllAnnotationsAsync(expect_empty);
+  storage_->Remove(test_directory_.AppendASCII("foo.png"));
+  EXPECT_TRUE(storage_->GetAllAnnotations().empty());
+
+  storage_->Remove(test_directory_.AppendASCII("foo.png"));
+  EXPECT_TRUE(storage_->GetAllAnnotations().empty());
 
   task_environment_.RunUntilIdle();
 }
 
-TEST_F(AnnotationStorageTest, FindImagePathAsync) {
-  storage_->InitializeAsync();
+TEST_F(AnnotationStorageTest, FindImagePath) {
+  storage_->Initialize();
   task_environment_.RunUntilIdle();
 
   ImageInfo bar_image({"test"}, test_directory_.AppendASCII("bar.jpg"),
                       base::Time::Now());
   ImageInfo foo_image({"test1"}, test_directory_.AppendASCII("foo.png"),
                       base::Time::Now());
-  storage_->InsertOrReplaceAsync(bar_image);
-  storage_->InsertOrReplaceAsync(foo_image);
+  storage_->Insert(bar_image);
+  storage_->Insert(foo_image);
 
   auto expect_bar =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images, OneOfImages(bar_image));
-      });
-  storage_->FindImagePathAsync(test_directory_.AppendASCII("bar.jpg"),
-                               expect_bar);
+      storage_->FindImagePath(test_directory_.AppendASCII("bar.jpg"));
+  EXPECT_THAT(expect_bar, testing::ElementsAreArray({bar_image}));
 
   auto expect_foo =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images, OneOfImages(foo_image));
-      });
-  storage_->FindImagePathAsync(test_directory_.AppendASCII("foo.png"),
-                               expect_foo);
+      storage_->FindImagePath(test_directory_.AppendASCII("foo.png"));
+  EXPECT_THAT(expect_foo, testing::ElementsAreArray({foo_image}));
 
   task_environment_.RunUntilIdle();
 }
 
-TEST_F(AnnotationStorageTest, LinearSearchAnnotationsAsync) {
-  storage_->InitializeAsync();
+TEST_F(AnnotationStorageTest, LinearSearchAnnotations) {
+  storage_->Initialize();
   task_environment_.RunUntilIdle();
 
   ImageInfo bar_image({"test", "bar"}, test_directory_.AppendASCII("bar.jpg"),
                       base::Time::Now());
   ImageInfo foo_image({"test1"}, test_directory_.AppendASCII("foo.png"),
                       base::Time::Now());
-  storage_->InsertOrReplaceAsync(bar_image);
-  storage_->InsertOrReplaceAsync(foo_image);
+  storage_->Insert(bar_image);
+  storage_->Insert(foo_image);
 
-  auto expect =
-      base::BindLambdaForTesting([=](std::vector<FileSearchResult> images) {
-        EXPECT_THAT(
-            images,
-            OneOfFileSearchResult(
-                FileSearchResult(bar_image.path, bar_image.last_modified, 1),
-                FileSearchResult(foo_image.path, foo_image.last_modified,
-                                 0.909375)));
-      });
-  storage_->LinearSearchAnnotationsAsync(base::UTF8ToUTF16(std::string("test")),
-                                         expect);
+  auto images =
+      storage_->LinearSearchAnnotations(base::UTF8ToUTF16(std::string("test")));
+
+  EXPECT_THAT(images,
+              testing::UnorderedElementsAreArray(
+                  {FileSearchResult(bar_image.path, bar_image.last_modified, 1),
+                   FileSearchResult(foo_image.path, foo_image.last_modified,
+                                    0.909375)}));
 
   task_environment_.RunUntilIdle();
 }
diff --git a/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.cc b/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.cc
index dad63037..c20168b 100644
--- a/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.cc
+++ b/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.cc
@@ -59,9 +59,9 @@
 
 ImageAnnotationWorker::~ImageAnnotationWorker() = default;
 
-void ImageAnnotationWorker::Run(
-    scoped_refptr<AnnotationStorage> annotation_storage) {
-  DCHECK(annotation_storage);
+void ImageAnnotationWorker::Run(AnnotationStorage* annotation_storage) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(annotation_storage);
   annotation_storage_ = annotation_storage;
 
   on_file_change_callback_ = base::BindRepeating(
@@ -107,12 +107,12 @@
           },
           on_file_change_callback_));
 
-  annotation_storage_->GetAllAnnotationsAsync(
-      base::BindOnce(&ImageAnnotationWorker::FindAndRemoveDeletedImages,
-                     weak_ptr_factory_.GetWeakPtr()));
+  FindAndRemoveDeletedImages(annotation_storage_->GetAllAnnotations());
 }
 
 void ImageAnnotationWorker::EnsureAnnotatorIsConnected() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (ml_service_.is_bound() && image_content_annotator_.is_bound() &&
       ml_service_.is_connected() && image_content_annotator_.is_connected()) {
     return;
@@ -140,6 +140,8 @@
 }
 
 void ImageAnnotationWorker::ConnectToImageAnnotator() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   auto config = chromeos::machine_learning::mojom::ImageAnnotatorConfig::New();
   config->locale = "en-US";
 
@@ -160,13 +162,15 @@
 
 void ImageAnnotationWorker::OnFileChange(const base::FilePath& path,
                                          bool error) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (DirectoryExists(path) || !IsImage(path) || error) {
     return;
   }
 
   auto file_info = std::make_unique<base::File::Info>();
   if (!base::GetFileInfo(path, file_info.get())) {
-    annotation_storage_->RemoveAsync(path);
+    annotation_storage_->Remove(path);
     return;
   }
 
@@ -179,20 +183,20 @@
   }
 
   if (file_info->size == 0) {
-    annotation_storage_->RemoveAsync(path);
+    annotation_storage_->Remove(path);
     return;
   }
 
-  annotation_storage_->FindImagePathAsync(
-      path, base::BindOnce(&ImageAnnotationWorker::ProcessImage,
-                           weak_ptr_factory_.GetWeakPtr(), path,
-                           std::move(file_info)));
+  auto stored_annotations = annotation_storage_->FindImagePath(path);
+  ProcessImage(path, std::move(file_info), std::move(stored_annotations));
 }
 
 void ImageAnnotationWorker::ProcessImage(
     base::FilePath image_path,
     std::unique_ptr<base::File::Info> file_info,
     std::vector<ImageInfo> stored_annotations_with_this_path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (!stored_annotations_with_this_path.empty()) {
     DVLOG(1) << "CompareModifiedTime: "
              << stored_annotations_with_this_path.size() << " same? "
@@ -242,6 +246,7 @@
 void ImageAnnotationWorker::RunImageAnnotator(
     ImageInfo image_info,
     base::MappedReadOnlyRegion mapped_region) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(mapped_region.IsValid());
   DCHECK(mapped_region.region.IsValid());
 
@@ -250,8 +255,7 @@
   image_content_annotator_->AnnotateEncodedImage(
       std::move(mapped_region.region),
       base::BindOnce(
-          [](scoped_refptr<AnnotationStorage> annotation_storage,
-             ImageInfo image_info,
+          [](AnnotationStorage* const annotation_storage, ImageInfo image_info,
              chromeos::machine_learning::mojom::ImageAnnotationResultPtr ptr) {
             DVLOG(1) << "Status: " << ptr->status
                      << " Size: " << ptr->annotations.size();
@@ -267,8 +271,8 @@
               }
             }
             if (!image_info.annotations.empty()) {
-              annotation_storage->RemoveAsync(image_info.path);
-              annotation_storage->InsertOrReplaceAsync(image_info);
+              annotation_storage->Remove(image_info.path);
+              annotation_storage->Insert(image_info);
             }
           },
           annotation_storage_, image_info));
@@ -276,36 +280,39 @@
 
 void ImageAnnotationWorker::FindAndRemoveDeletedImages(
     const std::vector<ImageInfo> images) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DVLOG(1) << "FindAndRemoveDeletedImages.";
   task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE, base::BindOnce(&GetDeletedPaths, std::move(images)),
       base::BindOnce(
-          [](scoped_refptr<AnnotationStorage> annotation_storage,
+          [](AnnotationStorage* const annotation_storage,
              std::set<base::FilePath> paths) {
-            std::for_each(paths.begin(), paths.end(), [&](auto path) {
-              annotation_storage->RemoveAsync(path);
-            });
+            std::for_each(paths.begin(), paths.end(),
+                          [&](auto path) { annotation_storage->Remove(path); });
           },
           annotation_storage_));
 }
 
 void ImageAnnotationWorker::UseFakeAnnotatorForTests() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   use_fake_annotator_for_tests_ = true;
 }
 
 void ImageAnnotationWorker::RunFakeImageAnnotator(
     ImageInfo image_info,
     base::MappedReadOnlyRegion mapped_region) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const std::string annotation =
       image_info.path.BaseName().RemoveFinalExtension().value();
   image_info.annotations.insert(annotation);
-  annotation_storage_->RemoveAsync(image_info.path);
-  annotation_storage_->InsertOrReplaceAsync(image_info);
+  annotation_storage_->Remove(image_info.path);
+  annotation_storage_->Insert(image_info);
 }
 
 void ImageAnnotationWorker::TriggerOnFileChangeForTests(
     const base::FilePath& path,
     bool error) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   on_file_change_callback_.Run(path, error);
 }
 
diff --git a/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.h b/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.h
index a2c6389..d5ccab5c 100644
--- a/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.h
+++ b/chrome/browser/ash/app_list/search/local_images/image_annotation_worker.h
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
+#include "chrome/browser/ash/app_list/search/local_images/annotation_storage.h"
 #include "chromeos/services/machine_learning/public/mojom/image_content_annotation.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -24,12 +25,12 @@
 }
 
 namespace app_list {
-class AnnotationStorage;
 struct ImageInfo;
 
 // The worker watches `root_path_` for any image changes, runs ICA on every
 // change, and saves the annotation to the AnnotationStorage.
-// It maintains and runs tasks on its own background task runner.
+// It must be initialized on the same sequence as AnnotationStorage.
+// It runs IO heavy tasks on a background task runner.
 // TODO(b/260646344): Revisit the use of a FilePathWatcher for My Files
 //  if needed. (It may hit the folder limit.)
 class ImageAnnotationWorker {
@@ -39,9 +40,10 @@
   ImageAnnotationWorker(const ImageAnnotationWorker&) = delete;
   ImageAnnotationWorker& operator=(const ImageAnnotationWorker&) = delete;
 
-  // Spawns the worker in a low-priority sequence and attaches it to the
-  // storage. Can be called from any sequence.
-  void Run(scoped_refptr<AnnotationStorage> annotation_storage);
+  // Initializes a file watcher, connects to ICA and performs a file system
+  // scan for new images. It must be called on the same sequence as
+  // AnnotationStorage is bound to.
+  void Run(AnnotationStorage* annotation_storage);
 
   // Disables mojo bindings and file watchers.
   void UseFakeAnnotatorForTests();
@@ -50,7 +52,7 @@
   // cannot be awaited by `RunUntilIdle()` and introduce unwanted flakiness.
   void TriggerOnFileChangeForTests(const base::FilePath& path, bool error);
 
- protected:
+ private:
   // Setups file watchers.
   void StartWatching();
   void OnFileChange(const base::FilePath& path, bool error);
@@ -74,7 +76,6 @@
 
   std::unique_ptr<base::FilePathWatcher> file_watcher_;
   base::FilePath root_path_;
-  scoped_refptr<AnnotationStorage> annotation_storage_;
 
   mojo::Remote<chromeos::machine_learning::mojom::MachineLearningService>
       ml_service_;
@@ -83,9 +84,15 @@
 
   base::FilePathWatcher::Callback on_file_change_callback_;
 
+  // AnnotationStorage owns this ImageAnnotationWorker. All the methods must
+  // be called from the main sequence.
+  AnnotationStorage* annotation_storage_;
+
   bool use_fake_annotator_for_tests_ = false;
 
+  // Owned by this class.
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<ImageAnnotationWorker> weak_ptr_factory_{this};
 };
 }  // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/local_images/image_annotation_worker_unittest.cc b/chrome/browser/ash/app_list/search/local_images/image_annotation_worker_unittest.cc
index 3ac3de2..4e56b68 100644
--- a/chrome/browser/ash/app_list/search/local_images/image_annotation_worker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/local_images/image_annotation_worker_unittest.cc
@@ -4,12 +4,15 @@
 
 #include "chrome/browser/ash/app_list/search/local_images/image_annotation_worker.h"
 
+#include <memory>
+
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/ash/app_list/search/local_images/annotation_storage.h"
 #include "chrome/browser/ash/app_list/search/local_images/local_image_search_provider.h"
+#include "chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.h"
 #include "chromeos/dbus/machine_learning/machine_learning_client.h"
 #include "chromeos/services/machine_learning/public/cpp/fake_service_connection.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -26,47 +29,25 @@
     ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
     test_directory_ = temp_dir.GetPath();
-    base::FilePath test_db = test_directory_.AppendASCII("test.db");
     annotation_worker_ =
         std::make_unique<ImageAnnotationWorker>(test_directory_);
     annotation_worker_->UseFakeAnnotatorForTests();
-    storage_ = base::MakeRefCounted<AnnotationStorage>(
+    bar_image_path_ = test_directory_.AppendASCII("bar.jpg");
+    const base::FilePath test_db = test_directory_.AppendASCII("test.db");
+    storage_ = std::make_unique<AnnotationStorage>(
         std::move(test_db), /*histogram_tag=*/"test",
         /*current_version_number=*/2, /*annotation_worker=*/nullptr);
-    bar_image_path_ = test_directory_.AppendASCII("bar.jpg");
   }
 
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<ImageAnnotationWorker> annotation_worker_;
-  scoped_refptr<AnnotationStorage> storage_;
+  std::unique_ptr<AnnotationStorage> storage_;
   base::FilePath test_directory_;
   base::FilePath bar_image_path_;
 };
 
-bool Matcher(std::vector<ImageInfo> arg,
-             std::vector<ImageInfo> expected_images) {
-  for (const auto& expect_image : expected_images) {
-    for (const auto& test_image : arg) {
-      if (test_image.path == expect_image.path &&
-          test_image.annotations == expect_image.annotations &&
-          test_image.last_modified == expect_image.last_modified) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-MATCHER_P(OneOfImages, image1, "") {
-  return Matcher(arg, {image1});
-}
-
-MATCHER_P4(OneOfImages, image1, image2, image3, image4, "") {
-  return Matcher(arg, {image1, image2, image3, image4});
-}
-
 TEST_F(ImageAnnotationWorkerTest, MustProcessTheFolderAtInitTest) {
-  storage_->InitializeAsync();
+  storage_->Initialize();
   task_environment_.RunUntilIdle();
 
   auto jpg_path = test_directory_.AppendASCII("bar.jpg");
@@ -77,20 +58,13 @@
   auto JPG_path = test_directory_.AppendASCII("bar5.JPG");
 
   auto image_time = base::Time::Now();
-  base::WriteFile(jpg_path, "test");
-  base::TouchFile(jpg_path, image_time, image_time);
-  base::WriteFile(jpeg_path, "test");
-  base::TouchFile(jpeg_path, image_time, image_time);
-  base::WriteFile(png_path, "test");
-  base::TouchFile(png_path, image_time, image_time);
-  base::WriteFile(jng_path, "test");
-  base::TouchFile(jng_path, image_time, image_time);
-  base::WriteFile(tjng_path, "test");
-  base::TouchFile(tjng_path, image_time, image_time);
-  base::WriteFile(JPG_path, "test");
-  base::TouchFile(JPG_path, image_time, image_time);
+  for (const auto& path :
+       {jpg_path, jpeg_path, png_path, jng_path, tjng_path, JPG_path}) {
+    base::WriteFile(path, "test");
+    base::TouchFile(path, image_time, image_time);
+  }
 
-  annotation_worker_->Run(storage_);
+  annotation_worker_->Run(storage_.get());
   task_environment_.RunUntilIdle();
 
   ImageInfo jpg_image({"bar"}, jpg_path, image_time);
@@ -98,19 +72,16 @@
   ImageInfo png_image({"bar2"}, png_path, image_time);
   ImageInfo JPG_image({"bar5"}, JPG_path, image_time);
 
-  auto expect_all =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images,
-                    OneOfImages(jpg_image, jpeg_image, png_image, JPG_image));
-      });
-  storage_->GetAllAnnotationsAsync(expect_all);
+  auto annotations = storage_->GetAllAnnotations();
+  EXPECT_THAT(annotations, testing::UnorderedElementsAreArray(
+                               {jpg_image, jpeg_image, png_image, JPG_image}));
 
   task_environment_.RunUntilIdle();
 }
 
 TEST_F(ImageAnnotationWorkerTest, MustProcessOnNewFileTest) {
-  storage_->InitializeAsync();
-  annotation_worker_->Run(storage_);
+  storage_->Initialize();
+  annotation_worker_->Run(storage_.get());
   task_environment_.RunUntilIdle();
 
   base::WriteFile(bar_image_path_, "test");
@@ -122,18 +93,16 @@
   task_environment_.RunUntilIdle();
 
   ImageInfo bar_image({"bar"}, bar_image_path_, bar_image_time);
-  auto expect_one =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images, OneOfImages(bar_image));
-      });
-  storage_->GetAllAnnotationsAsync(expect_one);
+
+  EXPECT_THAT(storage_->GetAllAnnotations(),
+              testing::ElementsAreArray({bar_image}));
 
   task_environment_.RunUntilIdle();
 }
 
 TEST_F(ImageAnnotationWorkerTest, MustUpdateOnFileUpdateTest) {
-  storage_->InitializeAsync();
-  annotation_worker_->Run(storage_);
+  storage_->Initialize();
+  annotation_worker_->Run(storage_.get());
   task_environment_.RunUntilIdle();
 
   base::WriteFile(bar_image_path_, "test");
@@ -152,18 +121,15 @@
   task_environment_.RunUntilIdle();
 
   ImageInfo bar_image_updated({"bar"}, bar_image_path_, bar_image_time_updated);
-  auto expect_updated =
-      base::BindLambdaForTesting([=](std::vector<ImageInfo> images) {
-        EXPECT_THAT(images, OneOfImages(bar_image_updated));
-      });
-  storage_->GetAllAnnotationsAsync(expect_updated);
+  EXPECT_THAT(storage_->GetAllAnnotations(),
+              testing::ElementsAreArray({bar_image_updated}));
 
   task_environment_.RunUntilIdle();
 }
 
 TEST_F(ImageAnnotationWorkerTest, MustRemoveOnFileDeleteTest) {
-  storage_->InitializeAsync();
-  annotation_worker_->Run(storage_);
+  storage_->Initialize();
+  annotation_worker_->Run(storage_.get());
   task_environment_.RunUntilIdle();
 
   base::WriteFile(bar_image_path_, "test");
@@ -177,9 +143,7 @@
                                                   /*error=*/false);
   task_environment_.RunUntilIdle();
 
-  auto expect_empty = base::BindLambdaForTesting(
-      [=](std::vector<ImageInfo> images) { EXPECT_TRUE(images.empty()); });
-  storage_->GetAllAnnotationsAsync(expect_empty);
+  EXPECT_TRUE(storage_->GetAllAnnotations().empty());
 
   task_environment_.RunUntilIdle();
 }
diff --git a/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.cc b/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.cc
index a057594..c66400f 100644
--- a/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.cc
+++ b/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.cc
@@ -32,20 +32,21 @@
     : profile_(profile),
       thumbnail_loader_(profile),
       root_path_(file_manager::util::GetMyFilesFolderForProfile(profile)),
-      annotation_storage_(base::MakeRefCounted<AnnotationStorage>(
+      annotation_storage_(
+          base::ThreadPool::CreateSequencedTaskRunner(
+              {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
+               base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
           ConstructPathToAnnotationDb(profile_),
           /* histogram_tag = */ kHistogramTag,
           /* current_version_number= */ 2,
-          std::make_unique<ImageAnnotationWorker>(root_path_))) {
+          std::make_unique<ImageAnnotationWorker>(root_path_)) {
   DCHECK(profile_);
   DCHECK(!root_path_.empty());
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  annotation_storage_->InitializeAsync();
+  annotation_storage_.AsyncCall(&AnnotationStorage::Initialize);
 }
 
-LocalImageSearchProvider::~LocalImageSearchProvider() {
-  annotation_storage_.reset();
-}
+LocalImageSearchProvider::~LocalImageSearchProvider() = default;
 
 ash::AppListSearchResultType LocalImageSearchProvider::ResultType() const {
   return ash::AppListSearchResultType::kImageSearch;
@@ -56,9 +57,10 @@
   query_start_time_ = base::TimeTicks::Now();
   last_query_ = query;
 
-  annotation_storage_->LinearSearchAnnotationsAsync(
-      query, base::BindOnce(&LocalImageSearchProvider::OnSearchComplete,
-                            weak_factory_.GetWeakPtr()));
+  annotation_storage_.AsyncCall(&AnnotationStorage::LinearSearchAnnotations)
+      .WithArgs(query)
+      .Then(base::BindOnce(&LocalImageSearchProvider::OnSearchComplete,
+                           weak_factory_.GetWeakPtr()));
 }
 
 void LocalImageSearchProvider::StopQuery() {
diff --git a/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.h b/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.h
index a879b7c2..46db7bd7 100644
--- a/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.h
+++ b/chrome/browser/ash/app_list/search/local_images/local_image_search_provider.h
@@ -8,6 +8,9 @@
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/sequence_bound.h"
 #include "chrome/browser/ash/app_list/search/search_provider.h"
 #include "chrome/browser/ui/ash/thumbnail_loader.h"
 
@@ -44,7 +47,7 @@
   ash::ThumbnailLoader thumbnail_loader_;
   base::FilePath root_path_;
 
-  scoped_refptr<AnnotationStorage> annotation_storage_;
+  base::SequenceBound<AnnotationStorage> annotation_storage_;
 
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<LocalImageSearchProvider> weak_factory_{this};
diff --git a/chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.cc b/chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.cc
new file mode 100644
index 0000000..0e0aec1
--- /dev/null
+++ b/chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.cc
@@ -0,0 +1,21 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.h"
+
+#include "chrome/browser/ash/app_list/search/local_images/annotation_storage.h"
+
+namespace app_list {
+
+bool operator==(const ImageInfo& i1, const ImageInfo& i2) {
+  return i1.path == i2.path && i1.annotations == i2.annotations &&
+         i1.last_modified == i2.last_modified;
+}
+
+bool operator==(const FileSearchResult& f1, const FileSearchResult& f2) {
+  return f1.path == f2.path && f1.last_modified == f2.last_modified &&
+         std::abs(f1.relevance - f2.relevance) < 0.0000001;
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.h b/chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.h
new file mode 100644
index 0000000..4c8ddea
--- /dev/null
+++ b/chrome/browser/ash/app_list/search/local_images/local_image_search_test_util.h
@@ -0,0 +1,19 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_APP_LIST_SEARCH_LOCAL_IMAGES_LOCAL_IMAGE_SEARCH_TEST_UTIL_H_
+#define CHROME_BROWSER_ASH_APP_LIST_SEARCH_LOCAL_IMAGES_LOCAL_IMAGE_SEARCH_TEST_UTIL_H_
+
+namespace app_list {
+
+struct ImageInfo;
+struct FileSearchResult;
+
+bool operator==(const ImageInfo& i1, const ImageInfo& i2);
+
+bool operator==(const FileSearchResult& f1, const FileSearchResult& f2);
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_ASH_APP_LIST_SEARCH_LOCAL_IMAGES_LOCAL_IMAGE_SEARCH_TEST_UTIL_H_
diff --git a/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc b/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc
index 7f2d7549..017c1b61 100644
--- a/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/answer_ranker.cc
@@ -34,8 +34,9 @@
   ChromeSearchResult* top_answer = nullptr;
   double top_score = 0.0;
   for (const auto& result : results) {
-    if (result->display_type() != DisplayType::kAnswerCard)
+    if (result->display_type() != DisplayType::kAnswerCard) {
       continue;
+    }
 
     const double score = result->relevance();
     if (!top_answer || score > top_score) {
@@ -71,8 +72,9 @@
 ChromeSearchResult* GetShortcutCandidate(Results& results) {
   ChromeSearchResult* best_shortcut = nullptr;
   for (auto& result : results) {
-    if (!result->best_match())
+    if (!result->best_match()) {
       continue;
+    }
 
     if (best_shortcut) {
       // A best match shortcut has already been found, so there are at least
@@ -95,24 +97,25 @@
                          CategoriesList& categories) {
   burn_in_elapsed_ = false;
   chosen_answer_ = nullptr;
+  omnibox_candidates_.clear();
 }
 
 void AnswerRanker::UpdateResultRanks(ResultsMap& results,
                                      ProviderType provider) {
-  if (GetPriority(provider) == 0)
+  if (GetPriority(provider) == 0) {
     return;
+  }
 
   const auto it = results.find(provider);
   DCHECK(it != results.end());
   auto& new_results = it->second;
 
-  // Omnibox answers should not be displayed unless they are selected, so filter
-  // them out by default. If an Omnibox answer is selected later, it will be
-  // un-filtered then.
+  // Keep track of Omnibox candidates. Any candidates that are not selected
+  // should be filtered out later.
   if (provider == ProviderType::kOmnibox) {
-    for (auto& result : new_results) {
+    for (const auto& result : new_results) {
       if (result->display_type() == DisplayType::kAnswerCard) {
-        result->scoring().set_filtered(true);
+        omnibox_candidates_.push_back(result.get());
       }
     }
   }
@@ -120,9 +123,7 @@
   // Don't change a selected answer after the burn-in period has elapsed. This
   // includes ensuring that the answer is re-selected.
   if (burn_in_elapsed_ && chosen_answer_) {
-    if (chosen_answer_->result_type() == provider) {
-      PromoteChosenAnswer();
-    }
+    PromoteChosenAnswer();
     return;
   }
 
@@ -148,11 +149,13 @@
     default:
       return;
   }
-  if (new_answer)
+  if (new_answer) {
     chosen_answer_ = new_answer->GetWeakPtr();
+  }
 
-  if (burn_in_elapsed_)
+  if (burn_in_elapsed_) {
     PromoteChosenAnswer();
+  }
 }
 
 void AnswerRanker::OnBurnInPeriodElapsed() {
@@ -161,13 +164,20 @@
 }
 
 void AnswerRanker::PromoteChosenAnswer() {
-  if (!chosen_answer_)
+  if (!chosen_answer_) {
     return;
+  }
+
+  // Filter out unsuccessful Omnibox candidates.
+  for (auto* result : omnibox_candidates_) {
+    if (result && result->id() != chosen_answer_->id()) {
+      result->scoring().set_filtered(true);
+    }
+  }
 
   chosen_answer_->SetDisplayType(DisplayType::kAnswerCard);
   chosen_answer_->SetMultilineTitle(true);
   chosen_answer_->SetIconDimension(kAnswerCardIconDimension);
-  chosen_answer_->scoring().set_filtered(false);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ash/app_list/search/ranking/answer_ranker.h b/chrome/browser/ash/app_list/search/ranking/answer_ranker.h
index 16661e0..90cfc0ee 100644
--- a/chrome/browser/ash/app_list/search/ranking/answer_ranker.h
+++ b/chrome/browser/ash/app_list/search/ranking/answer_ranker.h
@@ -36,6 +36,10 @@
   // The currently selected answer. A nullptr value indicates that no answer
   // card has been chosen.
   base::WeakPtr<ChromeSearchResult> chosen_answer_;
+
+  // All current Omnibox answer candidates.
+  std::vector<ChromeSearchResult*> omnibox_candidates_;
+
   bool burn_in_elapsed_ = false;
 };
 
diff --git a/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc b/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc
index 61706e6e..c5b5dcc8 100644
--- a/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc
+++ b/chrome/browser/ash/app_list/search/ranking/answer_ranker_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/app_list/search/ranking/answer_ranker.h"
 
+#include "base/strings/string_number_conversions.h"
 #include "chrome/browser/ash/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ash/app_list/search/common/icon_constants.h"
 #include "chrome/browser/ash/app_list/search/test/test_result.h"
@@ -14,30 +15,6 @@
 namespace app_list::test {
 namespace {
 
-Results MakeOmniboxCandidates(std::vector<double> relevances) {
-  Results results;
-  for (const double relevance : relevances) {
-    // |id| and |normalized_relevance| must be set but are not used.
-    results.push_back(std::make_unique<TestResult>(
-        /*id=*/"", relevance,
-        /*normalized_relevance=*/0.0, ash::SearchResultDisplayType::kAnswerCard,
-        false));
-  }
-  return results;
-}
-
-Results MakeShortcutCandidates(std::vector<bool> best_matches) {
-  Results results;
-  for (const double best_match : best_matches) {
-    // |id| and |normalized_relevance| must be set but are not used.
-    results.push_back(std::make_unique<TestResult>(
-        /*id=*/"",
-        /*relevance=*/1, /*normalized_relevance=*/0.0,
-        ash::SearchResultDisplayType::kList, best_match));
-  }
-  return results;
-}
-
 bool AnswerFieldsAreSet(const std::unique_ptr<ChromeSearchResult>& result) {
   return result->display_type() == ash::SearchResultDisplayType::kAnswerCard &&
          result->multiline_title() &&
@@ -47,9 +24,39 @@
 
 }  // namespace
 
+class AnswerRankerTest : public testing::Test {
+ public:
+  Results MakeOmniboxCandidates(std::vector<double> relevances) {
+    Results results;
+    for (const double relevance : relevances) {
+      // |id| and |normalized_relevance| must be set but are not used.
+      results.push_back(std::make_unique<TestResult>(
+          /*id=*/base::NumberToString(next_id++), relevance,
+          /*normalized_relevance=*/0.0,
+          ash::SearchResultDisplayType::kAnswerCard, false));
+    }
+    return results;
+  }
+
+  Results MakeShortcutCandidates(std::vector<bool> best_matches) {
+    Results results;
+    for (const double best_match : best_matches) {
+      // |id| and |normalized_relevance| must be set but are not used.
+      results.push_back(std::make_unique<TestResult>(
+          /*id=*/base::NumberToString(next_id++),
+          /*relevance=*/1, /*normalized_relevance=*/0.0,
+          ash::SearchResultDisplayType::kList, best_match));
+    }
+    return results;
+  }
+
+ private:
+  int next_id = 0;
+};
+
 // Tests that the best Omnibox answer is selected and all others are filtered
 // out.
-TEST(AnswerRankerTest, SelectAndFilterOmnibox) {
+TEST_F(AnswerRankerTest, SelectAndFilterOmnibox) {
   ResultsMap results_map;
   results_map[ResultType::kOmnibox] = MakeOmniboxCandidates({0.3, 0.5, 0.4});
 
@@ -69,7 +76,7 @@
 }
 
 // Tests that a best match shortcut is selected.
-TEST(AnswerRankerTest, SelectBestShortcut) {
+TEST_F(AnswerRankerTest, SelectBestShortcut) {
   ResultsMap results_map;
   results_map[ResultType::kKeyboardShortcut] =
       MakeShortcutCandidates({false, true});
@@ -90,7 +97,7 @@
 
 // Tests that no shortcut answers are selected if there are multiple best
 // matches.
-TEST(AnswerRankerTest, OnlySelectIfOneBestShortcut) {
+TEST_F(AnswerRankerTest, OnlySelectIfOneBestShortcut) {
   ResultsMap results_map;
   results_map[ResultType::kKeyboardShortcut] =
       MakeShortcutCandidates({true, true});
@@ -110,7 +117,7 @@
 }
 
 // Tests that Omnibox answers take priority over Shortcuts.
-TEST(AnswerRankerTest, OmniboxOverShortcuts) {
+TEST_F(AnswerRankerTest, OmniboxOverShortcuts) {
   ResultsMap results_map;
   results_map[ResultType::kOmnibox] = MakeOmniboxCandidates({0.4});
   results_map[ResultType::kKeyboardShortcut] = MakeShortcutCandidates({true});
@@ -133,7 +140,7 @@
 }
 
 // Tests that a chosen answer is not changed after burn-in.
-TEST(AnswerRankerTest, SelectedAnswerNotChangedAfterBurnIn) {
+TEST_F(AnswerRankerTest, SelectedAnswerNotChangedAfterBurnIn) {
   ResultsMap results_map;
   results_map[ResultType::kKeyboardShortcut] = MakeShortcutCandidates({true});
 
diff --git a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
index f784197..d2c08e8f 100644
--- a/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
+++ b/chrome/browser/ash/app_list/search/ranking/removed_results_ranker.cc
@@ -41,19 +41,20 @@
   // Otherwise, filter any results whose IDs have been recorded as for removal.
   const bool proto_initialized = initialized();
   for (const auto& result : it->second) {
-    if (!proto_initialized) {
-      result->scoring().set_filtered(result->display_type() !=
-                                     DisplayType::kRecentApps);
-    } else {
-      result->scoring().set_filtered(
-          (*proto_)->removed_ids().contains(result->id()));
+    if (!proto_initialized &&
+        result->display_type() != DisplayType::kRecentApps) {
+      result->scoring().set_filtered(true);
+    }
+    if (proto_initialized && (*proto_)->removed_ids().contains(result->id())) {
+      result->scoring().set_filtered(true);
     }
   }
 }
 
 void RemovedResultsRanker::Remove(ChromeSearchResult* result) {
-  if (!initialized())
+  if (!initialized()) {
     return;
+  }
 
   if (IsFileSuggestion(*result)) {
     // If `result` is a file suggestion, remove it through the suggestion
diff --git a/chrome/browser/ash/app_list/search/scoring.h b/chrome/browser/ash/app_list/search/scoring.h
index 9d1cadb..e987fb2 100644
--- a/chrome/browser/ash/app_list/search/scoring.h
+++ b/chrome/browser/ash/app_list/search/scoring.h
@@ -24,6 +24,9 @@
   // Score used to determine if a result should be considered a best match.
   double BestMatchScore() const;
 
+  // There are many components that filter out results. Be careful with
+  // set_filtered(false) as it may override a filter from a different location.
+  // TODO(b/268281615): Consider removing the ability to set_filtered(false).
   void set_filtered(bool filtered);
   bool filtered() const { return filtered_; }
 
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.cc b/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.cc
index e69d1c2..21cacd1 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.cc
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ash/app_list/search/system_info/system_info_answer_result.h"
 
+#include <string>
+
 #include "ash/public/cpp/app_list/app_list_metrics.h"
 #include "ash/webui/diagnostics_ui/url_constants.h"
 #include "base/strings/strcat.h"
@@ -29,19 +31,15 @@
     double relevance_score,
     const std::u16string& title,
     const std::u16string& description,
-    AnswerCardDisplayType card_display_type,
-    SystemInfoCategory system_info_category)
+    SystemInfoCategory system_info_category,
+    ash::SystemInfoAnswerCardData answer_card_info)
     : system_info_category_(system_info_category),
       profile_(profile),
       query_(query),
       url_path_(url_path) {
-  // TODO(b/263994165): Handle bar chart display types.
-  if (card_display_type == AnswerCardDisplayType::kTextCard) {
-    SetDisplayType(DisplayType::kAnswerCard);
-  }
+  SetDisplayType(DisplayType::kAnswerCard);
   set_relevance(relevance_score);
   SetIcon(IconInfo(icon, kAppIconDimension));
-  SetTitle(title);
   SetCategory(Category::kSettings);
   SetResultType(ResultType::kSystemInfo);
   UpdateTitleAndDetails(title, description);
@@ -51,6 +49,7 @@
           ? base::StrCat({kOsSettingsResultPrefix, url_path_})
           : base::StrCat({ash::kChromeUIDiagnosticsAppUrl, url_path_});
   set_id(id);
+  SetSystemInfoAnswerCardData(answer_card_info);
 }
 
 SystemInfoAnswerResult::~SystemInfoAnswerResult() = default;
@@ -65,6 +64,14 @@
   std::vector<TextItem> details_vector;
   details_vector.push_back(CreateStringTextItem(description));
   SetDetailsTextVector(details_vector);
+
+  std::vector<std::u16string> accessibility_vector;
+  for (const std::u16string& text : {title, description}) {
+    if (!text.empty()) {
+      accessibility_vector.emplace_back(text);
+    }
+  }
+  SetAccessibleName(base::JoinString(accessibility_vector, u", "));
 }
 
 void SystemInfoAnswerResult::Open(int event_flags) {
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.h b/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.h
index 260dbb4..ba01a79 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.h
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_answer_result.h
@@ -14,18 +14,7 @@
 
 class SystemInfoAnswerResult : public ChromeSearchResult {
  public:
-  enum class SystemInfoCategory {
-    kUnknown = 0,
-    kSettings = 1,
-    kDiagnostics = 2
-  };
-
-  enum class AnswerCardDisplayType {
-    kUnknown = 0,
-    kBarChart = 1,
-    kTextCard = 2,
-    kMulitElementBarChart = 3
-  };
+  enum class SystemInfoCategory { kSettings, kDiagnostics };
 
   SystemInfoAnswerResult(Profile* profile,
                          const std::u16string& query,
@@ -34,8 +23,8 @@
                          double relevance_score,
                          const std::u16string& title,
                          const std::u16string& description,
-                         AnswerCardDisplayType card_display_type,
-                         SystemInfoCategory system_info_category);
+                         SystemInfoCategory system_info_category,
+                         ash::SystemInfoAnswerCardData answer_card_info);
   SystemInfoAnswerResult(const SystemInfoAnswerResult&) = delete;
   SystemInfoAnswerResult& operator=(const SystemInfoAnswerResult&) = delete;
 
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_answer_result_unittest.cc b/chrome/browser/ash/app_list/search/system_info/system_info_answer_result_unittest.cc
index fbdf20d..17efefe 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_answer_result_unittest.cc
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_answer_result_unittest.cc
@@ -19,6 +19,8 @@
 
 namespace app_list::test {
 namespace {
+using AnswerCardInfo = ash::SystemInfoAnswerCardData;
+const char kChromeUIDiagnosticsAppUrl[] = "chrome://diagnostics";
 
 // Creates a 50x50 yellow test icon.
 gfx::ImageSkia GetTestIcon() {
@@ -41,15 +43,14 @@
   std::unique_ptr<Profile> profile_;
 };
 
-TEST_F(SystemInfoAnswerResultTest, Settings) {
+TEST_F(SystemInfoAnswerResultTest, version) {
+  AnswerCardInfo answer_card_info(
+      ash::SystemInfoAnswerCardDisplayType::kTextCard);
   SystemInfoAnswerResult result(
       profile_.get(), u"query", "path", GetTestIcon(), 0.8,
       u"Version 108.0.5359.37 (Official Build) beta (64-bit)",
       u"Click here to check for updates",
-      SystemInfoAnswerResult::AnswerCardDisplayType::kTextCard,
-      SystemInfoAnswerResult::SystemInfoCategory::kSettings);
-  EXPECT_EQ(result.title(),
-            u"Version 108.0.5359.37 (Official Build) beta (64-bit)");
+      SystemInfoAnswerResult::SystemInfoCategory::kSettings, answer_card_info);
   EXPECT_EQ(result.id(), "os-settings://path");
   EXPECT_EQ(result.display_type(), DisplayType::kAnswerCard);
   EXPECT_EQ(result.category(), Category::kSettings);
@@ -59,6 +60,11 @@
   EXPECT_EQ(result.icon().shape, ash::SearchResultIconShape::kDefault);
   EXPECT_TRUE(gfx::BitmapsAreEqual(*result.icon().icon.bitmap(),
                                    *GetTestIcon().bitmap()));
+  EXPECT_EQ(result.system_info_answer_card_data()->display_type,
+            ash::SystemInfoAnswerCardDisplayType::kTextCard);
+  EXPECT_EQ(result.accessible_name(),
+            u"Version 108.0.5359.37 (Official Build) beta (64-bit), Click here "
+            u"to check for updates");
 
   ASSERT_EQ(result.title_text_vector().size(), 1u);
   const auto& title = result.title_text_vector()[0];
@@ -74,4 +80,38 @@
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
+TEST_F(SystemInfoAnswerResultTest, memory) {
+  AnswerCardInfo answer_card_info(54.8);
+  SystemInfoAnswerResult result(
+      profile_.get(), u"query", "", GetTestIcon(), 0.8, u"",
+      u"5.16 GB of 15.52 GB available",
+      SystemInfoAnswerResult::SystemInfoCategory::kDiagnostics,
+      answer_card_info);
+  EXPECT_EQ(result.id(), kChromeUIDiagnosticsAppUrl);
+  EXPECT_EQ(result.display_type(), DisplayType::kAnswerCard);
+  EXPECT_EQ(result.category(), Category::kSettings);
+  EXPECT_EQ(result.result_type(), ResultType::kSystemInfo);
+  EXPECT_EQ(result.metrics_type(), ash::SYSTEM_INFO);
+  EXPECT_EQ(result.icon().dimension, kAppIconDimension);
+  EXPECT_EQ(result.icon().shape, ash::SearchResultIconShape::kDefault);
+  EXPECT_TRUE(gfx::BitmapsAreEqual(*result.icon().icon.bitmap(),
+                                   *GetTestIcon().bitmap()));
+  EXPECT_EQ(result.system_info_answer_card_data()->display_type,
+            ash::SystemInfoAnswerCardDisplayType::kBarChart);
+  EXPECT_EQ(result.system_info_answer_card_data()->bar_chart_percentage, 54.8);
+  EXPECT_EQ(result.accessible_name(), u"5.16 GB of 15.52 GB available");
+
+  ASSERT_EQ(result.title_text_vector().size(), 1u);
+  const auto& title = result.title_text_vector()[0];
+  ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title.GetText(), u"");
+  EXPECT_TRUE(title.GetTextTags().empty());
+
+  ASSERT_EQ(result.details_text_vector().size(), 1u);
+  const auto& details = result.details_text_vector()[0];
+  ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details.GetText(), u"5.16 GB of 15.52 GB available");
+  EXPECT_TRUE(details.GetTextTags().empty());
+}
+
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc
index 9ecc565b..119fb98 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/app_list/search/system_info/system_info_card_provider.h"
 
+#include <iomanip>
 #include <memory>
 #include <optional>
 #include <string>
@@ -48,15 +49,21 @@
 using ::ash::string_matching::FuzzyTokenizedStringMatch;
 using ::ash::string_matching::TokenizedString;
 using ::chromeos::settings::mojom::kAboutChromeOsSectionPath;
+using AnswerCardInfo = ::ash::SystemInfoAnswerCardData;
 
 constexpr double kRelevanceThreshold = 0.64;
 
+double ConvertKBtoBytes(uint32_t amount) {
+  return static_cast<double>(amount) * 1024;
+}
+
 }  // namespace
 
 SystemInfoCardProvider::SystemInfoCardProvider(Profile* profile)
     : total_disk_space_calculator_(profile),
       free_disk_space_calculator_(profile),
       my_files_size_calculator_(profile),
+      drive_offline_size_calculator_(profile),
       browsing_data_size_calculator_(profile),
       apps_size_calculator_(profile),
       crostini_size_calculator_(profile),
@@ -196,6 +203,34 @@
   }
 
   memory_info_ = GetMemoryInfo(*info_ptr);
+  if (!memory_info_) {
+    LOG(ERROR) << "Memory information not provided by croshealthd";
+    return;
+  }
+
+  std::u16string available_memory_gb =
+      ui::FormatBytes(ConvertKBtoBytes(memory_info_->available_memory_kib));
+  std::u16string total_memory_gb =
+      ui::FormatBytes(ConvertKBtoBytes(memory_info_->total_memory_kib));
+
+  double used_memory_kb =
+      memory_info_->total_memory_kib - memory_info_->available_memory_kib;
+  double memory_usage_percentage =
+      static_cast<double>(used_memory_kb) * 100 /
+      static_cast<double>(memory_info_->total_memory_kib);
+
+  std::u16string description =
+      l10n_util::GetStringFUTF16(IDS_ASH_MEMORY_USAGE_IN_LAUNCHER_DESCRIPTION,
+                                 available_memory_gb, total_memory_gb);
+
+  AnswerCardInfo answer_card_info(memory_usage_percentage);
+  SearchProvider::Results new_results;
+  new_results.emplace_back(std::make_unique<SystemInfoAnswerResult>(
+      profile_, last_query_, /*url_path=*/"", diagnostics_icon_, relevance_,
+      /*title=*/u"", description,
+      SystemInfoAnswerResult::SystemInfoCategory::kDiagnostics,
+      answer_card_info));
+  SwapResults(&new_results);
 }
 
 void SystemInfoCardProvider::UpdateMemoryUsage() {
@@ -258,12 +293,15 @@
           static_cast<double>(
               cpu_usage_->GetScalingAverageCurrentFrequencyKhz() / 10000) /
           100));
+
+  AnswerCardInfo answer_card_info(
+      ash::SystemInfoAnswerCardDisplayType::kTextCard);
   SearchProvider::Results new_results;
   new_results.emplace_back(std::make_unique<SystemInfoAnswerResult>(
       profile_, last_query_, /*url_path=*/"", diagnostics_icon_, relevance_,
       title, description,
-      SystemInfoAnswerResult::AnswerCardDisplayType::kTextCard,
-      SystemInfoAnswerResult::SystemInfoCategory::kDiagnostics));
+      SystemInfoAnswerResult::SystemInfoCategory::kDiagnostics,
+      answer_card_info));
   SwapResults(&new_results);
 }
 
@@ -340,12 +378,14 @@
       processor_variation);
   std::u16string description =
       l10n_util::GetStringUTF16(IDS_SETTINGS_ABOUT_PAGE_CHECK_FOR_UPDATES);
+
+  AnswerCardInfo answer_card_info(
+      ash::SystemInfoAnswerCardDisplayType::kTextCard);
   SearchProvider::Results new_results;
   new_results.emplace_back(std::make_unique<SystemInfoAnswerResult>(
       profile_, last_query_, kAboutChromeOsSectionPath, os_settings_icon_,
       relevance_, version_string, description,
-      SystemInfoAnswerResult::AnswerCardDisplayType::kTextCard,
-      SystemInfoAnswerResult::SystemInfoCategory::kSettings));
+      SystemInfoAnswerResult::SystemInfoCategory::kSettings, answer_card_info));
   SwapResults(&new_results);
 }
 
@@ -353,6 +393,7 @@
   total_disk_space_calculator_.StartCalculation();
   free_disk_space_calculator_.StartCalculation();
   my_files_size_calculator_.StartCalculation();
+  drive_offline_size_calculator_.StartCalculation();
   browsing_data_size_calculator_.StartCalculation();
   apps_size_calculator_.StartCalculation();
   crostini_size_calculator_.StartCalculation();
@@ -363,6 +404,7 @@
   total_disk_space_calculator_.AddObserver(this);
   free_disk_space_calculator_.AddObserver(this);
   my_files_size_calculator_.AddObserver(this);
+  drive_offline_size_calculator_.AddObserver(this);
   browsing_data_size_calculator_.AddObserver(this);
   apps_size_calculator_.AddObserver(this);
   crostini_size_calculator_.AddObserver(this);
@@ -373,6 +415,7 @@
   total_disk_space_calculator_.RemoveObserver(this);
   free_disk_space_calculator_.RemoveObserver(this);
   my_files_size_calculator_.RemoveObserver(this);
+  drive_offline_size_calculator_.RemoveObserver(this);
   browsing_data_size_calculator_.RemoveObserver(this);
   apps_size_calculator_.RemoveObserver(this);
   crostini_size_calculator_.RemoveObserver(this);
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.h b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.h
index 4554fc59..fcb5982 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.h
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.h
@@ -85,6 +85,7 @@
   ::ash::settings::TotalDiskSpaceCalculator total_disk_space_calculator_;
   ::ash::settings::FreeDiskSpaceCalculator free_disk_space_calculator_;
   ::ash::settings::MyFilesSizeCalculator my_files_size_calculator_;
+  ::ash::settings::DriveOfflineSizeCalculator drive_offline_size_calculator_;
   ::ash::settings::BrowsingDataSizeCalculator browsing_data_size_calculator_;
   ::ash::settings::AppsSizeCalculator apps_size_calculator_;
   ::ash::settings::CrostiniSizeCalculator crostini_size_calculator_;
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc
index f50620c..3172d89 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc
@@ -13,6 +13,7 @@
 #include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom-forward.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "components/version_info/version_info.h"
 #include "components/version_info/version_string.h"
 #include "content/public/test/browser_task_environment.h"
@@ -104,6 +105,16 @@
   SetCrosHealthdCpuResponse(usage_data, {50}, cpu_speeds);
 }
 
+void SetCrosHealthdMemoryUsageResponse(uint32_t total_memory_kib,
+                                       uint32_t free_memory_kib,
+                                       uint32_t available_memory_kib) {
+  healthd_mojom::MemoryInfoPtr memory_info = healthd_mojom::MemoryInfo::New(
+      total_memory_kib, free_memory_kib, available_memory_kib,
+      /*page_faults_since_last_boot=*/0);
+  SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr, /*cpu_info=*/nullptr,
+                                /*memory_info=*/std::move(memory_info));
+}
+
 }  // namespace
 
 class SystemInfoCardProviderTest : public testing::Test {
@@ -146,8 +157,8 @@
 
   content::BrowserTaskEnvironment task_environment_;
   ::ash::mojo_service_manager::FakeMojoServiceManager fake_service_manager_;
-  std::unique_ptr<Profile> profile_;
   std::unique_ptr<arc::ArcServiceManager> arc_service_manager_;
+  std::unique_ptr<Profile> profile_;
   std::unique_ptr<TestSearchController> search_controller_;
   std::unique_ptr<SystemInfoCardProvider> provider_;
 };
@@ -174,6 +185,8 @@
   EXPECT_EQ(results()[0]->result_type(),
             ash::AppListSearchResultType::kSystemInfo);
   EXPECT_EQ(results()[0]->metrics_type(), ash::SYSTEM_INFO);
+  EXPECT_EQ(results()[0]->system_info_answer_card_data()->display_type,
+            ash::SystemInfoAnswerCardDisplayType::kTextCard);
 
   ASSERT_EQ(results()[0]->title_text_vector().size(), 1u);
   const auto& title = results()[0]->title_text_vector()[0];
@@ -211,6 +224,8 @@
   EXPECT_EQ(results()[0]->result_type(),
             ash::AppListSearchResultType::kSystemInfo);
   EXPECT_EQ(results()[0]->metrics_type(), ash::SYSTEM_INFO);
+  EXPECT_EQ(results()[0]->system_info_answer_card_data()->display_type,
+            ash::SystemInfoAnswerCardDisplayType::kTextCard);
 
   ASSERT_EQ(results()[0]->title_text_vector().size(), 1u);
   const auto& title = results()[0]->title_text_vector()[0];
@@ -225,4 +240,40 @@
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
+TEST_F(SystemInfoCardProviderTest, memory) {
+  const uint32_t total_memory_kib = 8000000;
+  const uint32_t free_memory_kib = 2000000;
+  const uint32_t available_memory_kib = 4000000;
+
+  SetCrosHealthdMemoryUsageResponse(total_memory_kib, free_memory_kib,
+                                    available_memory_kib);
+
+  StartSearch(u"memory");
+  Wait();
+
+  ASSERT_FALSE(results().empty());
+  EXPECT_EQ(results().size(), 1u);
+  EXPECT_EQ(results()[0]->display_type(),
+            ash::SearchResultDisplayType::kAnswerCard);
+  EXPECT_EQ(results()[0]->result_type(),
+            ash::AppListSearchResultType::kSystemInfo);
+  EXPECT_EQ(results()[0]->metrics_type(), ash::SYSTEM_INFO);
+  EXPECT_EQ(results()[0]->system_info_answer_card_data()->display_type,
+            ash::SystemInfoAnswerCardDisplayType::kBarChart);
+  EXPECT_EQ(results()[0]->system_info_answer_card_data()->bar_chart_percentage,
+            50);
+
+  ASSERT_EQ(results()[0]->title_text_vector().size(), 1u);
+  const auto& title = results()[0]->title_text_vector()[0];
+  ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title.GetText(), u"");
+  EXPECT_TRUE(title.GetTextTags().empty());
+
+  ASSERT_EQ(results()[0]->details_text_vector().size(), 1u);
+  const auto& details = results()[0]->details_text_vector()[0];
+  ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details.GetText(), u"3.8 GB of 7.6 GB available");
+  EXPECT_TRUE(details.GetTextTags().empty());
+}
+
 }  // namespace app_list::test
diff --git a/chrome/browser/ash/app_mode/kiosk_app_data_base.cc b/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
index 15030135..c185611 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
@@ -43,9 +43,7 @@
     return;
   }
 
-  const int wrote = base::WriteFile(
-      icon_path, reinterpret_cast<char*>(image_data.data()), image_data.size());
-  if (wrote != static_cast<int>(image_data.size())) {
+  if (!base::WriteFile(icon_path, image_data)) {
     LOG(ERROR) << "Failed to write kiosk icon file";
   }
 }
diff --git a/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc b/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc
index c657f6ac..e9cff86 100644
--- a/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc
+++ b/chrome/browser/ash/app_mode/kiosk_troubleshooting_tools_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/app_mode/app_session_ash.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
+#include "chrome/browser/ash/login/app_mode/test/kiosk_base_test.h"
 #include "chrome/browser/ash/login/app_mode/test/web_kiosk_base_test.h"
 #include "chrome/browser/chromeos/app_mode/app_session_browser_window_handler.h"
 #include "chrome/browser/devtools/devtools_window_testing.h"
@@ -12,6 +13,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -30,16 +32,20 @@
   KioskTroubleshootingToolsTest& operator=(
       const KioskTroubleshootingToolsTest&) = delete;
 
-  void EnableDevTools() const {
-    initial_browser()->profile()->GetPrefs()->SetInteger(
-        prefs::kDevToolsAvailability, static_cast<int>(kAllowed));
+  void UpdateTroubleshootingToolsPolicy(bool enable) {
+    profile()->GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled,
+                                      enable);
   }
 
-  void ExpectOpenDevTools() const {
+  void EnableDevTools() const {
+    profile()->GetPrefs()->SetInteger(prefs::kDevToolsAvailability,
+                                      static_cast<int>(kAllowed));
+  }
+
+  void ExpectOpenBrowser(chromeos::KioskBrowserWindowType window_type) const {
     EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
-    histogram.ExpectBucketCount(
-        chromeos::kKioskNewBrowserWindowHistogram,
-        chromeos::KioskBrowserWindowType::kOpenedDevToolsBrowser, 1);
+    histogram.ExpectBucketCount(chromeos::kKioskNewBrowserWindowHistogram,
+                                window_type, 1);
     histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 1);
   }
 
@@ -48,10 +54,21 @@
     EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
   }
 
+  Browser* OpenRegularBrowser() const {
+    return Browser::Create(
+        Browser::CreateParams(profile(), /* user_gesture=*/true));
+  }
+
+  Profile* profile() const { return initial_browser()->profile(); }
+
   Browser* initial_browser() const {
     return BrowserList::GetInstance()->get(0);
   }
 
+  AppSessionAsh* app_session() const {
+    return WebKioskAppManager::Get()->app_session();
+  }
+
  protected:
   base::HistogramTester histogram;
 };
@@ -61,19 +78,15 @@
   InitializeRegularOnlineKiosk();
   ExpectOnlyKioskAppOpen();
 
-  initial_browser()->profile()->GetPrefs()->SetBoolean(
-      prefs::kKioskTroubleshootingToolsEnabled, true);
-
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
   EnableDevTools();
   DevToolsWindowTesting::OpenDevToolsWindowSync(initial_browser(),
                                                 /* is_docked= */ false);
-  ExpectOpenDevTools();
+  ExpectOpenBrowser(chromeos::KioskBrowserWindowType::kOpenedDevToolsBrowser);
 
   // Shut down the session when kiosk troubleshooting tools get disabled.
-  initial_browser()->profile()->GetPrefs()->SetBoolean(
-      prefs::kKioskTroubleshootingToolsEnabled, false);
-  auto* app_session = WebKioskAppManager::Get()->app_session();
-  EXPECT_TRUE(app_session->is_shutting_down());
+  UpdateTroubleshootingToolsPolicy(/*enable=*/false);
+  EXPECT_TRUE(app_session()->is_shutting_down());
 }
 
 IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
@@ -81,8 +94,7 @@
   InitializeRegularOnlineKiosk();
   ExpectOnlyKioskAppOpen();
 
-  initial_browser()->profile()->GetPrefs()->SetBoolean(
-      prefs::kKioskTroubleshootingToolsEnabled, true);
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
 
   // Devtools are not enabled, but disabled by default.
   DevToolsWindowTesting::OpenDevToolsWindowSync(initial_browser(),
@@ -111,5 +123,80 @@
   histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
+                       NewWindowBasicShowAndShutdown) {
+  InitializeRegularOnlineKiosk();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
+  ExpectOnlyKioskAppOpen();
+
+  OpenRegularBrowser();
+  EXPECT_FALSE(ShouldBrowserBeClosedByAppSessionBrowserHander(app_session()));
+
+  ExpectOpenBrowser(
+      chromeos::KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser);
+
+  // Shut down the session when kiosk troubleshooting tools get disabled.
+  UpdateTroubleshootingToolsPolicy(/*enable=*/false);
+  EXPECT_TRUE(app_session()->is_shutting_down());
+}
+
+IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
+                       OpenAllTroubleshootingTools) {
+  InitializeRegularOnlineKiosk();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
+  ExpectOnlyKioskAppOpen();
+  EnableDevTools();
+
+  DevToolsWindowTesting::OpenDevToolsWindowSync(initial_browser(),
+                                                /* is_docked= */ false);
+
+  OpenRegularBrowser();
+  EXPECT_FALSE(ShouldBrowserBeClosedByAppSessionBrowserHander(app_session()));
+
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 3u);
+  histogram.ExpectBucketCount(
+      chromeos::kKioskNewBrowserWindowHistogram,
+      chromeos::KioskBrowserWindowType::kOpenedDevToolsBrowser, 1);
+  histogram.ExpectBucketCount(
+      chromeos::kKioskNewBrowserWindowHistogram,
+      chromeos::KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser, 1);
+  histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 2);
+}
+
+IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest,
+                       NewWindowDisalloweddNoShow) {
+  InitializeRegularOnlineKiosk();
+  ExpectOnlyKioskAppOpen();
+
+  OpenRegularBrowser();
+  EXPECT_TRUE(ShouldBrowserBeClosedByAppSessionBrowserHander(app_session()));
+
+  histogram.ExpectBucketCount(
+      chromeos::kKioskNewBrowserWindowHistogram,
+      chromeos::KioskBrowserWindowType::kClosedRegularBrowser, 1);
+  histogram.ExpectTotalCount(chromeos::kKioskNewBrowserWindowHistogram, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(KioskTroubleshootingToolsTest, NewWindowAddTab) {
+  InitializeRegularOnlineKiosk();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
+  ExpectOnlyKioskAppOpen();
+
+  Browser* new_browser = OpenRegularBrowser();
+  new_browser->window()->Show();
+  EXPECT_FALSE(ShouldBrowserBeClosedByAppSessionBrowserHander(app_session()));
+
+  ExpectOpenBrowser(
+      chromeos::KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser);
+  int initial_number_of_tabs = new_browser->tab_strip_model()->count();
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      new_browser, GURL("https://www.google.com/"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  EXPECT_EQ(new_browser->tab_strip_model()->count(),
+            initial_number_of_tabs + 1);
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
index f6c0e3c..0246e0d 100644
--- a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
+++ b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
@@ -53,6 +53,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom-forward.h"
 #include "chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom-shared.h"
 #include "components/account_id/account_id.h"
@@ -73,10 +74,11 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+using ash::standalone_browser::BrowserSupport;
+
 namespace ash {
 
 namespace {
-
 using ::extensions::ExternalInstallInfoFile;
 using ::extensions::ExternalInstallInfoUpdateUrl;
 using ::extensions::Manifest;
@@ -1736,7 +1738,7 @@
   std::unique_ptr<KioskAppLauncher> startup_app_launcher_;
 
   base::AutoReset<bool> set_lacros_enabled_ =
-      crosapi::browser_util::SetLacrosEnabledForTest(true);
+      BrowserSupport::SetLacrosEnabledForTest(true);
   base::AutoReset<absl::optional<bool>> set_lacros_primary_ =
       crosapi::browser_util::SetLacrosPrimaryBrowserForTest(true);
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_launcher_unittest.cc b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_launcher_unittest.cc
index 8c78231..44e8cf7 100644
--- a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_launcher_unittest.cc
+++ b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_launcher_unittest.cc
@@ -25,6 +25,7 @@
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/wm_helper.h"
 #include "components/user_manager/scoped_user_manager.h"
@@ -35,6 +36,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+using ash::standalone_browser::BrowserSupport;
+
 namespace ash {
 
 namespace {
@@ -378,7 +381,7 @@
 
  private:
   base::AutoReset<bool> set_lacros_enabled_ =
-      crosapi::browser_util::SetLacrosEnabledForTest(true);
+      BrowserSupport::SetLacrosEnabledForTest(true);
   base::AutoReset<absl::optional<bool>> set_lacros_primary_ =
       crosapi::browser_util::SetLacrosPrimaryBrowserForTest(true);
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/chrome/browser/ash/arc/input_overlay/db/data_controller.cc b/chrome/browser/ash/arc/input_overlay/db/data_controller.cc
index ac6cce8..f5b4a540 100644
--- a/chrome/browser/ash/arc/input_overlay/db/data_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/db/data_controller.cc
@@ -94,7 +94,7 @@
   if (!proto->SerializeToString(&proto_str)) {
     return false;
   }
-  return base::WriteFile(file_path, proto_str.data(), proto_str.size()) > 0;
+  return base::WriteFile(file_path, proto_str);
 }
 
 }  // namespace arc::input_overlay
diff --git a/chrome/browser/ash/crosapi/browser_data_back_migrator_unittest.cc b/chrome/browser/ash/crosapi/browser_data_back_migrator_unittest.cc
index 7698f935..da272ed 100644
--- a/chrome/browser/ash/crosapi/browser_data_back_migrator_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_back_migrator_unittest.cc
@@ -76,9 +76,8 @@
                             const char* file_content,
                             int file_size) {
   ASSERT_TRUE(base::CreateDirectory(directory_path));
-  ASSERT_EQ(base::WriteFile(directory_path.Append(file_path), file_content,
-                            file_size),
-            file_size);
+  ASSERT_TRUE(base::WriteFile(directory_path.Append(file_path),
+                              base::StringPiece(file_content, file_size)));
 }
 
 void SetUpExtensions(const base::FilePath& ash_profile_dir,
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_util_unittest.cc b/chrome/browser/ash/crosapi/browser_data_migrator_util_unittest.cc
index f8ed7e4..4ac7f323 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_util_unittest.cc
@@ -37,8 +37,7 @@
 constexpr char kCachePath[] = "Cache";
 constexpr char kCodeCachePath[] = "Code Cache";
 constexpr char kCodeCacheUMAName[] = "CodeCache";
-constexpr char kTextFileContent[] = "Hello, World!";
-constexpr int kTextFileSize = sizeof(kTextFileContent);
+constexpr base::StringPiece kTextFileContent = "Hello, World!";
 constexpr char kMoveExtensionId[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
 
 constexpr syncer::ModelType kAshSyncDataType =
@@ -241,26 +240,24 @@
   base::ScopedTempDir dir_1;
   ASSERT_TRUE(dir_1.CreateUniqueTempDir());
 
-  ASSERT_TRUE(
-      base::WriteFile(dir_1.GetPath().Append(FILE_PATH_LITERAL("file1")),
-                      kTextFileContent, kTextFileSize));
+  ASSERT_TRUE(base::WriteFile(
+      dir_1.GetPath().Append(FILE_PATH_LITERAL("file1")), kTextFileContent));
   ASSERT_TRUE(
       base::CreateDirectory(dir_1.GetPath().Append(FILE_PATH_LITERAL("dir"))));
   ASSERT_TRUE(base::WriteFile(dir_1.GetPath()
                                   .Append(FILE_PATH_LITERAL("dir"))
                                   .Append(FILE_PATH_LITERAL("file2")),
-                              kTextFileContent, kTextFileSize));
+                              kTextFileContent));
 
   // Check that `ComputeDirectorySizeWithoutLinks` returns the sum of sizes of
   // the two files in the directory.
   EXPECT_EQ(ComputeDirectorySizeWithoutLinks(dir_1.GetPath()),
-            kTextFileSize * 2);
+            static_cast<int>(kTextFileContent.size() * 2));
 
   base::ScopedTempDir dir_2;
   ASSERT_TRUE(dir_2.CreateUniqueTempDir());
-  ASSERT_TRUE(
-      base::WriteFile(dir_2.GetPath().Append(FILE_PATH_LITERAL("file3")),
-                      kTextFileContent, kTextFileSize));
+  ASSERT_TRUE(base::WriteFile(
+      dir_2.GetPath().Append(FILE_PATH_LITERAL("file3")), kTextFileContent));
 
   ASSERT_TRUE(CreateSymbolicLink(
       dir_2.GetPath().Append(FILE_PATH_LITERAL("file3")),
@@ -269,7 +266,7 @@
   // Check that `ComputeDirectorySizeWithoutLinks` does not follow symlinks from
   // `dir_1` to `dir_2`.
   EXPECT_EQ(ComputeDirectorySizeWithoutLinks(dir_1.GetPath()),
-            kTextFileSize * 2);
+            static_cast<int>(kTextFileContent.size() * 2));
 }
 
 TEST(BrowserDataMigratorUtilTest, GetUMAItemName) {
@@ -506,7 +503,7 @@
       scoped_temp_dir.GetPath().Append(FILE_PATH_LITERAL("from_file"));
   const base::FilePath to_file =
       scoped_temp_dir.GetPath().Append(FILE_PATH_LITERAL("to_file"));
-  base::WriteFile(from_file, "Hello, World", sizeof("Hello, World"));
+  ASSERT_TRUE(base::WriteFile(from_file, "Hello, World"));
 
   ASSERT_TRUE(CreateHardLink(from_file, to_file));
 
@@ -537,24 +534,24 @@
   //         |- data
   //         |- Subdirectory/data
   //     |- symlink  /* symlink to original */
-
+  ASSERT_TRUE(
+      base::CreateDirectory(scoped_temp_dir.GetPath().Append(sensitive)));
   ASSERT_TRUE(base::WriteFile(
       scoped_temp_dir.GetPath().Append(sensitive).Append(original),
-      kTextFileContent, kTextFileSize));
+      kTextFileContent));
   ASSERT_TRUE(base::CreateDirectory(copy_from));
   ASSERT_TRUE(base::CreateDirectory(copy_from.Append(subdirectory)));
   ASSERT_TRUE(base::CreateDirectory(
       copy_from.Append(subdirectory).Append(subdirectory)));
-  ASSERT_TRUE(base::WriteFile(copy_from.Append(data_file), kTextFileContent,
-                              kTextFileSize));
+  ASSERT_TRUE(base::WriteFile(copy_from.Append(data_file), kTextFileContent));
   ASSERT_TRUE(base::WriteFile(copy_from.Append(subdirectory).Append(data_file),
-                              kTextFileContent, kTextFileSize));
+                              kTextFileContent));
   ASSERT_TRUE(base::WriteFile(
       copy_from.Append(subdirectory).Append(subdirectory).Append(data_file),
-      kTextFileContent, kTextFileSize));
-  base::CreateSymbolicLink(
+      kTextFileContent));
+  ASSERT_TRUE(base::CreateSymbolicLink(
       scoped_temp_dir.GetPath().Append(sensitive).Append(original),
-      copy_from.Append(symlink));
+      copy_from.Append(symlink)));
 
   // Test `CopyDirectory()`.
   scoped_refptr<CancelFlag> cancelled = base::MakeRefCounted<CancelFlag>();
@@ -707,31 +704,31 @@
 
     // Lacros items.
     ASSERT_TRUE(base::WriteFile(profile_data_dir_.Append(kBookmarksPath),
-                                kTextFileContent, kTextFileSize));
+                                kTextFileContent));
     ASSERT_TRUE(base::WriteFile(profile_data_dir_.Append(kCookiesPath),
-                                kTextFileContent, kTextFileSize));
+                                kTextFileContent));
     // Remain in ash items.
     ASSERT_TRUE(
         base::CreateDirectory(profile_data_dir_.Append(kDownloadsPath)));
     ASSERT_TRUE(base::WriteFile(profile_data_dir_.Append(kDownloadsPath)
                                     .Append(FILE_PATH_LITERAL("file")),
-                                kTextFileContent, kTextFileSize));
+                                kTextFileContent));
     ASSERT_TRUE(base::WriteFile(profile_data_dir_.Append(kDownloadsPath)
                                     .Append(FILE_PATH_LITERAL("file 2")),
-                                kTextFileContent, kTextFileSize));
+                                kTextFileContent));
 
     // Need to copy items.
     ASSERT_TRUE(base::WriteFile(profile_data_dir_.Append(kSharedProtoDBPath),
-                                kTextFileContent, kTextFileSize));
+                                kTextFileContent));
 
     // Deletable items.
     ASSERT_TRUE(base::WriteFile(profile_data_dir_.Append(kCachePath),
-                                kTextFileContent, kTextFileSize));
+                                kTextFileContent));
     ASSERT_TRUE(
         base::CreateDirectory(profile_data_dir_.Append(kCodeCachePath)));
     ASSERT_TRUE(base::WriteFile(profile_data_dir_.Append(kCodeCachePath)
                                     .Append(FILE_PATH_LITERAL("file")),
-                                kTextFileContent, kTextFileSize));
+                                kTextFileContent));
   }
 
   void TearDown() override { EXPECT_TRUE(scoped_temp_dir_.Delete()); }
@@ -743,13 +740,14 @@
 TEST_F(BrowserDataMigratorUtilWithTargetsTest, GetTargetItems) {
   // Check for lacros data.
   std::vector<TargetItem> expected_lacros_items = {
-      {profile_data_dir_.Append(kBookmarksPath), kTextFileSize,
+      {profile_data_dir_.Append(kBookmarksPath), kTextFileContent.size(),
        TargetItem::ItemType::kFile},
-      {profile_data_dir_.Append(kCookiesPath), kTextFileSize,
+      {profile_data_dir_.Append(kCookiesPath), kTextFileContent.size(),
        TargetItem::ItemType::kFile}};
   TargetItems lacros_items =
       GetTargetItems(profile_data_dir_, ItemType::kLacros);
-  EXPECT_EQ(lacros_items.total_size, kTextFileSize * 2);
+  EXPECT_EQ(lacros_items.total_size,
+            static_cast<int>(kTextFileContent.size() * 2));
   ASSERT_EQ(lacros_items.items.size(), expected_lacros_items.size());
   std::sort(lacros_items.items.begin(), lacros_items.items.end(),
             TargetItemComparator());
@@ -760,36 +758,39 @@
 
   // Check for remain in ash data.
   std::vector<TargetItem> expected_remain_in_ash_items = {
-      {profile_data_dir_.Append(kDownloadsPath), kTextFileSize * 2,
+      {profile_data_dir_.Append(kDownloadsPath), kTextFileContent.size() * 2,
        TargetItem::ItemType::kDirectory}};
   TargetItems remain_in_ash_items =
       GetTargetItems(profile_data_dir_, ItemType::kRemainInAsh);
-  EXPECT_EQ(remain_in_ash_items.total_size, kTextFileSize * 2);
+  EXPECT_EQ(remain_in_ash_items.total_size,
+            static_cast<int>(kTextFileContent.size() * 2));
   ASSERT_EQ(remain_in_ash_items.items.size(),
             expected_remain_in_ash_items.size());
   EXPECT_EQ(remain_in_ash_items.items[0], expected_remain_in_ash_items[0]);
 
   // Check for items that need copies in lacros.
   std::vector<TargetItem> expected_need_copy_items = {
-      {profile_data_dir_.Append(kSharedProtoDBPath), kTextFileSize,
+      {profile_data_dir_.Append(kSharedProtoDBPath), kTextFileContent.size(),
        TargetItem::ItemType::kFile}};
   TargetItems need_copy_items =
       GetTargetItems(profile_data_dir_, ItemType::kNeedCopyForMove);
-  EXPECT_EQ(need_copy_items.total_size, kTextFileSize);
+  EXPECT_EQ(need_copy_items.total_size,
+            static_cast<int>(kTextFileContent.size()));
   ASSERT_EQ(need_copy_items.items.size(), expected_need_copy_items.size());
   EXPECT_EQ(need_copy_items.items[0], expected_need_copy_items[0]);
 
   // Check for deletable items.
   std::vector<TargetItem> expected_deletable_items = {
-      {profile_data_dir_.Append(kCachePath), kTextFileSize,
+      {profile_data_dir_.Append(kCachePath), kTextFileContent.size(),
        TargetItem::ItemType::kFile},
-      {profile_data_dir_.Append(kCodeCachePath), kTextFileSize,
+      {profile_data_dir_.Append(kCodeCachePath), kTextFileContent.size(),
        TargetItem::ItemType::kDirectory}};
   TargetItems deletable_items =
       GetTargetItems(profile_data_dir_, ItemType::kDeletable);
   std::sort(deletable_items.items.begin(), deletable_items.items.end(),
             TargetItemComparator());
-  EXPECT_EQ(deletable_items.total_size, kTextFileSize * 2);
+  EXPECT_EQ(deletable_items.total_size,
+            static_cast<int>(kTextFileContent.size() * 2));
   ASSERT_EQ(deletable_items.items.size(), expected_deletable_items.size());
   for (size_t i = 0; i < deletable_items.items.size(); i++) {
     SCOPED_TRACE(deletable_items.items[i].path.value());
@@ -822,26 +823,26 @@
       "CodeCache";
 
   histogram_tester.ExpectBucketCount(uma_name_bookmarks,
-                                     kTextFileSize / 1024 / 1024, 1);
+                                     kTextFileContent.size() / 1024 / 1024, 1);
   histogram_tester.ExpectBucketCount(uma_name_cookies,
-                                     kTextFileSize / 1024 / 1024, 1);
-  histogram_tester.ExpectBucketCount(uma_name_downloads,
-                                     kTextFileSize * 2 / 1024 / 1024, 1);
+                                     kTextFileContent.size() / 1024 / 1024, 1);
+  histogram_tester.ExpectBucketCount(
+      uma_name_downloads, kTextFileContent.size() * 2 / 1024 / 1024, 1);
   histogram_tester.ExpectBucketCount(uma_name_shared_proto_db,
-                                     kTextFileSize / 1024 / 1024, 1);
+                                     kTextFileContent.size() / 1024 / 1024, 1);
   histogram_tester.ExpectBucketCount(uma_name_cache,
-                                     kTextFileSize / 1024 / 1024, 1);
+                                     kTextFileContent.size() / 1024 / 1024, 1);
   histogram_tester.ExpectBucketCount(uma_name_code_cache,
-                                     kTextFileSize / 1024 / 1024, 1);
+                                     kTextFileContent.size() / 1024 / 1024, 1);
 
-  histogram_tester.ExpectBucketCount(kDryRunLacrosDataSize,
-                                     kTextFileSize * 2 / 1024 / 1024, 1);
-  histogram_tester.ExpectBucketCount(kDryRunAshDataSize,
-                                     kTextFileSize * 2 / 1024 / 1024, 1);
+  histogram_tester.ExpectBucketCount(
+      kDryRunLacrosDataSize, kTextFileContent.size() * 2 / 1024 / 1024, 1);
+  histogram_tester.ExpectBucketCount(
+      kDryRunAshDataSize, kTextFileContent.size() * 2 / 1024 / 1024, 1);
   histogram_tester.ExpectBucketCount(kDryRunCommonDataSize,
-                                     kTextFileSize / 1024 / 1024, 1);
-  histogram_tester.ExpectBucketCount(kDryRunNoCopyDataSize,
-                                     kTextFileSize * 2 / 1024 / 1024, 1);
+                                     kTextFileContent.size() / 1024 / 1024, 1);
+  histogram_tester.ExpectBucketCount(
+      kDryRunNoCopyDataSize, kTextFileContent.size() * 2 / 1024 / 1024, 1);
 
   histogram_tester.ExpectTotalCount(kDryRunCopyMigrationHasEnoughDiskSpace, 1);
   histogram_tester.ExpectTotalCount(
diff --git a/chrome/browser/ash/crosapi/browser_loader_unittest.cc b/chrome/browser/ash/crosapi/browser_loader_unittest.cc
index 8134354..11ebe3cb 100644
--- a/chrome/browser/ash/crosapi/browser_loader_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_loader_unittest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/component_updater/fake_cros_component_manager.h"
 #include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
 #include "chromeos/ash/components/dbus/upstart/fake_upstart_client.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "components/component_updater/mock_component_updater_service.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/policy_constants.h"
@@ -25,6 +26,7 @@
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ash::standalone_browser::BrowserSupport;
 using testing::Return;
 
 namespace crosapi {
@@ -102,7 +104,7 @@
 
  private:
   base::AutoReset<bool> set_lacros_enabled_ =
-      browser_util::SetLacrosEnabledForTest(true);
+      BrowserSupport::SetLacrosEnabledForTest(true);
 };
 
 TEST_F(BrowserLoaderTest, OnLoadSelectionQuicklyChooseRootfs) {
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index 073e5af5..0e9ff02 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -24,6 +24,7 @@
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "chromeos/ash/components/standalone_browser/lacros_availability.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
@@ -40,6 +41,7 @@
 #include "components/version_info/version_info.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
+using ash::standalone_browser::BrowserSupport;
 using ash::standalone_browser::IsGoogleInternal;
 using ash::standalone_browser::LacrosAvailability;
 using user_manager::User;
@@ -50,8 +52,6 @@
 namespace browser_util {
 namespace {
 
-bool g_lacros_enabled_for_test = false;
-
 bool g_profile_migration_completed_for_test = false;
 
 absl::optional<bool> g_lacros_primary_browser_for_test;
@@ -143,8 +143,9 @@
 bool IsLacrosAllowedToBeEnabledWithUser(
     const User* user,
     LacrosAvailability launch_availability) {
-  if (g_lacros_enabled_for_test)
+  if (BrowserSupport::GetLacrosEnabledForTest()) {
     return true;
+  }
 
   if (!IsUserTypeAllowed(user)) {
     return false;
@@ -323,8 +324,9 @@
 bool IsLacrosAllowedToBeEnabled() {
   // Allows tests to avoid enabling the flag, constructing a fake user manager,
   // creating g_browser_process->local_state(), etc.
-  if (g_lacros_enabled_for_test)
+  if (BrowserSupport::GetLacrosEnabledForTest()) {
     return true;
+  }
 
   // TODO(crbug.com/1185813): TaskManagerImplTest is not ready to run with
   // Lacros enabled.
@@ -347,8 +349,9 @@
 bool IsLacrosEnabled() {
   // Allows tests to avoid enabling the flag, constructing a fake user manager,
   // creating g_browser_process->local_state(), etc.
-  if (g_lacros_enabled_for_test)
+  if (BrowserSupport::GetLacrosEnabledForTest()) {
     return true;
+  }
 
   if (!IsLacrosAllowedToBeEnabled())
     return false;
@@ -384,8 +387,9 @@
 
 bool IsLacrosEnabledForMigration(const User* user,
                                  PolicyInitState policy_init_state) {
-  if (g_lacros_enabled_for_test)
+  if (BrowserSupport::GetLacrosEnabledForTest()) {
     return true;
+  }
 
   LacrosAvailability lacros_availability;
   if (policy_init_state == PolicyInitState::kBeforeInit) {
@@ -1189,10 +1193,6 @@
              ash::features::kEnforceAshExtensionKeeplist);
 }
 
-base::AutoReset<bool> SetLacrosEnabledForTest(bool force_enabled) {
-  return base::AutoReset<bool>(&g_lacros_enabled_for_test, force_enabled);
-}
-
 base::AutoReset<absl::optional<bool>>
 SetLacrosPrimaryBrowserForTest(  // IN-TEST
     absl::optional<bool> value) {
diff --git a/chrome/browser/ash/crosapi/browser_util.h b/chrome/browser/ash/crosapi/browser_util.h
index e00f3f9..0dd4f9c 100644
--- a/chrome/browser/ash/crosapi/browser_util.h
+++ b/chrome/browser/ash/crosapi/browser_util.h
@@ -502,10 +502,6 @@
 // Returns true if ash 1st party extension keep list should be enforced.
 bool ShouldEnforceAshExtensionKeepList();
 
-// Forces IsLacrosEnabled() to return true or false for testing. Reset upon
-// destruction of returned |base::AutoReset| object.
-base::AutoReset<bool> SetLacrosEnabledForTest(bool force_enabled);
-
 // Forces IsLacrosPrimaryBrowser() to return true or false for testing.
 // Reset upon destruction of returned |base::AutoReset| object.
 base::AutoReset<absl::optional<bool>> SetLacrosPrimaryBrowserForTest(
diff --git a/chrome/browser/ash/crosapi/browser_util_unittest.cc b/chrome/browser/ash/crosapi/browser_util_unittest.cc
index 0b0f7328..9410aaf 100644
--- a/chrome/browser/ash/crosapi/browser_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_util_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "chromeos/ash/components/standalone_browser/lacros_availability.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
@@ -33,6 +34,7 @@
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ash::standalone_browser::BrowserSupport;
 using ash::standalone_browser::LacrosAvailability;
 using crosapi::browser_util::LacrosLaunchSwitchSource;
 using crosapi::browser_util::LacrosSelection;
@@ -884,7 +886,7 @@
   }
 
   {
-    auto scoped_enabled = browser_util::SetLacrosEnabledForTest(true);
+    auto scoped_enabled = BrowserSupport::SetLacrosEnabledForTest(true);
     EXPECT_TRUE(browser_util::IsLacrosEnabled());
     EXPECT_TRUE(browser_util::IsAshWebBrowserEnabled());
     EXPECT_TRUE(browser_util::IsAshBrowserSyncEnabled());
@@ -907,7 +909,7 @@
         {ash::features::kLacrosOnly, ash::features::kLacrosPrimary,
          ash::features::kLacrosSupport},
         {});
-    auto scoped_enabled = browser_util::SetLacrosEnabledForTest(true);
+    auto scoped_enabled = BrowserSupport::SetLacrosEnabledForTest(true);
     EXPECT_TRUE(browser_util::IsLacrosEnabled());
     EXPECT_FALSE(browser_util::IsAshWebBrowserEnabled());
     EXPECT_FALSE(browser_util::IsAshBrowserSyncEnabled());
diff --git a/chrome/browser/ash/crosapi/copy_migrator_unittest.cc b/chrome/browser/ash/crosapi/copy_migrator_unittest.cc
index b7f9cc1..b64b2e4 100644
--- a/chrome/browser/ash/crosapi/copy_migrator_unittest.cc
+++ b/chrome/browser/ash/crosapi/copy_migrator_unittest.cc
@@ -47,9 +47,9 @@
     ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir());
     from_dir_ = user_data_dir_.GetPath().Append("user");
 
-    ASSERT_TRUE(base::WriteFile(user_data_dir_.GetPath().Append(
-                                    kFirstRun) /* .../'First Run' */,
-                                "", 0) == 0);
+    ASSERT_TRUE(base::WriteFile(
+        user_data_dir_.GetPath().Append(kFirstRun) /* .../'First Run' */,
+        base::StringPiece()));
     ASSERT_TRUE(base::CreateDirectory(
         from_dir_.Append(kDownloads) /* .../user/Downloads/ */));
     ASSERT_TRUE(base::CreateDirectory(from_dir_.Append(
@@ -59,30 +59,28 @@
             .Append(
                 kDownloads) /* .../user/Affiliation Database/Downloads/ */));
     ASSERT_TRUE(base::WriteFile(from_dir_.Append(kCache) /* .../user/Cache/ */,
-                                kDataContent, kFileSize));
+                                kDataContent));
     ASSERT_TRUE(base::WriteFile(
         from_dir_.Append(kFullRestoreData) /* .../user/FullRestoreData/ */,
-        kDataContent, kFileSize));
+        kDataContent));
     ASSERT_TRUE(
         base::WriteFile(from_dir_.Append(kDownloads)
                             .Append(kDataFile) /* .../user/Downloads/data */,
-                        kDataContent, kFileSize));
-    ASSERT_TRUE(
-        base::WriteFile(from_dir_.Append(kCookies) /* .../user/Cookies */,
-                        kDataContent, kFileSize));
-    ASSERT_TRUE(
-        base::WriteFile(from_dir_.Append(kBookmarks) /* .../user/Bookmarks */,
-                        kDataContent, kFileSize));
+                        kDataContent));
+    ASSERT_TRUE(base::WriteFile(
+        from_dir_.Append(kCookies) /* .../user/Cookies */, kDataContent));
+    ASSERT_TRUE(base::WriteFile(
+        from_dir_.Append(kBookmarks) /* .../user/Bookmarks */, kDataContent));
     ASSERT_TRUE(base::WriteFile(
         from_dir_.Append(kAffiliationDatabase)
             .Append(kDataFile) /* .../user/Affiliation Database/data */,
-        kDataContent, kFileSize));
+        kDataContent));
     ASSERT_TRUE(base::WriteFile(
         from_dir_.Append(kAffiliationDatabase)
             .Append(kDownloads)
             .Append(
                 kDataFile) /* .../user/Affiliation Database/Downloads/data */,
-        kDataContent, kFileSize));
+        kDataContent));
   }
 
   void TearDown() override { EXPECT_TRUE(user_data_dir_.Delete()); }
diff --git a/chrome/browser/ash/crosapi/move_migrator_unittest.cc b/chrome/browser/ash/crosapi/move_migrator_unittest.cc
index 8706c14..ebe603b 100644
--- a/chrome/browser/ash/crosapi/move_migrator_unittest.cc
+++ b/chrome/browser/ash/crosapi/move_migrator_unittest.cc
@@ -46,7 +46,6 @@
 
 constexpr char kDataFilePath[] = "Data";
 constexpr char kDataContent[] = "Hello, World!";
-constexpr int kDataSize = sizeof(kDataContent);
 
 // ID of an extension that will be moved from Ash to Lacros.
 // NOTE: we use a sequence of characters that can't be an
@@ -82,10 +81,8 @@
   // Generate data for an extension that has to be moved to Lacros.
   if (lacros) {
     ASSERT_TRUE(base::CreateDirectory(path.Append(kMoveExtensionId)));
-    ASSERT_EQ(
-        base::WriteFile(path.Append(kMoveExtensionId).Append(kDataFilePath),
-                        kDataContent, kDataSize),
-        kDataSize);
+    ASSERT_TRUE(base::WriteFile(
+        path.Append(kMoveExtensionId).Append(kDataFilePath), kDataContent));
   }
 
   // Generate data for an extension that has to stay in Ash.
@@ -93,10 +90,8 @@
     std::string keep_extension_id =
         browser_data_migrator_util::kExtensionsAshOnly[0];
     ASSERT_TRUE(base::CreateDirectory(path.Append(keep_extension_id)));
-    ASSERT_EQ(
-        base::WriteFile(path.Append(keep_extension_id).Append(kDataFilePath),
-                        kDataContent, kDataSize),
-        kDataSize);
+    ASSERT_TRUE(base::WriteFile(
+        path.Append(keep_extension_id).Append(kDataFilePath), kDataContent));
   }
 
   // Generate data for an extension that has to be in both Ash and Lacros.
@@ -104,10 +99,8 @@
     std::string both_extension_id =
         browser_data_migrator_util::kExtensionsBothChromes[0];
     ASSERT_TRUE(base::CreateDirectory(path.Append(both_extension_id)));
-    ASSERT_EQ(
-        base::WriteFile(path.Append(both_extension_id).Append(kDataFilePath),
-                        kDataContent, kDataSize),
-        kDataSize);
+    ASSERT_TRUE(base::WriteFile(
+        path.Append(both_extension_id).Append(kDataFilePath), kDataContent));
   }
 }
 
@@ -126,10 +119,8 @@
   // Generate data for an extension that has to be moved to Lacros.
   if (lacros) {
     ASSERT_TRUE(base::CreateDirectory(path.Append(kMoveExtensionId)));
-    ASSERT_EQ(
-        base::WriteFile(path.Append(kMoveExtensionId).Append(kDataFilePath),
-                        kDataContent, kDataSize),
-        kDataSize);
+    ASSERT_TRUE(base::WriteFile(
+        path.Append(kMoveExtensionId).Append(kDataFilePath), kDataContent));
   }
 
   // Generate data for an extension that has to stay in Ash.
@@ -137,10 +128,8 @@
     std::string keep_extension_id =
         browser_data_migrator_util::kExtensionsAshOnly[0];
     ASSERT_TRUE(base::CreateDirectory(path.Append(keep_extension_id)));
-    ASSERT_EQ(
-        base::WriteFile(path.Append(keep_extension_id).Append(kDataFilePath),
-                        kDataContent, kDataSize),
-        kDataSize);
+    ASSERT_TRUE(base::WriteFile(
+        path.Append(keep_extension_id).Append(kDataFilePath), kDataContent));
   }
 
   // Generate data for an extension that has to be in both Ash and Lacros.
@@ -148,10 +137,8 @@
     std::string both_extension_id =
         browser_data_migrator_util::kExtensionsBothChromes[0];
     ASSERT_TRUE(base::CreateDirectory(path.Append(both_extension_id)));
-    ASSERT_EQ(
-        base::WriteFile(path.Append(both_extension_id).Append(kDataFilePath),
-                        kDataContent, kDataSize),
-        kDataSize);
+    ASSERT_TRUE(base::WriteFile(
+        path.Append(both_extension_id).Append(kDataFilePath), kDataContent));
   }
 }
 
@@ -240,12 +227,10 @@
                                                       kMoveExtensionId);
     ASSERT_TRUE(base::CreateDirectory(move_extension_blob_path));
     ASSERT_TRUE(base::CreateDirectory(move_extension_leveldb_path));
-    ASSERT_EQ(base::WriteFile(move_extension_blob_path.Append(kDataFilePath),
-                              kDataContent, kDataSize),
-              kDataSize);
-    ASSERT_EQ(base::WriteFile(move_extension_leveldb_path.Append(kDataFilePath),
-                              kDataContent, kDataSize),
-              kDataSize);
+    ASSERT_TRUE(base::WriteFile(move_extension_blob_path.Append(kDataFilePath),
+                                kDataContent));
+    ASSERT_TRUE(base::WriteFile(
+        move_extension_leveldb_path.Append(kDataFilePath), kDataContent));
   }
 
   if (ash) {
@@ -256,12 +241,10 @@
                                                       keep_extension_id);
     ASSERT_TRUE(base::CreateDirectory(keep_extension_blob_path));
     ASSERT_TRUE(base::CreateDirectory(keep_extension_leveldb_path));
-    ASSERT_EQ(base::WriteFile(keep_extension_blob_path.Append(kDataFilePath),
-                              kDataContent, kDataSize),
-              kDataSize);
-    ASSERT_EQ(base::WriteFile(keep_extension_leveldb_path.Append(kDataFilePath),
-                              kDataContent, kDataSize),
-              kDataSize);
+    ASSERT_TRUE(base::WriteFile(keep_extension_blob_path.Append(kDataFilePath),
+                                kDataContent));
+    ASSERT_TRUE(base::WriteFile(
+        keep_extension_leveldb_path.Append(kDataFilePath), kDataContent));
   }
 
   if (both) {
@@ -272,12 +255,10 @@
                                                       both_extension_id);
     ASSERT_TRUE(base::CreateDirectory(both_extension_blob_path));
     ASSERT_TRUE(base::CreateDirectory(both_extension_leveldb_path));
-    ASSERT_EQ(base::WriteFile(both_extension_blob_path.Append(kDataFilePath),
-                              kDataContent, kDataSize),
-              kDataSize);
-    ASSERT_EQ(base::WriteFile(both_extension_leveldb_path.Append(kDataFilePath),
-                              kDataContent, kDataSize),
-              kDataSize);
+    ASSERT_TRUE(base::WriteFile(both_extension_blob_path.Append(kDataFilePath),
+                                kDataContent));
+    ASSERT_TRUE(base::WriteFile(
+        both_extension_leveldb_path.Append(kDataFilePath), kDataContent));
   }
 }
 
@@ -369,35 +350,25 @@
   //     |- LevelDB
   //     |- Nigori.bin
   ASSERT_TRUE(base::CreateDirectory(path.Append(kCacheFilePath)));
-  ASSERT_EQ(base::WriteFile(path.Append(kCacheFilePath).Append(kDataFilePath),
-                            kDataContent, kDataSize),
-            kDataSize);
+  ASSERT_TRUE(base::WriteFile(path.Append(kCacheFilePath).Append(kDataFilePath),
+                              kDataContent));
 
   ASSERT_TRUE(base::CreateDirectory(path.Append(kDownloadsFilePath)));
-  ASSERT_EQ(
-      base::WriteFile(path.Append(kDownloadsFilePath).Append(kDataFilePath),
-                      kDataContent, kDataSize),
-      kDataSize);
+  ASSERT_TRUE(base::WriteFile(
+      path.Append(kDownloadsFilePath).Append(kDataFilePath), kDataContent));
 
   ASSERT_TRUE(base::CreateDirectory(path.Append(kBookmarksFilePath)));
-  ASSERT_EQ(
-      base::WriteFile(path.Append(kBookmarksFilePath).Append(kDataFilePath),
-                      kDataContent, kDataSize),
-      kDataSize);
-  ASSERT_EQ(
-      base::WriteFile(path.Append(kCookiesFilePath), kDataContent, kDataSize),
-      kDataSize);
+  ASSERT_TRUE(base::WriteFile(
+      path.Append(kBookmarksFilePath).Append(kDataFilePath), kDataContent));
+  ASSERT_TRUE(base::WriteFile(path.Append(kCookiesFilePath), kDataContent));
 
   ASSERT_TRUE(base::CreateDirectory(path.Append(kSharedProtoDBPath)));
-  ASSERT_EQ(
-      base::WriteFile(path.Append(kSharedProtoDBPath).Append(kDataFilePath),
-                      kDataContent, kDataSize),
-      kDataSize);
+  ASSERT_TRUE(base::WriteFile(
+      path.Append(kSharedProtoDBPath).Append(kDataFilePath), kDataContent));
 
   ASSERT_TRUE(base::CreateDirectory(
       path.Append(browser_data_migrator_util::kSyncDataFilePath)));
-  ASSERT_EQ(base::WriteFile(GetNigoriPath(path), kDataContent, kDataSize),
-            kDataSize);
+  ASSERT_TRUE(base::WriteFile(GetNigoriPath(path), kDataContent));
 
   SetUpExtensions(path);
   SetUpStorage(path);
@@ -450,11 +421,10 @@
   EXPECT_TRUE(base::CreateDirectory(original_profile_dir_2));
   EXPECT_TRUE(base::CreateDirectory(
       original_profile_dir_2.Append(browser_data_migrator_util::kLacrosDir)));
-  EXPECT_EQ(base::WriteFile(original_profile_dir_2
-                                .Append(browser_data_migrator_util::kLacrosDir)
-                                .Append(chrome::kFirstRunSentinel),
-                            "", 0),
-            0);
+  ASSERT_TRUE(base::WriteFile(
+      original_profile_dir_2.Append(browser_data_migrator_util::kLacrosDir)
+          .Append(chrome::kFirstRunSentinel),
+      base::StringPiece()));
   MoveMigrator::TaskResult result_2 =
       MoveMigrator::PreMigrationCleanUp(original_profile_dir_2);
   ASSERT_EQ(result_2.status, MoveMigrator::TaskStatus::kSucceeded);
@@ -466,9 +436,8 @@
   const base::FilePath original_profile_dir_3 =
       scoped_temp_dir.GetPath().Append("user3");
   EXPECT_TRUE(base::CreateDirectory(original_profile_dir_3));
-  ASSERT_EQ(base::WriteFile(original_profile_dir_3.Append(kCacheFilePath),
-                            kDataContent, kDataSize),
-            kDataSize);
+  ASSERT_TRUE(base::WriteFile(original_profile_dir_3.Append(kCacheFilePath),
+                              kDataContent));
   MoveMigrator::TaskResult result_3 =
       MoveMigrator::PreMigrationCleanUp(original_profile_dir_3);
   ASSERT_EQ(result_3.status, MoveMigrator::TaskStatus::kSucceeded);
@@ -484,12 +453,10 @@
   EXPECT_TRUE(base::CreateDirectory(original_profile_dir_4));
   EXPECT_TRUE(base::CreateDirectory(
       original_profile_dir_4.Append(browser_data_migrator_util::kSplitTmpDir)));
-  EXPECT_EQ(
-      base::WriteFile(original_profile_dir_4
-                          .Append(browser_data_migrator_util::kSplitTmpDir)
-                          .Append("TestFile"),
-                      kDataContent, kDataSize),
-      kDataSize);
+  ASSERT_TRUE(base::WriteFile(
+      original_profile_dir_4.Append(browser_data_migrator_util::kSplitTmpDir)
+          .Append("TestFile"),
+      kDataContent));
   MoveMigrator::TaskResult result_4 =
       MoveMigrator::PreMigrationCleanUp(original_profile_dir_4);
   ASSERT_EQ(result_4.status, MoveMigrator::TaskStatus::kSucceeded);
@@ -975,9 +942,8 @@
       original_profile_dir_.Append(kCacheFilePath)));
 
   ASSERT_TRUE(base::CreateDirectory(tmp_user_dir));
-  ASSERT_EQ(
-      base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel), "", 0),
-      0);
+  ASSERT_TRUE(base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel),
+                              base::StringPiece()));
   ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
   ASSERT_TRUE(base::CreateDirectory(tmp_split_dir));
   ASSERT_TRUE(
@@ -1085,9 +1051,8 @@
       original_profile_dir_.Append(kCacheFilePath)));
 
   ASSERT_TRUE(base::CreateDirectory(tmp_user_dir));
-  ASSERT_EQ(
-      base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel), "", 0),
-      0);
+  ASSERT_TRUE(base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel),
+                              base::StringPiece()));
   ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
   ASSERT_TRUE(base::CreateDirectory(tmp_split_dir));
   ASSERT_TRUE(
@@ -1195,9 +1160,8 @@
       original_profile_dir_.Append(kCacheFilePath)));
 
   ASSERT_TRUE(base::CreateDirectory(tmp_user_dir));
-  ASSERT_EQ(
-      base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel), "", 0),
-      0);
+  ASSERT_TRUE(base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel),
+                              base::StringPiece()));
   ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
   ASSERT_TRUE(
       base::CopyDirectory(original_profile_dir_.Append(kSharedProtoDBPath),
diff --git a/chrome/browser/ash/crosapi/persistent_forced_extension_keep_alive_unittest.cc b/chrome/browser/ash/crosapi/persistent_forced_extension_keep_alive_unittest.cc
index 277f93a..20cad51 100644
--- a/chrome/browser/ash/crosapi/persistent_forced_extension_keep_alive_unittest.cc
+++ b/chrome/browser/ash/crosapi/persistent_forced_extension_keep_alive_unittest.cc
@@ -10,12 +10,15 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/pref_names.h"
 #include "extensions/common/extension_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ash::standalone_browser::BrowserSupport;
+
 namespace {
 
 const char kAllowedExtensionId[] = "baobpecgllpajfeojepgedjdlnlfffde";
@@ -76,7 +79,7 @@
   content::BrowserTaskEnvironment task_environment_;
 
   base::AutoReset<bool> set_lacros_enabled_ =
-      browser_util::SetLacrosEnabledForTest(true);
+      BrowserSupport::SetLacrosEnabledForTest(true);
 
   std::unique_ptr<FakeBrowserManager> browser_manager_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
diff --git a/chrome/browser/ash/crostini/ansible/ansible_management_test_helper.cc b/chrome/browser/ash/crostini/ansible/ansible_management_test_helper.cc
index 79894478..f5d87e095 100644
--- a/chrome/browser/ash/crostini/ansible/ansible_management_test_helper.cc
+++ b/chrome/browser/ash/crostini/ansible/ansible_management_test_helper.cc
@@ -36,7 +36,7 @@
   base::FilePath ansible_playbook_file_path =
       profile_->GetPath().AppendASCII("playbook.yaml");
   const char playbook[] = "---";
-  base::WriteFile(ansible_playbook_file_path, playbook, strlen(playbook));
+  base::WriteFile(ansible_playbook_file_path, playbook);
   profile_->GetPrefs()->SetFilePath(prefs::kCrostiniAnsiblePlaybookFilePath,
                                     ansible_playbook_file_path);
 }
diff --git a/chrome/browser/ash/crostini/crostini_export_import.cc b/chrome/browser/ash/crostini/crostini_export_import.cc
index c5247caa..05fb6a30 100644
--- a/chrome/browser/ash/crostini/crostini_export_import.cc
+++ b/chrome/browser/ash/crostini/crostini_export_import.cc
@@ -68,6 +68,10 @@
   }
 };
 
+void CrostiniExportImport::EnsureFactoryBuilt() {
+  CrostiniExportImportFactory::GetInstance();
+}
+
 CrostiniExportImport* CrostiniExportImport::GetForProfile(Profile* profile) {
   return CrostiniExportImportFactory::GetForProfile(profile);
 }
diff --git a/chrome/browser/ash/crostini/crostini_export_import.h b/chrome/browser/ash/crostini/crostini_export_import.h
index cde3ddb..7aca83b1 100644
--- a/chrome/browser/ash/crostini/crostini_export_import.h
+++ b/chrome/browser/ash/crostini/crostini_export_import.h
@@ -89,6 +89,10 @@
     OnceTrackerFactory tracker_factory;
   };
 
+  // Call this method to ensure that the factory for this KeyedService is built
+  // (necessary for embedders that disallow lazy initialization of factories).
+  static void EnsureFactoryBuilt();
+
   static CrostiniExportImport* GetForProfile(Profile* profile);
 
   explicit CrostiniExportImport(Profile* profile);
diff --git a/chrome/browser/ash/customization/customization_wallpaper_util.cc b/chrome/browser/ash/customization/customization_wallpaper_util.cc
index 5fc992d..fa454f5 100644
--- a/chrome/browser/ash/customization/customization_wallpaper_util.cc
+++ b/chrome/browser/ash/customization/customization_wallpaper_util.cc
@@ -38,9 +38,8 @@
   scoped_refptr<base::RefCountedBytes> image_data = new base::RefCountedBytes();
   gfx::JPEGCodec::Encode(*resized_image.bitmap(), 90 /*quality=*/,
                          &image_data->data());
-  size_t written_bytes = base::WriteFile(
-      file_path, image_data->front_as<const char>(), image_data->size());
-  return written_bytes == image_data->size();
+  return base::WriteFile(
+      file_path, base::make_span(image_data->front(), image_data->size()));
 }
 
 // Returns true if both file paths exist.
diff --git a/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc b/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc
index 9fb59f3d..6f46239 100644
--- a/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc
+++ b/chrome/browser/ash/dbus/vm/vm_applications_service_provider.cc
@@ -113,6 +113,12 @@
   Profile* profile = ProfileManager::GetPrimaryUserProfile();
   auto* registry_service =
       guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile);
+  if (!registry_service) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
+                                                 "Shutting down"));
+    return;
+  }
   registry_service->UpdateApplicationList(request);
 
   std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
@@ -168,8 +174,15 @@
   }
 
   Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  guest_os::GuestOsMimeTypesServiceFactory::GetForProfile(profile)
-      ->UpdateMimeTypes(request);
+  auto* mime_types_service =
+      guest_os::GuestOsMimeTypesServiceFactory::GetForProfile(profile);
+  if (!mime_types_service) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
+                                                 "Shutting down"));
+    return;
+  }
+  mime_types_service->UpdateMimeTypes(request);
 
   std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
 }
diff --git a/chrome/browser/ash/extensions/default_app_order_unittest.cc b/chrome/browser/ash/extensions/default_app_order_unittest.cc
index f34022bb..a22be98 100644
--- a/chrome/browser/ash/extensions/default_app_order_unittest.cc
+++ b/chrome/browser/ash/extensions/default_app_order_unittest.cc
@@ -59,7 +59,7 @@
   void CreateExternalOrderFile(const std::string& content) {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     base::FilePath external_file = temp_dir_.GetPath().Append(kTestFile);
-    base::WriteFile(external_file, content.c_str(), content.size());
+    base::WriteFile(external_file, content);
     SetExternalFile(external_file);
   }
 
diff --git a/chrome/browser/ash/extensions/external_cache_impl_unittest.cc b/chrome/browser/ash/extensions/external_cache_impl_unittest.cc
index ca9eaba..a769cca6 100644
--- a/chrome/browser/ash/extensions/external_cache_impl_unittest.cc
+++ b/chrome/browser/ash/extensions/external_cache_impl_unittest.cc
@@ -104,7 +104,7 @@
   }
 
   void CreateFile(const base::FilePath& file) {
-    EXPECT_EQ(base::WriteFile(file, nullptr, 0), 0);
+    EXPECT_TRUE(base::WriteFile(file, base::StringPiece()));
   }
 
   base::FilePath GetExtensionFile(const base::FilePath& dir,
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks.cc b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
index f5d2c62c..925871ee 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
@@ -107,13 +107,19 @@
   }
 }
 
-const char kImportCrostiniImageHandlerId[] = "import-crostini-image";
-const char kInstallLinuxPackageHandlerId[] = "install-linux-package";
+const char kImportCrostiniImageHandlerId[] =
+    "chrome://file-manager/?import-crostini-image";
+const char kInstallLinuxPackageHandlerId[] =
+    "chrome://file-manager/?install-linux-package";
 
 }  // namespace
 
 bool FileHandlerIsEnabled(Profile* profile,
+                          const std::string& app_id,
                           const std::string& file_handler_id) {
+  if (app_id != kFileManagerSwaAppId) {
+    return true;
+  }
   // Crostini deb files and backup files can be disabled by policy.
   if (file_handler_id == kInstallLinuxPackageHandlerId) {
     return crostini::CrostiniFeatures::Get()->IsRootAccessAllowed(profile);
@@ -279,9 +285,6 @@
       if (profile->IsOffTheRecord() &&
           !extensions::util::IsIncognitoEnabled(launch_entry.app_id, profile))
         continue;
-      if (!FileHandlerIsEnabled(profile_with_app_service,
-                                launch_entry.activity_name))
-        continue;
     }
 
     if ((app_type == apps::AppType::kCrostini ||
@@ -290,6 +293,11 @@
       continue;
     }
 
+    if (!FileHandlerIsEnabled(profile_with_app_service, launch_entry.app_id,
+                              launch_entry.activity_name)) {
+      continue;
+    }
+
     constexpr int kIconSize = 32;
     GURL icon_url =
         apps::AppIconSource::GetIconURL(launch_entry.app_id, kIconSize);
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks.h b/chrome/browser/ash/file_manager/app_service_file_tasks.h
index 1094bd5..443ef5b3 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks.h
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks.h
@@ -25,7 +25,9 @@
 
 // Returns true if a file handler is enabled. Some handlers such as
 // import-crostini-image can be disabled at runtime by enterprise policy.
-bool FileHandlerIsEnabled(Profile* profile, const std::string& file_handler_id);
+bool FileHandlerIsEnabled(Profile* profile,
+                          const std::string& app_id,
+                          const std::string& file_handler_id);
 
 // Returns a profile that has App Service available. App Service doesn't exist
 // in Incognito mode, so when the user opens a file from the downloads page
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
index 638d182..1eec752 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
@@ -18,6 +18,8 @@
 #include "chrome/browser/apps/app_service/intent_util.h"
 #include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ash/crostini/crostini_test_helper.h"
+#include "chrome/browser/ash/crostini/fake_crostini_features.h"
+#include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/ash/file_manager/file_manager_test_util.h"
 #include "chrome/browser/ash/file_manager/file_tasks.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
@@ -922,6 +924,52 @@
   ASSERT_EQ(0U, tasks.size());
 }
 
+TEST_F(AppServiceFileTasksTestEnabled, CrositiniTasksControlledByPolicy) {
+  std::string tini_task_name = "chrome://file-manager/?import-crostini-image";
+  std::string deb_task_name = "chrome://file-manager/?install-linux-package";
+  std::vector<apps::IntentFilterPtr> filters;
+  filters.push_back(
+      apps_util::MakeFileFilterForView("tini", "tini", "import-tini"));
+  filters[0]->activity_name = tini_task_name;
+  filters.push_back(
+      apps_util::MakeFileFilterForView("deb", "deb", "import-deb"));
+  filters[1]->activity_name = deb_task_name;
+  AddFakeAppWithIntentFilters(file_manager::kFileManagerSwaAppId,
+                              std::move(filters), apps::AppType::kWeb, true,
+                              app_service_proxy_);
+
+  std::string file_name = "test.tini";
+
+  crostini::FakeCrostiniFeatures crostini_features;
+  crostini_features.set_export_import_ui_allowed(true);
+  std::vector<FullTaskDescriptor> tasks = FindAppServiceTasks({{file_name}});
+
+  ASSERT_EQ(1U, tasks.size());
+  EXPECT_EQ(file_manager::kFileManagerSwaAppId,
+            tasks[0].task_descriptor.app_id);
+  EXPECT_EQ(tini_task_name, tasks[0].task_descriptor.action_id);
+
+  crostini_features.set_export_import_ui_allowed(false);
+  tasks = FindAppServiceTasks({{file_name}});
+
+  ASSERT_EQ(0U, tasks.size());
+
+  file_name = "test.deb";
+
+  crostini_features.set_root_access_allowed(true);
+  tasks = FindAppServiceTasks({{file_name}});
+
+  ASSERT_EQ(1U, tasks.size());
+  EXPECT_EQ(file_manager::kFileManagerSwaAppId,
+            tasks[0].task_descriptor.app_id);
+  EXPECT_EQ(deb_task_name, tasks[0].task_descriptor.action_id);
+
+  crostini_features.set_root_access_allowed(false);
+  tasks = FindAppServiceTasks({{file_name}});
+
+  ASSERT_EQ(0U, tasks.size());
+}
+
 // Tests applying policies when listing tasks.
 class AppServiceFileTasksPolicyTest : public AppServiceFileTasksTestEnabled {
  protected:
diff --git a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
index 8ebd6321..7dfd7dfd 100644
--- a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
@@ -1386,8 +1386,7 @@
     feature_list_.InitAndEnableFeature(ash::features::kUploadOfficeToCloud);
     test_file_name_ = "text.docx";
     // Relative path for a file on ODFS and Android OneDrive.
-    relative_test_path_ = base::FilePath(ash::cloud_upload::kDestinationFolder)
-                              .Append(test_file_name_);
+    relative_test_path_ = base::FilePath(test_file_name_);
     // The path in ODFS is the relative path with "/" prefixed.
     test_path_within_odfs_ = base::FilePath("/").Append(relative_test_path_);
     file_system_id_ = "odfs";
diff --git a/chrome/browser/ash/file_manager/file_tasks_notifier_unittest.cc b/chrome/browser/ash/file_manager/file_tasks_notifier_unittest.cc
index 29cbb9b..53d03b5 100644
--- a/chrome/browser/ash/file_manager/file_tasks_notifier_unittest.cc
+++ b/chrome/browser/ash/file_manager/file_tasks_notifier_unittest.cc
@@ -140,7 +140,7 @@
 
     my_files_ = util::GetMyFilesFolderForProfile(profile_.get());
     ASSERT_TRUE(base::CreateDirectory(my_files_));
-    base::WriteFile(my_files_.Append("file"), "data", 4);
+    base::WriteFile(my_files_.Append("file"), "data");
     ASSERT_TRUE(mount_points->RegisterFileSystem(
         "downloads", storage::kFileSystemTypeLocal, {}, my_files_));
     ASSERT_TRUE(mount_points->RegisterFileSystem(
diff --git a/chrome/browser/ash/file_manager/file_tasks_unittest.cc b/chrome/browser/ash/file_manager/file_tasks_unittest.cc
index e043c10..41949fd 100644
--- a/chrome/browser/ash/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/ash/file_manager/file_tasks_unittest.cc
@@ -188,20 +188,32 @@
   const std::string test_id = "test";
 
   crostini_features.set_export_import_ui_allowed(true);
-  EXPECT_TRUE(FileHandlerIsEnabled(&test_profile, "import-crostini-image"));
-  EXPECT_TRUE(FileHandlerIsEnabled(&test_profile, test_id));
+  EXPECT_TRUE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId,
+                           "chrome://file-manager/?import-crostini-image"));
+  EXPECT_TRUE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId, test_id));
 
   crostini_features.set_export_import_ui_allowed(false);
-  EXPECT_FALSE(FileHandlerIsEnabled(&test_profile, "import-crostini-image"));
-  EXPECT_TRUE(FileHandlerIsEnabled(&test_profile, test_id));
+  EXPECT_FALSE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId,
+                           "chrome://file-manager/?import-crostini-image"));
+  EXPECT_TRUE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId, test_id));
 
   crostini_features.set_root_access_allowed(true);
-  EXPECT_TRUE(FileHandlerIsEnabled(&test_profile, "install-linux-package"));
-  EXPECT_TRUE(FileHandlerIsEnabled(&test_profile, test_id));
+  EXPECT_TRUE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId,
+                           "chrome://file-manager/?install-linux-package"));
+  EXPECT_TRUE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId, test_id));
 
   crostini_features.set_root_access_allowed(false);
-  EXPECT_FALSE(FileHandlerIsEnabled(&test_profile, "install-linux-package"));
-  EXPECT_TRUE(FileHandlerIsEnabled(&test_profile, test_id));
+  EXPECT_FALSE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId,
+                           "chrome://file-manager/?install-linux-package"));
+  EXPECT_TRUE(
+      FileHandlerIsEnabled(&test_profile, kFileManagerSwaAppId, test_id));
 }
 
 class FileManagerFileTaskWithAppServiceTest : public testing::Test {
diff --git a/chrome/browser/ash/fileapi/file_system_backend.cc b/chrome/browser/ash/fileapi/file_system_backend.cc
index e8614624..4bc2c2f4 100644
--- a/chrome/browser/ash/fileapi/file_system_backend.cc
+++ b/chrome/browser/ash/fileapi/file_system_backend.cc
@@ -12,6 +12,7 @@
 #include "ash/webui/file_manager/url_constants.h"
 #include "base/check_op.h"
 #include "base/command_line.h"
+#include "base/functional/callback_helpers.h"
 #include "base/notreached.h"
 #include "base/strings/escape.h"
 #include "base/task/task_traits.h"
@@ -24,6 +25,7 @@
 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
 #include "chrome/common/url_constants.h"
 #include "chromeos/ash/components/dbus/cros_disks/cros_disks_client.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "components/user_manager/user.h"
 #include "storage/browser/file_system/async_file_util.h"
 #include "storage/browser/file_system/external_mount_points.h"
@@ -421,7 +423,9 @@
     int64_t offset,
     int64_t max_bytes_to_read,
     const base::Time& expected_modification_time,
-    storage::FileSystemContext* context) const {
+    storage::FileSystemContext* context,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) const {
   DCHECK(url.is_valid());
 
   if (!IsAccessAllowed(url))
@@ -431,8 +435,15 @@
     case storage::kFileSystemTypeProvided:
       return file_system_provider_delegate_->CreateFileStreamReader(
           url, offset, max_bytes_to_read, expected_modification_time, context);
+    // The dlp file_access callback is needed for the local filesystem only.
     case storage::kFileSystemTypeLocal:
     case storage::kFileSystemTypeRestrictedLocal:
+      return storage::FileStreamReader::CreateForLocalFile(
+          base::ThreadPool::CreateTaskRunner(
+              {base::MayBlock(), base::TaskPriority::USER_VISIBLE})
+              .get(),
+          url.path(), offset, expected_modification_time,
+          std::move(file_access));
     case storage::kFileSystemTypeDriveFs:
     case storage::kFileSystemTypeSmbFs:
     case storage::kFileSystemTypeFuseBox:
diff --git a/chrome/browser/ash/fileapi/file_system_backend.h b/chrome/browser/ash/fileapi/file_system_backend.h
index 13cfb468..5037b02 100644
--- a/chrome/browser/ash/fileapi/file_system_backend.h
+++ b/chrome/browser/ash/fileapi/file_system_backend.h
@@ -14,6 +14,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "components/account_id/account_id.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/task_runner_bound_observer_list.h"
 #include "storage/common/file_system/file_system_types.h"
@@ -125,7 +126,9 @@
       int64_t offset,
       int64_t max_bytes_to_read,
       const base::Time& expected_modification_time,
-      storage::FileSystemContext* context) const override;
+      storage::FileSystemContext* context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) const override;
   std::unique_ptr<storage::FileStreamWriter> CreateFileStreamWriter(
       const storage::FileSystemURL& url,
       int64_t offset,
diff --git a/chrome/browser/ash/guest_os/guest_os_registry_service.cc b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
index 340bce9..a6a41607 100644
--- a/chrome/browser/ash/guest_os/guest_os_registry_service.cc
+++ b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
@@ -182,8 +182,7 @@
 
   base::CreateDirectory(icon_path.DirName());
 
-  int wrote = base::WriteFile(icon_path, content.c_str(), content.size());
-  if (wrote != static_cast<int>(content.size())) {
+  if (!base::WriteFile(icon_path, content)) {
     VLOG(2) << "Failed to write Crostini icon file: "
             << icon_path.MaybeAsASCII();
     if (!base::DeleteFile(icon_path)) {
diff --git a/chrome/browser/ash/login/demo_mode/demo_setup_test_utils.cc b/chrome/browser/ash/login/demo_mode/demo_setup_test_utils.cc
index 9278e38..f4bb384 100644
--- a/chrome/browser/ash/login/demo_mode/demo_setup_test_utils.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_setup_test_utils.cc
@@ -79,7 +79,8 @@
     return false;
   }
 
-  if (base::WriteFile(policy_dir.AppendASCII("device_policy"), "", 0) != 0) {
+  if (!base::WriteFile(policy_dir.AppendASCII("device_policy"),
+                       base::StringPiece())) {
     LOG(ERROR) << "Failed to create device_policy file";
     return false;
   }
@@ -95,9 +96,8 @@
     policy.set_policy_data(policy_data.SerializeAsString());
     policy_blob = policy.SerializeAsString();
   }
-  if (base::WriteFile(policy_dir.AppendASCII("local_account_policy"),
-                      policy_blob.data(), policy_blob.size()) !=
-      static_cast<int>(policy_blob.size())) {
+  if (!base::WriteFile(policy_dir.AppendASCII("local_account_policy"),
+                       policy_blob)) {
     LOG(ERROR) << "Failed to create local_account_policy file";
     return false;
   }
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc
index a6a6f4a..9b558c0 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.cc
@@ -10,7 +10,6 @@
 #include "base/json/json_writer.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h"
 #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h"
 #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom.h"
 #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom.h"
@@ -72,10 +71,8 @@
 
 AuthenticatedConnection::AuthenticatedConnection(
     NearbyConnection* nearby_connection,
-    mojo::SharedRemote<mojom::QuickStartDecoder> remote,
-    RandomSessionId session_id,
-    SharedSecret shared_secret)
-    : Connection(nearby_connection, session_id, shared_secret) {
+    mojo::SharedRemote<mojom::QuickStartDecoder> remote)
+    : Connection(nearby_connection) {
   decoder_ = std::move(remote);
 }
 
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h
index 3e01b63..cfe8c45 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection.h
@@ -11,7 +11,6 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/wifi_credentials.h"
 #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h"
 #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder.mojom.h"
@@ -33,10 +32,9 @@
   using RequestWifiCredentialsCallback =
       base::OnceCallback<void(absl::optional<WifiCredentials>)>;
 
-  AuthenticatedConnection(NearbyConnection* nearby_connection,
-                          mojo::SharedRemote<mojom::QuickStartDecoder> remote,
-                          RandomSessionId session_id,
-                          SharedSecret shared_secret);
+  explicit AuthenticatedConnection(
+      NearbyConnection* nearby_connection,
+      mojo::SharedRemote<mojom::QuickStartDecoder> remote);
 
   AuthenticatedConnection(AuthenticatedConnection&) = delete;
   AuthenticatedConnection& operator=(AuthenticatedConnection&) = delete;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc
index e8c631d3..858d34e 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/authenticated_connection_unittest.cc
@@ -43,16 +43,6 @@
 const char kNotifySourceOfUpdateMessageKey[] = "isForcedUpdateRequired";
 constexpr uint8_t kSuccess = 0x00;
 
-// 32 random bytes to use as the shared secret.
-constexpr std::array<uint8_t, 32> kSharedSecret = {
-    0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59,
-    0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4,
-    0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2};
-
-// 6 random bytes to use as the RandomSessionId.
-constexpr std::array<uint8_t, 6> kRandomSessionId = {0x6b, 0xb3, 0x85,
-                                                     0x27, 0xbb, 0x28};
-
 }  // namespace
 
 class AuthenticatedConnectionTest : public testing::Test {
@@ -66,12 +56,11 @@
     fake_nearby_connection_ = std::make_unique<FakeNearbyConnection>();
     fake_quick_start_decoder_ = std::make_unique<FakeQuickStartDecoder>();
     NearbyConnection* nearby_connection = fake_nearby_connection_.get();
-    RandomSessionId session_id(kRandomSessionId);
+
     authenticated_connection_ = std::make_unique<AuthenticatedConnection>(
         nearby_connection,
         mojo::SharedRemote<ash::quick_start::mojom::QuickStartDecoder>(
-            fake_quick_start_decoder_->GetRemote()),
-        session_id, kSharedSecret);
+            fake_quick_start_decoder_->GetRemote()));
   }
 
   std::string CreateFidoClientDataJson(url::Origin origin) {
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
index ba0bc9a5..b490423 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
@@ -6,24 +6,12 @@
 
 #include "base/json/json_writer.h"
 #include "base/values.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h"
 #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h"
-#include "crypto/random.h"
 
 namespace ash::quick_start {
 
-Connection::Connection(NearbyConnection* nearby_connection,
-                       RandomSessionId session_id,
-                       SharedSecret shared_secret)
-    : nearby_connection_(nearby_connection),
-      random_session_id_(session_id),
-      shared_secret_(shared_secret) {}
-
-Connection::Connection(NearbyConnection* nearby_connection,
-                       RandomSessionId session_id)
-    : nearby_connection_(nearby_connection), random_session_id_(session_id) {
-  crypto::RandBytes(shared_secret_);
-}
+Connection::Connection(NearbyConnection* nearby_connection)
+    : nearby_connection_(nearby_connection) {}
 
 void Connection::SendPayload(const base::Value::Dict& message_payload) {
   std::string json_serialized_payload;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
index 66be7a0..0c2b2704 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_CONNECTION_H_
 
 #include "base/values.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h"
 #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h"
 
 namespace ash::quick_start {
@@ -15,14 +14,7 @@
 // a Nearby Connection.
 class Connection {
  public:
-  using SharedSecret = std::array<uint8_t, 32>;
-
-  Connection(NearbyConnection* nearby_connection, RandomSessionId session_id);
-
-  Connection(NearbyConnection* nearby_connection,
-             RandomSessionId session_id,
-             SharedSecret shared_secret);
-
+  explicit Connection(NearbyConnection* nearby_connection);
   Connection(const Connection&) = delete;
   Connection& operator=(const Connection&) = delete;
   virtual ~Connection() = default;
@@ -41,8 +33,6 @@
                                   PayloadResponseCallback callback);
 
   NearbyConnection* nearby_connection_;
-  RandomSessionId random_session_id_;
-  SharedSecret shared_secret_;
 };
 
 }  // namespace ash::quick_start
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc
index 577fdbdaf..3b1a519 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h"
 #include "chrome/browser/nearby_sharing/fake_nearby_connection.h"
 #include "chrome/browser/nearby_sharing/public/cpp/nearby_connection.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,22 +20,10 @@
 constexpr char kTestMessagePayloadValue[] = "testValue";
 constexpr char kTestBytes[] = "testbytes";
 
-// 32 random bytes to use as the shared secret.
-constexpr std::array<uint8_t, 32> kSharedSecret = {
-    0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59,
-    0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4,
-    0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2};
-
-// 6 random bytes to use as the RandomSessionId.
-constexpr std::array<uint8_t, 6> kRandomSessionId = {0x6b, 0xb3, 0x85,
-                                                     0x27, 0xbb, 0x28};
-
 class FakeConnection : public Connection {
  public:
   explicit FakeConnection(NearbyConnection* nearby_connection)
-      : Connection(nearby_connection,
-                   RandomSessionId(kRandomSessionId),
-                   kSharedSecret) {}
+      : Connection(nearby_connection) {}
 
   void SendPayloadAndReadResponseWrapperForTesting(
       const base::Value::Dict& message_payload,
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc
index c88dd18..7e60868 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_target_device_connection_broker.cc
@@ -19,12 +19,6 @@
 // Arbitrary string to use as the connection's authentication token.
 constexpr char kAuthenticationToken[] = "auth_token";
 
-// 32 random bytes to use as the shared secret.
-constexpr std::array<uint8_t, 32> kSharedSecret = {
-    0x54, 0xbd, 0x40, 0xcf, 0x8a, 0x7c, 0x2f, 0x6a, 0xca, 0x15, 0x59,
-    0xcf, 0xf3, 0xeb, 0x31, 0x08, 0x90, 0x73, 0xef, 0xda, 0x87, 0xd4,
-    0x23, 0xc0, 0x55, 0xd5, 0x83, 0x5b, 0x04, 0x28, 0x49, 0xf2};
-
 }  // namespace
 
 FakeTargetDeviceConnectionBroker::Factory::Factory() = default;
@@ -84,13 +78,11 @@
   NearbyConnection* nearby_connection = fake_nearby_connection_.get();
   mojo::PendingRemote<mojom::QuickStartDecoder> remote;
   fake_quick_start_decoder_ = std::make_unique<FakeQuickStartDecoder>();
-  auto random_session_id = RandomSessionId();
   auto fake_authenticated_connection =
       std::make_unique<FakeAuthenticatedConnection>(
           nearby_connection,
           mojo::SharedRemote<ash::quick_start::mojom::QuickStartDecoder>(
-              fake_quick_start_decoder_->GetRemote()),
-          random_session_id, kSharedSecret);
+              fake_quick_start_decoder_->GetRemote()));
   connection_lifecycle_listener_->OnConnectionAuthenticated(
       source_device_id, fake_authenticated_connection->AsWeakPtr());
 
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc
index 4752ffee..98076cc43 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/incoming_connection.cc
@@ -15,15 +15,17 @@
 IncomingConnection::IncomingConnection(NearbyConnection* nearby_connection,
                                        RandomSessionId session_id,
                                        const std::string& authentication_token)
-    : Connection(nearby_connection, session_id),
+    : Connection(nearby_connection),
       random_session_id_(session_id),
-      pin_(DerivePin(authentication_token)) {}
+      pin_(DerivePin(authentication_token)) {
+  crypto::RandBytes(shared_secret_);
+}
 
 IncomingConnection::IncomingConnection(NearbyConnection* nearby_connection,
                                        RandomSessionId session_id,
                                        const std::string& authentication_token,
-                                       SharedSecret shared_secret)
-    : Connection(nearby_connection, session_id, shared_secret),
+                                       std::array<uint8_t, 32> shared_secret)
+    : Connection(nearby_connection),
       random_session_id_(session_id),
       shared_secret_(shared_secret),
       pin_(DerivePin(authentication_token)) {}
diff --git a/chrome/browser/ash/login/test/device_state_mixin.cc b/chrome/browser/ash/login/test/device_state_mixin.cc
index 9444108..411dabb 100644
--- a/chrome/browser/ash/login/test/device_state_mixin.cc
+++ b/chrome/browser/ash/login/test/device_state_mixin.cc
@@ -64,8 +64,7 @@
 }
 
 void WriteFile(const base::FilePath& path, const std::string& blob) {
-  CHECK_EQ(base::checked_cast<int>(blob.length()),
-           base::WriteFile(path, blob.data(), blob.length()));
+  CHECK(base::WriteFile(path, blob));
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/login/test/user_policy_mixin.cc b/chrome/browser/ash/login/test/user_policy_mixin.cc
index 1a01c36..34d3cbc 100644
--- a/chrome/browser/ash/login/test/user_policy_mixin.cc
+++ b/chrome/browser/ash/login/test/user_policy_mixin.cc
@@ -80,9 +80,8 @@
       user_keys_dir.AppendASCII(sanitized_username).AppendASCII("policy.pub");
 
   CHECK(base::CreateDirectory(user_key_file.DirName()));
-  int write_result = base::WriteFile(user_key_file, user_key_bits.data(),
-                                     user_key_bits.length());
-  DCHECK_EQ(static_cast<int>(user_key_bits.length()), write_result);
+  bool success = base::WriteFile(user_key_file, user_key_bits);
+  DCHECK(success);
 }
 
 void UserPolicyMixin::SetUpPolicy() {
diff --git a/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc b/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
index 2b5646d..ec28a77 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
@@ -71,9 +71,8 @@
                         const base::FilePath& image_path,
                         const base::FilePath& old_image_path) {
   if (image_bytes->size() == 0 ||
-      base::WriteFile(image_path,
-                      reinterpret_cast<const char*>(image_bytes->front()),
-                      image_bytes->size()) == -1) {
+      !base::WriteFile(image_path, base::make_span(image_bytes->front(),
+                                                   image_bytes->size()))) {
     LOG(ERROR) << "Failed to save image to file: " << image_path.AsUTF8Unsafe();
     return false;
   }
diff --git a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
index 3ff44e94..3705e2b 100644
--- a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
+++ b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
@@ -179,6 +179,7 @@
   feedback_params.send_histograms = report->include_system_logs_and_histograms;
   feedback_params.send_bluetooth_logs = report->send_bluetooth_logs;
   feedback_params.send_tab_titles = report->include_screenshot;
+  feedback_params.send_autofill_metadata = report->include_autofill_metadata;
   feedback_params.is_internal_email =
       report->feedback_context->is_internal_account;
 
@@ -211,6 +212,11 @@
     feedback_data->set_category_tag(feedback_context->category_tag.value());
   }
 
+  if (feedback_params.send_autofill_metadata &&
+      feedback_context->autofill_metadata.has_value()) {
+    feedback_data->set_autofill_metadata(*feedback_context->autofill_metadata);
+  }
+
   scoped_refptr<base::RefCountedMemory> png_data = GetScreenshotData();
   if (report->include_screenshot && png_data && png_data.get()) {
     feedback_data->set_image(
diff --git a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
index b8ec936..e8e106c4 100644
--- a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
+++ b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
@@ -69,6 +69,7 @@
 using feedback::FeedbackData;
 using testing::_;
 
+constexpr char kFakeAutofillMetadata[] = "FakeAutofillMetadata";
 constexpr char kExtraDiagnosticsKey[] = "EXTRA_DIAGNOSTICS";
 constexpr char kFakeExtraDiagnosticsValue[] =
     "Failed to connect to wifi network.";
@@ -233,6 +234,8 @@
           EXPECT_EQ(expected_params.send_histograms, params.send_histograms);
           EXPECT_EQ(expected_params.send_bluetooth_logs,
                     params.send_bluetooth_logs);
+          EXPECT_EQ(expected_params.send_autofill_metadata,
+                    params.send_autofill_metadata);
 
           std::move(callback).Run(true);
         });
@@ -373,13 +376,15 @@
                                        /*load_system_info=*/true,
                                        /*send_tab_titles=*/true,
                                        /*send_histograms=*/true,
-                                       /*send_bluetooth_logs=*/true};
+                                       /*send_bluetooth_logs=*/true,
+                                       /*send_autofill_metadata=*/false};
 
   scoped_refptr<FeedbackData> feedback_data;
   RunSendReport(std::move(report), expected_params, feedback_data);
 
   EXPECT_EQ("", feedback_data->user_email());
   EXPECT_EQ("", feedback_data->page_url());
+  EXPECT_EQ("", feedback_data->autofill_metadata());
   EXPECT_EQ(base::UTF16ToUTF8(kDescription), feedback_data->description());
   // Verify screenshot is added to feedback data.
   EXPECT_GT(feedback_data->image().size(), 0u);
@@ -432,13 +437,15 @@
                                        /*load_system_info=*/true,
                                        /*send_tab_titles=*/true,
                                        /*send_histograms=*/true,
-                                       /*send_bluetooth_logs=*/false};
+                                       /*send_bluetooth_logs=*/false,
+                                       /*send_autofill_metadata=*/false};
 
   scoped_refptr<FeedbackData> feedback_data;
   RunSendReport(std::move(report), expected_params, feedback_data);
 
   EXPECT_EQ("", feedback_data->user_email());
   EXPECT_EQ("", feedback_data->page_url());
+  EXPECT_EQ("", feedback_data->autofill_metadata());
   EXPECT_EQ(base::UTF16ToUTF8(kDescription), feedback_data->description());
   // Verify screenshot is added to feedback data.
   EXPECT_GT(feedback_data->image().size(), 0u);
@@ -463,6 +470,71 @@
 // passed to SendFeedback method of the feedback service.
 // - System logs and histograms are not included.
 // - Screenshot is not included.
+// - Consent granted.
+// - Non-empty extra_diagnostics provided.
+// - sentBluetoothLog flag is set false.
+// - category_tag is set to a fake value.
+// - User is logged in with internal google account.
+// - Performance trace id is present.
+// - from_assistant flag is set false.
+// - Assistant debug info is not allowed.
+// - from_autofill flag is set true.
+// - Non-empty autofill_metadata provided.
+IN_PROC_BROWSER_TEST_F(ChromeOsFeedbackDelegateTest,
+                       FeedbackDataPopulatedIncludeAutofillMetadata) {
+  ReportPtr report = Report::New();
+  report->feedback_context = FeedbackContext::New();
+  report->description = kDescription;
+  report->include_screenshot = false;
+  report->contact_user_consent_granted = true;
+  report->feedback_context->extra_diagnostics = kFakeExtraDiagnosticsValue;
+  report->send_bluetooth_logs = false;
+  report->feedback_context->category_tag = kFakeCategoryTag;
+  report->include_system_logs_and_histograms = false;
+  report->include_autofill_metadata = true;
+  report->feedback_context->is_internal_account = true;
+  report->feedback_context->trace_id = kPerformanceTraceId;
+  report->feedback_context->from_assistant = false;
+  report->feedback_context->from_autofill = true;
+  report->feedback_context->autofill_metadata = kFakeAutofillMetadata;
+  report->feedback_context->assistant_debug_info_allowed = false;
+  const FeedbackParams expected_params{/*is_internal_email=*/true,
+                                       /*load_system_info=*/false,
+                                       /*send_tab_titles=*/false,
+                                       /*send_histograms=*/false,
+                                       /*send_bluetooth_logs=*/false,
+                                       /*send_autofill_metadata=*/true};
+
+  scoped_refptr<FeedbackData> feedback_data;
+  RunSendReport(std::move(report), expected_params, feedback_data);
+
+  EXPECT_EQ("", feedback_data->user_email());
+  EXPECT_EQ("", feedback_data->page_url());
+  EXPECT_EQ(kFakeAutofillMetadata, feedback_data->autofill_metadata());
+  EXPECT_EQ(base::UTF16ToUTF8(kDescription), feedback_data->description());
+  // Verify no screenshot is added to feedback data.
+  EXPECT_EQ(feedback_data->image().size(), 0u);
+  // Verify consent data appended to sys_info map.
+  auto consent_granted =
+      feedback_data->sys_info()->find(kFeedbackUserConsentKey);
+  EXPECT_NE(feedback_data->sys_info()->end(), consent_granted);
+  EXPECT_EQ(kFeedbackUserConsentKey, consent_granted->first);
+  EXPECT_EQ(kFeedbackUserConsentGrantedValue, consent_granted->second);
+  auto extra_diagnostics =
+      feedback_data->sys_info()->find(kExtraDiagnosticsKey);
+  EXPECT_EQ(kExtraDiagnosticsKey, extra_diagnostics->first);
+  EXPECT_EQ(kFakeExtraDiagnosticsValue, extra_diagnostics->second);
+  // Verify category_tag is marked as a fake category tag in the report.
+  EXPECT_EQ(kFakeCategoryTag, feedback_data->category_tag());
+  EXPECT_EQ(kPerformanceTraceId, feedback_data->trace_id());
+  EXPECT_FALSE(feedback_data->from_assistant());
+  EXPECT_FALSE(feedback_data->assistant_debug_info_allowed());
+}
+
+// Test that feedback params and data are populated with correct data before
+// passed to SendFeedback method of the feedback service.
+// - System logs and histograms are not included.
+// - Screenshot is not included.
 // - Consent not granted.
 // - sentBluetoothLogs flag is set false.
 // - category_tag is not set to "BluetoothReportWithLogs".
@@ -491,13 +563,15 @@
                                        /*load_system_info=*/false,
                                        /*send_tab_titles=*/false,
                                        /*send_histograms=*/false,
-                                       /*send_bluetooth_logs=*/false};
+                                       /*send_bluetooth_logs=*/false,
+                                       /*send_autofill_metadata=*/false};
 
   scoped_refptr<FeedbackData> feedback_data;
   RunSendReport(std::move(report), expected_params, feedback_data);
 
   EXPECT_EQ(kSignedInUserEmail, feedback_data->user_email());
   EXPECT_EQ(kPageUrl, feedback_data->page_url());
+  EXPECT_EQ("", feedback_data->autofill_metadata());
   EXPECT_EQ(base::UTF16ToUTF8(kDescription), feedback_data->description());
   // Verify no screenshot is added to feedback data.
   EXPECT_EQ("", feedback_data->image());
@@ -642,7 +716,8 @@
                                        /*load_system_info=*/false,
                                        /*send_tab_titles=*/false,
                                        /*send_histograms=*/true,
-                                       /*send_bluetooth_logs=*/false};
+                                       /*send_bluetooth_logs=*/false,
+                                       /*send_autofill_metadata=*/false};
 
   scoped_refptr<FeedbackData> feedback_data;
   RunSendReport(std::move(report), expected_params, feedback_data,
@@ -674,7 +749,8 @@
                                        /*load_system_info=*/false,
                                        /*send_tab_titles=*/false,
                                        /*send_histograms=*/false,
-                                       /*send_bluetooth_logs=*/false};
+                                       /*send_bluetooth_logs=*/false,
+                                       /*send_autofill_metadata=*/false};
 
   scoped_refptr<FeedbackData> feedback_data;
   RunSendReport(std::move(report), expected_params, feedback_data,
@@ -701,7 +777,8 @@
                                        /*load_system_info=*/true,
                                        /*send_tab_titles=*/false,
                                        /*send_histograms=*/true,
-                                       /*send_bluetooth_logs=*/false};
+                                       /*send_bluetooth_logs=*/false,
+                                       /*send_autofill_metadata=*/false};
 
   scoped_refptr<FeedbackData> feedback_data;
   // Set preload to false to simulate preloading did not complete before sending
diff --git a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
index 9573d317..9606fef 100644
--- a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
+++ b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
@@ -66,9 +66,7 @@
   std::string user_key_bits = user_policy.GetPublicSigningKeyAsString();
   ASSERT_FALSE(user_key_bits.empty());
   ASSERT_TRUE(base::CreateDirectory(user_key_file.DirName()));
-  ASSERT_EQ(base::WriteFile(user_key_file, user_key_bits.data(),
-                            user_key_bits.length()),
-            base::checked_cast<int>(user_key_bits.length()));
+  ASSERT_TRUE(base::WriteFile(user_key_file, user_key_bits));
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/policy/core/device_policy_cros_test_helper.cc b/chrome/browser/ash/policy/core/device_policy_cros_test_helper.cc
index 93461b6..61451ad 100644
--- a/chrome/browser/ash/policy/core/device_policy_cros_test_helper.cc
+++ b/chrome/browser/ash/policy/core/device_policy_cros_test_helper.cc
@@ -62,9 +62,7 @@
                                      &owner_key_file));
   std::string owner_key_bits = device_policy()->GetPublicSigningKeyAsString();
   ASSERT_FALSE(owner_key_bits.empty());
-  ASSERT_EQ(base::checked_cast<int>(owner_key_bits.length()),
-            base::WriteFile(owner_key_file, owner_key_bits.data(),
-                            owner_key_bits.length()));
+  ASSERT_TRUE(base::WriteFile(owner_key_file, owner_key_bits));
 }
 
 // static
diff --git a/chrome/browser/ash/policy/handlers/extension_cache_unittest.cc b/chrome/browser/ash/policy/handlers/extension_cache_unittest.cc
index 0813aef..146976f 100644
--- a/chrome/browser/ash/policy/handlers/extension_cache_unittest.cc
+++ b/chrome/browser/ash/policy/handlers/extension_cache_unittest.cc
@@ -38,8 +38,7 @@
                        size_t size,
                        const base::Time& timestamp) {
   const std::string data(size, 0);
-  EXPECT_EQ(base::WriteFile(file, data.data(), data.size()),
-            static_cast<int>(size));
+  EXPECT_TRUE(base::WriteFile(file, data));
   EXPECT_TRUE(base::TouchFile(file, timestamp, timestamp));
 }
 
diff --git a/chrome/browser/ash/policy/uploading/system_log_uploader.cc b/chrome/browser/ash/policy/uploading/system_log_uploader.cc
index 671109f..f7bd9af 100644
--- a/chrome/browser/ash/policy/uploading/system_log_uploader.cc
+++ b/chrome/browser/ash/policy/uploading/system_log_uploader.cc
@@ -86,8 +86,7 @@
   for (const auto& syslog_entry : *system_logs) {
     base::FilePath file_name = base::FilePath(syslog_entry.first).BaseName();
     base::FilePath file_path(temp_dir.GetPath().Append(file_name));
-    if (!base::WriteFile(file_path, syslog_entry.second.c_str(),
-                         syslog_entry.second.size())) {
+    if (!base::WriteFile(file_path, syslog_entry.second)) {
       PLOG(ERROR) << "Can't write log file: " << file_path.value();
       continue;
     }
diff --git a/chrome/browser/ash/power/auto_screen_brightness/als_file_reader_unittest.cc b/chrome/browser/ash/power/auto_screen_brightness/als_file_reader_unittest.cc
index 146ab253..ebc4b6d 100644
--- a/chrome/browser/ash/power/auto_screen_brightness/als_file_reader_unittest.cc
+++ b/chrome/browser/ash/power/auto_screen_brightness/als_file_reader_unittest.cc
@@ -40,11 +40,7 @@
  protected:
   void WriteLux(int lux) {
     const std::string lux_string = base::NumberToString(lux);
-    const int bytes_written = base::WriteFile(
-        ambient_light_path_, lux_string.data(), lux_string.size());
-    ASSERT_EQ(bytes_written, static_cast<int>(lux_string.size()))
-        << "Wrote " << bytes_written << " byte(s) instead of "
-        << lux_string.size() << " to " << ambient_light_path_.value();
+    ASSERT_TRUE(base::WriteFile(ambient_light_path_, lux_string));
   }
 
   base::ScopedTempDir temp_dir_;
diff --git a/chrome/browser/ash/power/auto_screen_brightness/model_config_loader_impl_unittest.cc b/chrome/browser/ash/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
index 832b291b..051f90e 100644
--- a/chrome/browser/ash/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
+++ b/chrome/browser/ash/power/auto_screen_brightness/model_config_loader_impl_unittest.cc
@@ -93,12 +93,7 @@
       return;
 
     CHECK(!temp_params_path_.empty());
-
-    const int bytes_written =
-        base::WriteFile(temp_params_path_, params.data(), params.size());
-    ASSERT_EQ(bytes_written, static_cast<int>(params.size()))
-        << "Wrote " << bytes_written << " byte(s) instead of " << params.size()
-        << " to " << temp_params_path_;
+    ASSERT_TRUE(base::WriteFile(temp_params_path_, params));
   }
 
   content::BrowserTaskEnvironment task_environment_;
diff --git a/chrome/browser/ash/power/auto_screen_brightness/modeller_impl.cc b/chrome/browser/ash/power/auto_screen_brightness/modeller_impl.cc
index e372b4b..1e9ea1b 100644
--- a/chrome/browser/ash/power/auto_screen_brightness/modeller_impl.cc
+++ b/chrome/browser/ash/power/auto_screen_brightness/modeller_impl.cc
@@ -112,10 +112,8 @@
 // Saves |data| to |path|. Returns whether successful and logs error if an
 // error occurs.
 bool SaveDataAndLogError(const base::FilePath& path, const std::string& data) {
-  const int bytes_written = base::WriteFile(path, data.data(), data.size());
-  if (bytes_written != static_cast<int>(data.size())) {
-    LOG(ERROR) << "Wrote " << bytes_written << " byte(s) instead of "
-               << data.size() << " to " << path.value();
+  if (!base::WriteFile(path, data)) {
+    LOG(ERROR) << "Writing to " << path.value() << " failed.";
     return false;
   }
   return true;
diff --git a/chrome/browser/ash/power/cpu_data_collector_unittest.cc b/chrome/browser/ash/power/cpu_data_collector_unittest.cc
index c43a516..5b52f61 100644
--- a/chrome/browser/ash/power/cpu_data_collector_unittest.cc
+++ b/chrome/browser/ash/power/cpu_data_collector_unittest.cc
@@ -108,11 +108,9 @@
   ASSERT_TRUE(base::CreateTemporaryFile(&time_in_state_path_cpu0_));
   ASSERT_TRUE(base::CreateTemporaryFile(&time_in_state_path_cpu1_));
   ASSERT_TRUE(
-      base::WriteFile(time_in_state_path_cpu0_, kTimeInStateContentCpu0,
-                      static_cast<int>(strlen(kTimeInStateContentCpu0))) != -1);
+      base::WriteFile(time_in_state_path_cpu0_, kTimeInStateContentCpu0));
   ASSERT_TRUE(
-      base::WriteFile(time_in_state_path_cpu1_, kTimeInStateContentCpu1,
-                      static_cast<int>(strlen(kTimeInStateContentCpu1))) != -1);
+      base::WriteFile(time_in_state_path_cpu1_, kTimeInStateContentCpu1));
 
   std::vector<std::string> cpu_freq_state_names;
   CpuDataCollector::StateOccupancySample freq_sample_cpu0;
@@ -132,9 +130,7 @@
 
 TEST_F(CpuDataCollectorTest, ReadCpuFreqAllTimeInState) {
   ASSERT_TRUE(base::CreateTemporaryFile(&all_time_in_state_path_));
-  ASSERT_TRUE(
-      base::WriteFile(all_time_in_state_path_, kAllTimeInStateContent,
-                      static_cast<int>(strlen(kAllTimeInStateContent))) != -1);
+  ASSERT_TRUE(base::WriteFile(all_time_in_state_path_, kAllTimeInStateContent));
 
   std::vector<std::string> cpu_freq_state_names;
   std::vector<CpuDataCollector::StateOccupancySample> freq_samples;
@@ -155,9 +151,8 @@
 
 TEST_F(CpuDataCollectorTest, ReadCpuFreqAllTimeInStateNA) {
   ASSERT_TRUE(base::CreateTemporaryFile(&all_time_in_state_path_));
-  ASSERT_TRUE(base::WriteFile(
-                  all_time_in_state_path_, kAllTimeInStateContentNA,
-                  static_cast<int>(strlen(kAllTimeInStateContentNA))) != -1);
+  ASSERT_TRUE(
+      base::WriteFile(all_time_in_state_path_, kAllTimeInStateContentNA));
 
   std::vector<std::string> cpu_freq_state_names;
   std::vector<CpuDataCollector::StateOccupancySample> freq_samples;
@@ -182,9 +177,8 @@
 
 TEST_F(CpuDataCollectorTest, ReadCpuFreqAllTimeInStateOff) {
   ASSERT_TRUE(base::CreateTemporaryFile(&all_time_in_state_path_));
-  ASSERT_TRUE(base::WriteFile(
-                  all_time_in_state_path_, kAllTimeInStateContentOff,
-                  static_cast<int>(strlen(kAllTimeInStateContentOff))) != -1);
+  ASSERT_TRUE(
+      base::WriteFile(all_time_in_state_path_, kAllTimeInStateContentOff));
 
   std::vector<std::string> cpu_freq_state_names;
   std::vector<CpuDataCollector::StateOccupancySample> freq_samples;
diff --git a/chrome/browser/ash/power/freezer_cgroup_process_manager.cc b/chrome/browser/ash/power/freezer_cgroup_process_manager.cc
index 789a581..a1dce96 100644
--- a/chrome/browser/ash/power/freezer_cgroup_process_manager.cc
+++ b/chrome/browser/ash/power/freezer_cgroup_process_manager.cc
@@ -126,15 +126,10 @@
  private:
   bool WriteCommandToFile(const std::string& command,
                           const base::FilePath& file) {
-    int bytes = base::WriteFile(file, command.c_str(), command.size());
-    if (bytes == -1) {
+    if (!base::WriteFile(file, command)) {
       PLOG(ERROR) << "Writing " << command << " to " << file.value()
                   << " failed";
       return false;
-    } else if (bytes != static_cast<int>(command.size())) {
-      LOG(ERROR) << "Only wrote " << bytes << " byte(s) when writing "
-                 << command << " to " << file.value();
-      return false;
     }
     return true;
   }
diff --git a/chrome/browser/ash/power/process_data_collector_unittest.cc b/chrome/browser/ash/power/process_data_collector_unittest.cc
index 2fcba20..0f16e47de 100644
--- a/chrome/browser/ash/power/process_data_collector_unittest.cc
+++ b/chrome/browser/ash/power/process_data_collector_unittest.cc
@@ -111,8 +111,7 @@
 // Writes |data| to |file_path| and checks that the write succeeded.
 void WriteStringToFile(const base::FilePath& file_path,
                        const std::string& data) {
-  ASSERT_EQ(base::WriteFile(file_path, data.c_str(), data.length()),
-            static_cast<int>(data.length()));
+  ASSERT_TRUE(base::WriteFile(file_path, data));
 }
 
 // Sets up a procfs-like directory with certain processes and stat files
diff --git a/chrome/browser/ash/psi_memory_metrics_unittest.cc b/chrome/browser/ash/psi_memory_metrics_unittest.cc
index cc918180..139d014 100644
--- a/chrome/browser/ash/psi_memory_metrics_unittest.cc
+++ b/chrome/browser/ash/psi_memory_metrics_unittest.cc
@@ -117,10 +117,7 @@
 // Tests timer cancellation.
 TEST_F(MemoryMetricsTest, CancelBeforeFirstRun) {
   Init(300);
-  int bytes_written = base::WriteFile(GetTestFileName(), kFileContents1,
-                                      sizeof(kFileContents1) - 1);
-
-  EXPECT_EQ(static_cast<int>(sizeof(kFileContents1) - 1), bytes_written);
+  EXPECT_TRUE(base::WriteFile(GetTestFileName(), kFileContents1));
 
   //  Repeating timer comes on - but we will cancel before first iteration.
   Cit()->Start();
@@ -147,10 +144,7 @@
 // Tests basic collection of PSI metrics with period=60.
 TEST_F(MemoryMetricsTest, SunnyDay2) {
   Init(60);
-  int bytes_written = base::WriteFile(GetTestFileName(), kFileContents1,
-                                      sizeof(kFileContents1) - 1);
-
-  EXPECT_EQ(static_cast<int>(sizeof(kFileContents1) - 1), bytes_written);
+  EXPECT_TRUE(base::WriteFile(GetTestFileName(), kFileContents1));
 
   Cit()->CollectEvents();
 
@@ -163,10 +157,7 @@
 // Tests basic collection of PSI metrics with period=300.
 TEST_F(MemoryMetricsTest, SunnyDay3) {
   Init(300);
-  int bytes_written = base::WriteFile(GetTestFileName(), kFileContents1,
-                                      sizeof(kFileContents1) - 1);
-
-  EXPECT_EQ(static_cast<int>(sizeof(kFileContents1) - 1), bytes_written);
+  EXPECT_TRUE(base::WriteFile(GetTestFileName(), kFileContents1));
 
   Cit()->CollectEvents();
 
diff --git a/chrome/browser/ash/startup_settings_cache.cc b/chrome/browser/ash/startup_settings_cache.cc
index 018f8a91..b6cb4cf 100644
--- a/chrome/browser/ash/startup_settings_cache.cc
+++ b/chrome/browser/ash/startup_settings_cache.cc
@@ -68,7 +68,7 @@
     return;
 
   // Ignore errors because we're shutting down and we can't recover.
-  base::WriteFile(cache_file, output.data(), static_cast<int>(output.size()));
+  base::WriteFile(cache_file, output);
 }
 
 }  // namespace startup_settings_cache
diff --git a/chrome/browser/ash/system_logs/system_logs_writer.cc b/chrome/browser/ash/system_logs/system_logs_writer.cc
index 6164cb75..8a9075c 100644
--- a/chrome/browser/ash/system_logs/system_logs_writer.cc
+++ b/chrome/browser/ash/system_logs/system_logs_writer.cc
@@ -32,7 +32,7 @@
   }
   base::FilePath temp_file_path(
       temp_dir.GetPath().Append(dest_file_path.BaseName()));
-  if (!base::WriteFile(temp_file_path, contents.c_str(), contents.size())) {
+  if (!base::WriteFile(temp_file_path, contents)) {
     LOG(ERROR) << "Unable to write file: " << temp_file_path.value();
     return absl::nullopt;
   }
diff --git a/chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl_browsertest.cc b/chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl_browsertest.cc
index 16ad4dbb..fe27cb5 100644
--- a/chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl_browsertest.cc
+++ b/chrome/browser/ash/wallpaper/wallpaper_drivefs_delegate_impl_browsertest.cc
@@ -72,9 +72,8 @@
     ASSERT_TRUE(base::CreateDirectory(target.DirName()));
   }
   auto data = EncodeImage(CreateTestImage());
-  size_t size =
-      base::WriteFile(target, data->front_as<const char>(), data->size());
-  ASSERT_EQ(size, data->size());
+  ASSERT_TRUE(
+      base::WriteFile(target, base::make_span(data->front(), data->size())));
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index cf661d6..e3c6510 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -295,6 +295,8 @@
 
   if (is_chromeos_ash) {
     sources += [
+      "app_mode/kiosk_troubleshooting_controller_ash.cc",
+      "app_mode/kiosk_troubleshooting_controller_ash.h",
       "extensions/login_screen/login/cleanup/cleanup_manager_ash.cc",
       "extensions/login_screen/login/cleanup/cleanup_manager_ash.h",
       "extensions/login_screen/login/cleanup/clipboard_cleanup_handler.cc",
diff --git a/chrome/browser/chromeos/app_mode/DEPS b/chrome/browser/chromeos/app_mode/DEPS
index ce718c8..36112d3 100644
--- a/chrome/browser/chromeos/app_mode/DEPS
+++ b/chrome/browser/chromeos/app_mode/DEPS
@@ -6,4 +6,17 @@
     "+ash/test/ash_test_helper.h",
     "+chrome/browser/ash/app_mode/test_kiosk_extension_builder.h",
   ],
-}
+  "kiosk_troubleshooting_controller_ash.cc": [
+    "+ash/accelerators/accelerator_controller_impl.h",
+    "+ash/public/cpp/new_window_delegate.h",
+    "+ash/shell.h"
+  ],
+  "app_session_unittest.cc": [
+    "+ash/accelerators/accelerator_controller_impl.h",
+    "+ash/public/cpp/session/session_types.h",
+    "+ash/public/cpp/test/test_new_window_delegate.h",
+    "+ash/session/session_controller_impl.h",
+    "+ash/shell.h",
+    "+ash/test/ash_test_helper.h",
+  ],
+}
\ No newline at end of file
diff --git a/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.cc b/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.cc
index e743d7a..b9b4687 100644
--- a/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.cc
+++ b/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.cc
@@ -16,6 +16,10 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_window.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "kiosk_troubleshooting_controller_ash.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 namespace chromeos {
 
 const char kKioskNewBrowserWindowHistogram[] = "Kiosk.NewBrowserWindow";
@@ -31,11 +35,21 @@
       on_browser_window_added_callback_(on_browser_window_added_callback),
       shutdown_app_session_callback_(std::move(shutdown_app_session_callback)),
       app_session_policies_(profile_->GetPrefs()) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  kiosk_troubleshooting_controller_ =
+      std::make_unique<ash::KioskTroubleshootingControllerAsh>(
+          profile_->GetPrefs(),
+          base::BindOnce(&AppSessionBrowserWindowHandler::ShutdownAppSession,
+                         weak_ptr_factory_.GetWeakPtr()));
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
   kiosk_troubleshooting_controller_ =
       std::make_unique<KioskTroubleshootingController>(
           profile_->GetPrefs(),
           base::BindOnce(&AppSessionBrowserWindowHandler::ShutdownAppSession,
                          weak_ptr_factory_.GetWeakPtr()));
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
   BrowserList::AddObserver(this);
 }
 
@@ -57,6 +71,17 @@
     return;
   }
 
+  if (IsNewBrowserWindowAllowed(browser)) {
+    base::UmaHistogramEnumeration(
+        kKioskNewBrowserWindowHistogram,
+        KioskBrowserWindowType::kOpenedRegularBrowser);
+    LOG(WARNING) << "Open additional fullscreen browser window in kiosk session"
+                 << ", url=" << url_string;
+    chrome::ToggleFullscreenMode(browser);
+    on_browser_window_added_callback_.Run(/*is_closing=*/false);
+    return;
+  }
+
   if (IsDevToolsAllowedBrowser(browser)) {
     base::UmaHistogramEnumeration(
         kKioskNewBrowserWindowHistogram,
@@ -65,13 +90,10 @@
     return;
   }
 
-  if (IsNewBrowserWindowAllowed(browser)) {
+  if (IsNormalTroubleshootingBrowserAllowed(browser)) {
     base::UmaHistogramEnumeration(
         kKioskNewBrowserWindowHistogram,
-        KioskBrowserWindowType::kOpenedRegularBrowser);
-    LOG(WARNING) << "Open additional fullscreen browser window in kiosk session"
-                 << ", url=" << url_string;
-    chrome::ToggleFullscreenMode(browser);
+        KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser);
     on_browser_window_added_callback_.Run(/*is_closing=*/false);
     return;
   }
@@ -165,6 +187,13 @@
              ->AreKioskTroubleshootingToolsEnabled();
 }
 
+bool AppSessionBrowserWindowHandler::IsNormalTroubleshootingBrowserAllowed(
+    Browser* browser) const {
+  return browser->is_type_normal() &&
+         kiosk_troubleshooting_controller_
+             ->AreKioskTroubleshootingToolsEnabled();
+}
+
 bool AppSessionBrowserWindowHandler::ShouldExitKioskWhenLastBrowserRemoved()
     const {
   return web_app_name_.has_value();
diff --git a/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.h b/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.h
index beee1c3..bf00b6d 100644
--- a/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.h
+++ b/chrome/browser/chromeos/app_mode/app_session_browser_window_handler.h
@@ -27,7 +27,8 @@
   kClosedRegularBrowser = 1,
   kOpenedRegularBrowser = 2,
   kOpenedDevToolsBrowser = 3,
-  kMaxValue = kOpenedDevToolsBrowser,
+  kOpenedTroubleshootingNormalBrowser = 4,
+  kMaxValue = kOpenedTroubleshootingNormalBrowser,
 };
 
 // This class monitors for the addition and removal of new browser windows
@@ -71,6 +72,10 @@
   // Returns true if open devtools browser and it is allowed by policy.
   bool IsDevToolsAllowedBrowser(Browser* browser) const;
 
+  // Returns true if open normal browser and it is allowed by troubleshooting
+  // policy.
+  bool IsNormalTroubleshootingBrowserAllowed(Browser* browser) const;
+
   // Returns true in case of the initial browser window existed for web kiosks.
   bool ShouldExitKioskWhenLastBrowserRemoved() const;
 
diff --git a/chrome/browser/chromeos/app_mode/app_session_unittest.cc b/chrome/browser/chromeos/app_mode/app_session_unittest.cc
index 249e13a..d62703e 100644
--- a/chrome/browser/chromeos/app_mode/app_session_unittest.cc
+++ b/chrome/browser/chromeos/app_mode/app_session_unittest.cc
@@ -33,10 +33,21 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/accelerators/accelerator_controller_impl.h"
+#include "ash/public/cpp/session/session_types.h"
+#include "ash/public/cpp/test/test_new_window_delegate.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_helper.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 #if BUILDFLAG(ENABLE_PLUGINS)
 #include "chrome/browser/chromeos/app_mode/kiosk_session_plugin_handler_delegate.h"
 #include "content/public/browser/plugin_service.h"
-#endif
+#endif  // BUILDFLAG(ENABLE_PLUGINS)
 
 namespace chromeos {
 
@@ -158,6 +169,8 @@
 
 }  // namespace
 
+// TODO(b/271336749): split app_session_unittest.cc file into smaller test
+// files.
 template <typename AppSessionParamType = KioskSessionRestartTestCase>
 class AppSessionBaseTest
     : public ::testing::TestWithParam<AppSessionParamType> {
@@ -176,7 +189,13 @@
     chromeos::PowerManagerClient::InitializeFake();
   }
 
-  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    ash_test_helper_.SetUp(ash::AshTestHelper::InitParams());
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  }
 
   static void TearDownTestSuite() { chromeos::PowerManagerClient::Shutdown(); }
 
@@ -286,6 +305,9 @@
   content::BrowserTaskEnvironment task_environment_;
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<ScopedTestingLocalState> local_state_;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ash::AshTestHelper ash_test_helper_;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   // Must outlive |app_session_|.
   TestingProfile profile_;
@@ -472,7 +494,7 @@
     Browser::Type::TYPE_DEVTOOLS,
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     Browser::Type::TYPE_CUSTOM_TAB,
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     Browser::Type::TYPE_PICTURE_IN_PICTURE,
   };
 
@@ -733,38 +755,50 @@
     StartChromeAppKioskSession();
   }
 
+  void UpdateTroubleshootingToolsPolicy(bool enable) {
+    GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, enable);
+  }
+
   std::unique_ptr<Browser> CreateDevToolsBrowserWithTestWindow() {
     auto params = Browser::CreateParams::CreateForDevTools(profile());
-    params.window = &test_window_;
+
+    TestBrowserWindow* test_window_ = new TestBrowserWindow;
+    new TestBrowserWindowOwner(test_window_);
+    params.window = test_window_;
+
     return base::WrapUnique<Browser>(Browser::Create(params));
   }
 
- private:
-  TestBrowserWindow test_window_;
+  std::unique_ptr<Browser> CreateRegularBrowserWithTestWindow() {
+    return CreateBrowserWithTestWindowAndType(Browser::TYPE_NORMAL);
+  }
+
+  std::unique_ptr<Browser> CreateBrowserWithTestWindowAndType(
+      Browser::Type type) {
+    Browser::CreateParams params(profile(), /*user_gesture=*/true);
+    params.type = type;
+    return CreateBrowserWithTestWindowForParams(params);
+  }
 };
 
 TEST_P(AppSessionTroubleshootingTest, EnableTroubleshootingToolsDuringSession) {
   SetUpKioskSession();
-
-  GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, true);
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
 
   // Kiosk session should shoutdown only if policy is changed from enable to
   // disable.
   EXPECT_FALSE(IsSessionShuttingDown());
 
-  GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, false);
-
+  UpdateTroubleshootingToolsPolicy(/*enable=*/false);
   EXPECT_TRUE(IsSessionShuttingDown());
 }
 
 TEST_P(AppSessionTroubleshootingTest,
        EnableTroubleshootingToolsBeforeSessionStarted) {
-  GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, true);
-
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
   SetUpKioskSession();
 
-  GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, false);
-
+  UpdateTroubleshootingToolsPolicy(/*enable=*/false);
   EXPECT_TRUE(IsSessionShuttingDown());
 }
 
@@ -785,8 +819,7 @@
 
 TEST_P(AppSessionTroubleshootingTest, OpenDevToolsEnabledTroubleshootingTools) {
   SetUpKioskSession();
-
-  GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, true);
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
 
   EXPECT_FALSE(ShouldBrowserBeClosedByAppSessionBrowserHander(
       CreateDevToolsBrowserWithTestWindow()->window()));
@@ -797,25 +830,30 @@
   histogram()->ExpectTotalCount(kKioskNewBrowserWindowHistogram, 1);
 }
 
-TEST_P(AppSessionTroubleshootingTest,
-       CloseDevToolsDisabledTroubleshootingTools) {
+TEST_P(AppSessionTroubleshootingTest, CloseTroubleshootingToolsByDefault) {
   SetUpKioskSession();
 
   // Kiosk troubleshooting tools are disabled by default.
   EXPECT_TRUE(ShouldBrowserBeClosedByAppSessionBrowserHander(
       CreateDevToolsBrowserWithTestWindow()->window()));
-
   histogram()->ExpectBucketCount(kKioskNewBrowserWindowHistogram,
                                  KioskBrowserWindowType::kClosedRegularBrowser,
                                  1);
   histogram()->ExpectTotalCount(kKioskNewBrowserWindowHistogram, 1);
+
+  EXPECT_TRUE(ShouldBrowserBeClosedByAppSessionBrowserHander(
+      CreateRegularBrowserWithTestWindow()->window()));
+
+  histogram()->ExpectBucketCount(kKioskNewBrowserWindowHistogram,
+                                 KioskBrowserWindowType::kClosedRegularBrowser,
+                                 2);
+  histogram()->ExpectTotalCount(kKioskNewBrowserWindowHistogram, 2);
 }
 
 TEST_P(AppSessionTroubleshootingTest,
        OpenDevToolsDisableTroubleshootingToolsDuringSession) {
   SetUpKioskSession();
-
-  GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, true);
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
 
   // Kiosk session should shoutdown only if policy is changed from enable to
   // disable.
@@ -828,15 +866,173 @@
                                  1);
   histogram()->ExpectTotalCount(kKioskNewBrowserWindowHistogram, 1);
 
-  GetPrefs()->SetBoolean(prefs::kKioskTroubleshootingToolsEnabled, false);
-
+  UpdateTroubleshootingToolsPolicy(/*enable=*/false);
   EXPECT_TRUE(IsSessionShuttingDown());
 }
 
+TEST_P(AppSessionTroubleshootingTest,
+       OpenNewWindowEnabledTroubleshootingTools) {
+  SetUpKioskSession();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
+
+  std::unique_ptr<Browser> normal_browser =
+      CreateRegularBrowserWithTestWindow();
+  EXPECT_FALSE(
+      ShouldBrowserBeClosedByAppSessionBrowserHander(normal_browser->window()));
+
+  histogram()->ExpectBucketCount(
+      kKioskNewBrowserWindowHistogram,
+      KioskBrowserWindowType::kOpenedTroubleshootingNormalBrowser, 1);
+  histogram()->ExpectTotalCount(kKioskNewBrowserWindowHistogram, 1);
+}
+
+TEST_P(AppSessionTroubleshootingTest,
+       CloseNewWindowDisabledTroubleshootingTools) {
+  UpdateTroubleshootingToolsPolicy(/*enable=*/false);
+  SetUpKioskSession();
+
+  EXPECT_TRUE(ShouldBrowserBeClosedByAppSessionBrowserHander(
+      CreateRegularBrowserWithTestWindow()->window()));
+
+  histogram()->ExpectBucketCount(kKioskNewBrowserWindowHistogram,
+                                 KioskBrowserWindowType::kClosedRegularBrowser,
+                                 1);
+  histogram()->ExpectTotalCount(kKioskNewBrowserWindowHistogram, 1);
+}
+
+TEST_P(AppSessionTroubleshootingTest,
+       OnlyAllowRegularBrowserAndDevToolsAsTroubleshootingBrowsers) {
+  const std::vector<Browser::Type> should_be_closed_browser_types = {
+    Browser::Type::TYPE_POPUP,
+    Browser::Type::TYPE_APP,
+    Browser::Type::TYPE_APP_POPUP,
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    Browser::Type::TYPE_CUSTOM_TAB,
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+    Browser::Type::TYPE_PICTURE_IN_PICTURE,
+  };
+  SetUpKioskSession();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
+
+  for (Browser::Type type : should_be_closed_browser_types) {
+    EXPECT_TRUE(ShouldBrowserBeClosedByAppSessionBrowserHander(
+        CreateBrowserWithTestWindowAndType(type)->window()));
+  }
+
+  histogram()->ExpectBucketCount(kKioskNewBrowserWindowHistogram,
+                                 KioskBrowserWindowType::kClosedRegularBrowser,
+                                 should_be_closed_browser_types.size());
+  histogram()->ExpectTotalCount(kKioskNewBrowserWindowHistogram,
+                                should_be_closed_browser_types.size());
+}
+
 INSTANTIATE_TEST_SUITE_P(AppSessionTroubleshootingTools,
                          AppSessionTroubleshootingTest,
                          ::testing::Bool());
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+class FakeNewWindowDelegate : public ash::TestNewWindowDelegate {
+ public:
+  FakeNewWindowDelegate() = default;
+  ~FakeNewWindowDelegate() override = default;
+
+  void NewWindow(bool incognito, bool should_trigger_session_restore) override {
+    new_window_called_ = true;
+  }
+
+  void NewTab() override { new_tab_called_ = true; }
+
+  bool is_new_window_called() const { return new_window_called_; }
+  bool is_new_tab_called() const { return new_tab_called_; }
+
+ private:
+  bool new_window_called_ = false;
+  bool new_tab_called_ = false;
+};
+
+// Tests actions after pressing troubleshooting shortcuts. Runs all tests for
+// web and chrome app kiosks.
+class AppSessionTroubleshootingShortcutsTest
+    : public AppSessionTroubleshootingTest {
+ public:
+  static bool ProcessInController(const ui::Accelerator& accelerator) {
+    return ash::Shell::Get()->accelerator_controller()->Process(accelerator);
+  }
+
+  void SetUp() override {
+    auto new_window_delegate = std::make_unique<FakeNewWindowDelegate>();
+    fake_new_window_delegate_ = new_window_delegate.get();
+    test_new_window_delegate_provider_ =
+        std::make_unique<ash::TestNewWindowDelegateProvider>(
+            std::move(new_window_delegate));
+
+    AppSessionTroubleshootingTest::SetUp();
+
+    ash::SessionInfo info;
+    info.is_running_in_app_mode = true;
+    info.state = session_manager::SessionState::ACTIVE;
+    ash::Shell::Get()->session_controller()->SetSessionInfo(info);
+  }
+
+  bool is_new_window_called() const {
+    return fake_new_window_delegate_->is_new_window_called();
+  }
+
+  bool is_new_tab_called() const {
+    return fake_new_window_delegate_->is_new_tab_called();
+  }
+
+ protected:
+  ui::Accelerator new_window_accelerator =
+      ui::Accelerator(ui::VKEY_N, ui::EF_CONTROL_DOWN);
+
+ private:
+  raw_ptr<FakeNewWindowDelegate> fake_new_window_delegate_;
+  std::unique_ptr<ash::TestNewWindowDelegateProvider>
+      test_new_window_delegate_provider_;
+};
+
+TEST_P(AppSessionTroubleshootingShortcutsTest, NewWindowShortcutEnabled) {
+  SetUpKioskSession();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
+
+  ProcessInController(new_window_accelerator);
+  EXPECT_TRUE(is_new_window_called());
+}
+
+// Just confirm that other shortcuts (e.g. new tab) do not work.
+TEST_P(AppSessionTroubleshootingShortcutsTest, NewTabShortcutIsNoAction) {
+  SetUpKioskSession();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/true);
+
+  ProcessInController(ui::Accelerator(ui::VKEY_T, ui::EF_CONTROL_DOWN));
+  EXPECT_FALSE(is_new_tab_called());
+  EXPECT_FALSE(is_new_window_called());
+}
+
+TEST_P(AppSessionTroubleshootingShortcutsTest,
+       NewWindowShortcutNoActionByDefault) {
+  SetUpKioskSession();
+
+  ProcessInController(new_window_accelerator);
+  EXPECT_FALSE(is_new_window_called());
+}
+
+TEST_P(AppSessionTroubleshootingShortcutsTest,
+       NewWindowShortcutNoActionIfPolicyDisabled) {
+  SetUpKioskSession();
+  UpdateTroubleshootingToolsPolicy(/*enable=*/false);
+
+  ProcessInController(new_window_accelerator);
+  EXPECT_FALSE(is_new_window_called());
+}
+
+INSTANTIATE_TEST_SUITE_P(AppSessionTroubleshootingShortcuts,
+                         AppSessionTroubleshootingShortcutsTest,
+                         ::testing::Bool());
+
+#endif  //  BUILDFLAG(IS_CHROMEOS_ASH)
+
 #if BUILDFLAG(ENABLE_PLUGINS)
 TEST_F(AppSessionTest, ShouldHandlePlugin) {
   // Create an out-of-process pepper plugin.
diff --git a/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller.h b/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller.h
index 2d700d5e5..b0fa9b8c 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller.h
@@ -27,7 +27,7 @@
       delete;
   KioskTroubleshootingController& operator=(
       const KioskTroubleshootingController&) = delete;
-  ~KioskTroubleshootingController();
+  virtual ~KioskTroubleshootingController();
 
   // Returns `false` if `prefs::kKioskTroubleshootingToolsEnabled` preference is
   // not found in the pref service, otherwise returns its value.
diff --git a/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller_ash.cc b/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller_ash.cc
new file mode 100644
index 0000000..5dadb3d
--- /dev/null
+++ b/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller_ash.cc
@@ -0,0 +1,72 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller_ash.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "ash/accelerators/accelerator_controller_impl.h"
+#include "ash/public/cpp/new_window_delegate.h"
+#include "ash/shell.h"
+
+namespace ash {
+
+KioskTroubleshootingControllerAsh::KioskTroubleshootingControllerAsh(
+    PrefService* pref_service,
+    base::OnceClosure shutdown_app_session_callback)
+    : KioskTroubleshootingController(pref_service,
+                                     std::move(shutdown_app_session_callback)) {
+  RegisterTroubleshootingAccelerators();
+}
+
+KioskTroubleshootingControllerAsh::~KioskTroubleshootingControllerAsh() {
+  Shell::Get()->accelerator_controller()->UnregisterAll(this);
+}
+
+bool KioskTroubleshootingControllerAsh::AcceleratorPressed(
+    const ui::Accelerator& accelerator) {
+  // Do not process any accelerators if troubleshooting tools are disabled.
+  if (!AreKioskTroubleshootingToolsEnabled()) {
+    return false;
+  }
+
+  auto it = accelerators_with_actions_.find(accelerator);
+  DCHECK(it != accelerators_with_actions_.end());
+
+  switch (it->second) {
+    case TroubleshootingAcceleratorAction::NEW_WINDOW:
+      NewWindowDelegate::GetPrimary()->NewWindow(
+          /*incognito=*/false,
+          /*should_trigger_session_restore=*/false);
+      return true;
+  }
+
+  return false;
+}
+
+bool KioskTroubleshootingControllerAsh::CanHandleAccelerators() const {
+  return AreKioskTroubleshootingToolsEnabled();
+}
+
+void KioskTroubleshootingControllerAsh::RegisterTroubleshootingAccelerators() {
+  // Ctrl+N
+  accelerators_with_actions_.insert(
+      {ui::Accelerator(ui::VKEY_N, ui::EF_CONTROL_DOWN),
+       TroubleshootingAcceleratorAction::NEW_WINDOW});
+
+  Shell::Get()->accelerator_controller()->Register(GetAllAccelerators(), this);
+}
+
+std::vector<ui::Accelerator>
+KioskTroubleshootingControllerAsh::GetAllAccelerators() const {
+  std::vector<ui::Accelerator> accelerators;
+  for (auto const& accelerator : accelerators_with_actions_) {
+    accelerators.emplace_back(accelerator.first);
+  }
+  return accelerators;
+}
+
+}  // namespace ash
diff --git a/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller_ash.h b/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller_ash.h
new file mode 100644
index 0000000..5b983ae
--- /dev/null
+++ b/chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller_ash.h
@@ -0,0 +1,52 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_TROUBLESHOOTING_CONTROLLER_ASH_H_
+#define CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_TROUBLESHOOTING_CONTROLLER_ASH_H_
+
+#include <map>
+#include <vector>
+
+#include "chrome/browser/chromeos/app_mode/kiosk_troubleshooting_controller.h"
+#include "components/prefs/pref_service.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace ash {
+
+// This class registers kiosk troubleshooting accelerators to be able to handle
+// them in the kiosk session. See `TroubleshootingAcceleratorAction` for
+// which accelerators are currently handled.
+class KioskTroubleshootingControllerAsh
+    : public chromeos::KioskTroubleshootingController,
+      public ui::AcceleratorTarget {
+ public:
+  KioskTroubleshootingControllerAsh(
+      PrefService* pref_service,
+      base::OnceClosure shutdown_app_session_callback);
+  KioskTroubleshootingControllerAsh(const KioskTroubleshootingControllerAsh&) =
+      delete;
+  KioskTroubleshootingControllerAsh& operator=(
+      const KioskTroubleshootingControllerAsh&) = delete;
+  ~KioskTroubleshootingControllerAsh() override;
+
+ private:
+  enum class TroubleshootingAcceleratorAction {
+    NEW_WINDOW,
+  };
+
+  // ui::AcceleratorTarget:
+  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+  bool CanHandleAccelerators() const override;
+
+  void RegisterTroubleshootingAccelerators();
+
+  std::vector<ui::Accelerator> GetAllAccelerators() const;
+
+  std::map<ui::Accelerator, TroubleshootingAcceleratorAction>
+      accelerators_with_actions_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_TROUBLESHOOTING_CONTROLLER_ASH_H_
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc
index 6982d0f4..8b217513 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.cc
@@ -4,12 +4,16 @@
 
 #include "chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h"
 
+#include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
 #include "base/process/process_handle.h"
 #include "base/task/bind_post_task.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_file_access_copy_or_move_delegate_factory.h"
 #include "chromeos/dbus/dlp/dlp_client.h"
+#include "components/file_access/scoped_file_access.h"
+#include "components/file_access/scoped_file_access_copy.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -56,7 +60,7 @@
         if (!request_files_access_for_system_io_callback_) {
           request_files_access_for_system_io_callback_ =
               new file_access::ScopedFileAccessDelegate::
-                  RequestFilesAccessForSystemIOCallback(base::BindPostTask(
+                  RequestFilesAccessIOCallback(base::BindPostTask(
                       content::GetUIThreadTaskRunner({}),
                       base::BindRepeating(&RequestFileAccessForSystem)));
         }
@@ -115,6 +119,29 @@
   PostRequestFileAccessToDaemon(request, std::move(callback));
 }
 
+DlpScopedFileAccessDelegate::RequestFilesAccessIOCallback
+DlpScopedFileAccessDelegate::CreateFileAccessCallback(
+    const GURL& destination) const {
+  return base::BindPostTask(
+      content::GetUIThreadTaskRunner({}),
+      base::BindRepeating(
+          [](const GURL& destination, const std::vector<base::FilePath>& files,
+             base::OnceCallback<void(file_access::ScopedFileAccess)> callback) {
+            if (file_access::ScopedFileAccessDelegate::HasInstance()) {
+              file_access::ScopedFileAccessDelegate::Get()->RequestFilesAccess(
+                  files, destination,
+                  base::BindPostTask(content::GetIOThreadTaskRunner({}),
+                                     std::move(callback)));
+            } else {
+              content::GetIOThreadTaskRunner({})->PostTask(
+                  FROM_HERE,
+                  base::BindOnce(std::move(callback),
+                                 file_access::ScopedFileAccess::Allowed()));
+            }
+          },
+          destination));
+}
+
 void DlpScopedFileAccessDelegate::PostRequestFileAccessToDaemon(
     const ::dlp::RequestFileAccessRequest request,
     base::OnceCallback<void(file_access::ScopedFileAccess)> callback) {
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h
index 500d8120..f058055b 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h
@@ -44,6 +44,8 @@
       const std::vector<base::FilePath>& files,
       base::OnceCallback<void(file_access::ScopedFileAccess)> callback)
       override;
+  RequestFilesAccessIOCallback CreateFileAccessCallback(
+      const GURL& destination) const override;
 
  protected:
   explicit DlpScopedFileAccessDelegate(chromeos::DlpClient* client);
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate_unittest.cc
index be7e29b6..ef68fb7 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate_unittest.cc
@@ -36,7 +36,8 @@
  protected:
   content::BrowserTaskEnvironment task_environment_;
   chromeos::FakeDlpClient fake_dlp_client_;
-  DlpScopedFileAccessDelegate delegate_{&fake_dlp_client_};
+  std::unique_ptr<DlpScopedFileAccessDelegate> delegate_{
+      new DlpScopedFileAccessDelegate(&fake_dlp_client_)};
 };
 
 TEST_F(DlpScopedFileAccessDelegateTest, TestNoSingleton) {
@@ -44,14 +45,14 @@
   base::CreateTemporaryFile(&file_path);
 
   base::test::TestFuture<file_access::ScopedFileAccess> future1;
-  delegate_.RequestFilesAccess({file_path}, GURL("example.com"),
-                               future1.GetCallback());
+  delegate_->RequestFilesAccess({file_path}, GURL("example.com"),
+                                future1.GetCallback());
   EXPECT_TRUE(future1.Get<0>().is_allowed());
 
   fake_dlp_client_.SetFileAccessAllowed(false);
   base::test::TestFuture<file_access::ScopedFileAccess> future2;
-  delegate_.RequestFilesAccess({file_path}, GURL("example.com"),
-                               future2.GetCallback());
+  delegate_->RequestFilesAccess({file_path}, GURL("example.com"),
+                                future2.GetCallback());
   EXPECT_FALSE(future2.Get<0>().is_allowed());
 }
 
@@ -87,6 +88,84 @@
   EXPECT_TRUE(future1.Get<0>().is_allowed());
 }
 
+TEST_F(DlpScopedFileAccessDelegateTest, CreateFileAccessCallbackAllowTest) {
+  base::FilePath file_path;
+  base::CreateTemporaryFile(&file_path);
+
+  DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
+  fake_dlp_client_.SetFileAccessAllowed(true);
+
+  base::test::TestFuture<file_access::ScopedFileAccess> future;
+  auto* delegate = file_access::ScopedFileAccessDelegate::Get();
+  auto cb = delegate->CreateFileAccessCallback(GURL("https://google.com"));
+  cb.Run({file_path}, future.GetCallback());
+  EXPECT_TRUE(future.Get<0>().is_allowed());
+}
+
+TEST_F(DlpScopedFileAccessDelegateTest, CreateFileAccessCallbackDenyTest) {
+  base::FilePath file_path;
+  base::CreateTemporaryFile(&file_path);
+
+  DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
+  fake_dlp_client_.SetFileAccessAllowed(false);
+
+  base::test::TestFuture<file_access::ScopedFileAccess> future;
+  auto* delegate = file_access::ScopedFileAccessDelegate::Get();
+  auto cb = delegate->CreateFileAccessCallback(GURL("https://google.com"));
+  cb.Run({file_path}, future.GetCallback());
+  EXPECT_FALSE(future.Get<0>().is_allowed());
+}
+
+TEST_F(DlpScopedFileAccessDelegateTest,
+       CreateFileAccessCallbackLostInstanceTest) {
+  base::FilePath file_path;
+  base::CreateTemporaryFile(&file_path);
+
+  DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
+  fake_dlp_client_.SetFileAccessAllowed(false);
+
+  base::test::TestFuture<file_access::ScopedFileAccess> future;
+  auto* delegate = file_access::ScopedFileAccessDelegate::Get();
+  auto cb = delegate->CreateFileAccessCallback(GURL("https://google.com"));
+  delegate_.reset();
+  cb.Run({file_path}, future.GetCallback());
+  EXPECT_TRUE(future.Get<0>().is_allowed());
+}
+
+TEST_F(DlpScopedFileAccessDelegateTest, GetCallbackSystemTest) {
+  base::FilePath file_path;
+  base::CreateTemporaryFile(&file_path);
+
+  DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
+
+  // Post a task on IO thread to sync with to be sure the IO task setting
+  // `request_files_access_for_system_io_callback_` has run.
+  base::RunLoop init;
+  auto io_thread = content::GetIOThreadTaskRunner({});
+  io_thread->PostTask(
+      FROM_HERE, base::BindOnce(&base::RunLoop::Quit, base::Unretained(&init)));
+  init.Run();
+
+  base::test::TestFuture<file_access::ScopedFileAccess> future;
+  auto* delegate = file_access::ScopedFileAccessDelegate::Get();
+  auto cb = delegate->GetCallbackForSystem();
+  EXPECT_TRUE(cb);
+  cb.Run({file_path}, future.GetCallback());
+  EXPECT_TRUE(future.Get<0>().is_allowed());
+}
+
+TEST_F(DlpScopedFileAccessDelegateTest, GetCallbackSystemNoSingeltonTest) {
+  base::FilePath file_path;
+  base::CreateTemporaryFile(&file_path);
+
+  base::test::TestFuture<file_access::ScopedFileAccess> future;
+  auto* delegate = file_access::ScopedFileAccessDelegate::Get();
+  auto cb = delegate->GetCallbackForSystem();
+  EXPECT_TRUE(cb);
+  cb.Run({file_path}, future.GetCallback());
+  EXPECT_TRUE(future.Get<0>().is_allowed());
+}
+
 TEST_F(DlpScopedFileAccessDelegateTest, TestMultipleInstances) {
   DlpScopedFileAccessDelegate::Initialize(nullptr);
   EXPECT_NO_FATAL_FAILURE(DlpScopedFileAccessDelegate::Initialize(nullptr));
@@ -209,7 +288,6 @@
                                                          std::move(callback));
               }),
           false /* = restore_original_callback*/);
-
   // The request for file access should be granted as that is the default
   // behaviour for no running dlp (no rules).
   io_thread_->PostTask(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index b0069ada..59a8618c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1834,6 +1834,11 @@
     "expiry_milestone": 115
   },
   {
+    "name": "enable-autofill-virtual-cards-on-touch-to-fill",
+    "owners": ["vishwasuppoor"],
+    "expiry_milestone": 120
+  },
+  {
     "name": "enable-automatic-key-rotation",
     "owners": [ "hmare@google.com", "cbe-device-trust-eng@google.com" ],
     "expiry_milestone": 120
@@ -2263,6 +2268,11 @@
     "expiry_milestone": 118
   },
   {
+    "name": "enable-edge-detection",
+    "owners": ["cgzhang","robsc"],
+    "expiry_milestone": 120
+  },
+  {
     "name": "enable-edid-based-display-ids",
     "owners": [ "gildekel", "//ui/display/OWNERS", "chromeos-gfx-display@chromium.org" ],
     "expiry_milestone": 120
@@ -4850,6 +4860,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "mixed-content-autoupgrade-ios",
+    "owners": [ "meacer", "trusty-transport@chromium.org" ],
+    "expiry_milestone": 114
+  },
+  {
     "name": "modern-tab-strip",
     "owners": [ "bling-flags@google.com" ],
     "expiry_milestone": 92
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 58f675f..ea95fa7 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -610,6 +610,12 @@
     "When enabled, the Autofill dropdown's suggestions' labels are displayed "
     "using the improved disambiguation format.";
 
+const char kAutofillVirtualCardsOnTouchToFillAndroidName[] =
+    "Enable virtual cards on Touch To Fill bottomsheet for credit cards";
+const char kAutofillVirtualCardsOnTouchToFillAndroidDescription[] =
+    "When enabled, virtual credit card suggestions are shown on the Touch To "
+    "Fill bottomsheet for credit cards.";
+
 const char kBackForwardCacheName[] = "Back-forward cache";
 const char kBackForwardCacheDescription[] =
     "If enabled, caches eligible pages after cross-site navigations."
@@ -5347,6 +5353,10 @@
 const char kEnablePalmSuppressionDescription[] =
     "If enabled, suppresses touch when a stylus is on a touchscreen.";
 
+const char kEnableEdgeDetectionName[] = "Enable Edge Detection.";
+const char kEnableEdgeDetectionDescription[] =
+    "If enabled, suppresses edge touch based on sensors' info.";
+
 const char kEnablePerDeskZOrderName[] =
     "Enable per-desk Z-order for all-desk windows.";
 const char kEnablePerDeskZOrderDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 4fa1c7ca..8bbcc8e3 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -333,6 +333,9 @@
 extern const char kAutofillUseImprovedLabelDisambiguationName[];
 extern const char kAutofillUseImprovedLabelDisambiguationDescription[];
 
+extern const char kAutofillVirtualCardsOnTouchToFillAndroidName[];
+extern const char kAutofillVirtualCardsOnTouchToFillAndroidDescription[];
+
 extern const char kBackForwardCacheName[];
 extern const char kBackForwardCacheDescription[];
 
@@ -3059,6 +3062,9 @@
 extern const char kEnableNeuralStylusPalmRejectionName[];
 extern const char kEnableNeuralStylusPalmRejectionDescription[];
 
+extern const char kEnableEdgeDetectionName[];
+extern const char kEnableEdgeDetectionDescription[];
+
 extern const char kEnableOsFeedbackName[];
 extern const char kEnableOsFeedbackDescription[];
 
diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
index a0ead8e3..218209b 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
@@ -316,7 +316,9 @@
     int64_t offset,
     int64_t max_bytes_to_read,
     const base::Time& expected_modification_time,
-    FileSystemContext* context) const {
+    FileSystemContext* context,
+    file_access::ScopedFileAccessDelegate::
+        RequestFilesAccessIOCallback /*file_access*/) const {
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
   if (url.type() == storage::kFileSystemTypeDeviceMedia) {
     std::unique_ptr<storage::FileStreamReader> reader =
diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.h b/chrome/browser/media_galleries/fileapi/media_file_system_backend.h
index 5273fd3..57aa48a 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.h
+++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.h
@@ -17,6 +17,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
 #include "components/download/public/common/quarantine_connection.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/file_system_request_info.h"
 #include "storage/browser/file_system/task_runner_bound_observer_list.h"
@@ -85,7 +86,9 @@
       int64_t offset,
       int64_t max_bytes_to_read,
       const base::Time& expected_modification_time,
-      storage::FileSystemContext* context) const override;
+      storage::FileSystemContext* context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) const override;
   std::unique_ptr<storage::FileStreamWriter> CreateFileStreamWriter(
       const storage::FileSystemURL& url,
       int64_t offset,
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index e23084f7..9696920 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -1202,6 +1202,8 @@
   uint32_t shared_footprint_total_kb = 0;
   uint32_t resident_set_total_kb = 0;
   uint64_t tiles_total_memory = 0;
+  uint64_t hibernated_canvas_total_memory = 0;
+  uint64_t hibernated_canvas_total_original_memory = 0;
   bool emit_metrics_for_all_processes = pid_scope_ == base::kNullProcessId;
 
   TabFootprintAggregator per_tab_metrics;
@@ -1249,6 +1251,10 @@
         renderer_private_footprint_visible_or_higher_total_kb +=
             is_less_than_visible_renderer ? 0 : process_pmf_kb;
 #endif  // BUILDFLAG(IS_ANDROID)
+        hibernated_canvas_total_memory +=
+            pmd.GetMetric("canvas/hibernated", kSize).value_or(0);
+        hibernated_canvas_total_original_memory +=
+            pmd.GetMetric("canvas/hibernated", "original_size").value_or(0);
         const PageInfo* single_page_info = nullptr;
         auto iter = process_infos_.find(pmd.pid());
         if (iter != process_infos_.end()) {
@@ -1402,6 +1408,12 @@
         renderer_private_footprint_visible_or_higher_total_kb / kKiB);
 #endif
 
+    UMA_HISTOGRAM_MEMORY_MEDIUM_MB("Memory.Total.HibernatedCanvas.Size",
+                                   hibernated_canvas_total_memory / kMiB);
+    UMA_HISTOGRAM_MEMORY_MEDIUM_MB(
+        "Memory.Total.HibernatedCanvas.OriginalSize",
+        hibernated_canvas_total_original_memory / kMiB);
+
     Memory_Experimental(ukm::UkmRecorder::GetNewSourceID())
         .SetTotal2_PrivateMemoryFootprint(private_footprint_total_kb / kKiB)
         .SetTotal2_SharedMemoryFootprint(shared_footprint_total_kb / kKiB)
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
index 994652c..5cb02de 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
@@ -895,11 +895,17 @@
   PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid202);
 
   constexpr uint64_t kMiB = 1024 * 1024;
+  constexpr uint64_t kKiB = 1024;
   SetAllocatorDumpMetric(global_dump->process_dumps[0], "cc/tile_memory",
                          "size", 12 * kMiB);
   SetAllocatorDumpMetric(global_dump->process_dumps[1], "cc/tile_memory",
                          "size", 22 * kMiB);
 
+  SetAllocatorDumpMetric(global_dump->process_dumps[0], "canvas/hibernated",
+                         "size", 22 * kMiB);
+  SetAllocatorDumpMetric(global_dump->process_dumps[1], "canvas/hibernated",
+                         "size", 12 * kMiB);
+
   global_dump->aggregated_metrics->native_library_resident_kb =
       kNativeLibraryResidentMemoryFootprint;
   global_dump->aggregated_metrics->native_library_not_resident_ordered_kb =
@@ -912,6 +918,7 @@
   histograms.ExpectTotalCount("Memory.Renderer.SharedMemoryFootprint", 0);
   histograms.ExpectTotalCount("Memory.Renderer.ResidentSet", 0);
 
+  histograms.ExpectTotalCount("Memory.Total.HibernatedCanvas.Size", 0);
   histograms.ExpectTotalCount("Memory.Total.PrivateMemoryFootprint", 0);
   histograms.ExpectTotalCount("Memory.Total.RendererPrivateMemoryFootprint", 0);
   histograms.ExpectTotalCount("Memory.Total.RendererMalloc", 0);
@@ -942,6 +949,12 @@
   emitter->ReceivedProcessInfos(GetProcessInfo(test_ukm_recorder_));
 
   // Check that the expected values have been emitted to histograms.
+  histograms.ExpectBucketCount(
+      "Memory.Experimental.Renderer2.Small.HibernatedCanvas.Size", 12 * kKiB,
+      1);
+  histograms.ExpectBucketCount(
+      "Memory.Experimental.Renderer2.Small.HibernatedCanvas.Size", 22 * kKiB,
+      1);
   histograms.ExpectUniqueSample("Memory.Renderer.PrivateMemoryFootprint",
                                 kTestRendererPrivateMemoryFootprint, 2);
   histograms.ExpectUniqueSample("Memory.Renderer.SharedMemoryFootprint",
@@ -949,6 +962,8 @@
   histograms.ExpectUniqueSample("Memory.Renderer.ResidentSet",
                                 kTestRendererResidentSet, 2);
 
+  histograms.ExpectUniqueSample("Memory.Total.HibernatedCanvas.Size", 12 + 22,
+                                1);
   histograms.ExpectUniqueSample("Memory.Total.PrivateMemoryFootprint",
                                 2 * kTestRendererPrivateMemoryFootprint, 1);
   histograms.ExpectUniqueSample("Memory.Total.RendererPrivateMemoryFootprint",
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
index 7e5191c..755dc8d9 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
@@ -17,4 +17,7 @@
 
   // Open the URLs as a new tab group and clobber the existing NTP.
   OpenUrlsInTabGroup(array<url.mojom.Url> urls);
+
+  // Dismisses a cluster by marking its associated visits as not relevant.
+  DismissCluster(array<history_clusters.mojom.URLVisit> visits);
 };
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
index 016c125..6b7608e 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history_clusters/history_clusters_service_factory.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom.h"
 #include "chrome/browser/profiles/profile.h"
@@ -26,6 +27,7 @@
 #include "components/history_clusters/core/history_clusters_service_task.h"
 #include "components/history_clusters/core/history_clusters_util.h"
 #include "components/history_clusters/public/mojom/history_cluster_types.mojom.h"
+#include "components/keyed_service/core/service_access_type.h"
 #include "components/search/ntp_features.h"
 #include "url/url_util.h"
 
@@ -164,11 +166,18 @@
   std::set<GURL> seen_urls = {};
   history_clusters::CullNonProminentOrDuplicateClusters("", clusters,
                                                         &seen_urls);
+  // Cull clusters that do not have the minimum number of visits to be eligible
+  // for display.
+  base::EraseIf(clusters, [&](auto& cluster) {
+    // Cull visits that have a zero relevance score.
+    base::EraseIf(cluster.visits,
+                  [&](auto& visit) { return visit.score == 0.0; });
 
-  history_clusters::HideAndCullLowScoringVisits(clusters, kMinRequiredVisits);
+    return cluster.visits.size() < kMinRequiredVisits;
+  });
   history_clusters::CoalesceRelatedSearches(clusters);
-  // Cull clusters that do not have the minimum required of related searches to
-  // be eligible for display.
+  // Cull clusters that do not have the minimum required number of related
+  // searches to be eligible for display.
   base::EraseIf(clusters, [&](auto& cluster) {
     return cluster.related_searches.size() < kMinRequiredRelatedSearches;
   });
@@ -282,3 +291,20 @@
                                               model->GetActiveWebContents()));
   model->AddToNewGroup(tab_indices);
 }
+
+void HistoryClustersPageHandler::DismissCluster(
+    const std::vector<history_clusters::mojom::URLVisitPtr> visits) {
+  if (visits.empty()) {
+    return;
+  }
+
+  std::vector<history::VisitID> visit_ids;
+  base::ranges::transform(
+      visits, std::back_inserter(visit_ids),
+      [](const auto& url_visit_ptr) { return url_visit_ptr->visit_id; });
+
+  auto* history_service = HistoryServiceFactory::GetForProfile(
+      profile_, ServiceAccessType::EXPLICIT_ACCESS);
+  history_service->HideVisits(visit_ids, base::BindOnce([]() {}),
+                              &hide_visits_task_tracker_);
+}
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h
index 3e2a256..ee8972e 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h
@@ -10,6 +10,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/history_clusters/core/history_clusters_types.h"
@@ -44,6 +45,8 @@
   void GetCluster(GetClusterCallback callback) override;
   void ShowJourneysSidePanel(const std::string& query) override;
   void OpenUrlsInTabGroup(const std::vector<GURL>&) override;
+  void DismissCluster(
+      const std::vector<history_clusters::mojom::URLVisitPtr> visits) override;
 
  private:
   // Forward the most relevant history cluster to the callback if any.
@@ -63,6 +66,7 @@
   // `Done()` will be true if there is no ongoing task.
   std::unique_ptr<history_clusters::HistoryClustersServiceTask>
       fetch_clusters_task_;
+  base::CancelableTaskTracker hide_visits_task_tracker_;
 
   base::WeakPtrFactory<HistoryClustersPageHandler> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler_unittest.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler_unittest.cc
index 59063008a..e3167f3 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler_unittest.cc
@@ -9,9 +9,11 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task/cancelable_task_tracker.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/time/time.h"
+#include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history_clusters/history_clusters_service_factory.h"
 #include "chrome/browser/ui/side_panel/history_clusters/history_clusters_tab_helper.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
@@ -19,6 +21,7 @@
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/test_browser_window.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/history/core/browser/history_context.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/history/core/test/history_service_test_util.h"
 #include "components/history_clusters/core/history_clusters_types.h"
@@ -31,6 +34,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+#include <vector>
+
 namespace {
 
 class MockHistoryClustersTabHelper
@@ -54,6 +59,19 @@
       : HistoryClustersTabHelper(web_contents) {}
 };
 
+class MockHistoryService : public history::HistoryService {
+ public:
+  MockHistoryService() : HistoryService() {}
+
+  MOCK_METHOD1(ClearCachedDataForContextID,
+               void(history::ContextID context_id));
+  MOCK_METHOD3(HideVisits,
+               base::CancelableTaskTracker::TaskId(
+                   const std::vector<history::VisitID>& visit_ids,
+                   base::OnceClosure callback,
+                   base::CancelableTaskTracker* tracker));
+};
+
 }  // namespace
 
 class HistoryClustersPageHandlerTest : public BrowserWithTestWindowTest {
@@ -70,6 +88,9 @@
         content::WebContents::CreateParams(profile()));
     mock_history_clusters_tab_helper_ =
         MockHistoryClustersTabHelper::CreateForWebContents(web_contents_.get());
+    mock_history_service_ =
+        static_cast<MockHistoryService*>(HistoryServiceFactory::GetForProfile(
+            profile(), ServiceAccessType::EXPLICIT_ACCESS));
     handler_ = std::make_unique<HistoryClustersPageHandler>(
         mojo::PendingReceiver<ntp::history_clusters::mojom::PageHandler>(),
         web_contents_.get());
@@ -89,6 +110,8 @@
     return *mock_history_clusters_tab_helper_;
   }
 
+  MockHistoryService& mock_history_service() { return *mock_history_service_; }
+
   HistoryClustersPageHandler& handler() { return *handler_; }
 
  private:
@@ -99,6 +122,11 @@
                                      -> std::unique_ptr<KeyedService> {
                return std::make_unique<
                    history_clusters::TestHistoryClustersService>();
+             })},
+            {HistoryServiceFactory::GetInstance(),
+             base::BindRepeating([](content::BrowserContext* context)
+                                     -> std::unique_ptr<KeyedService> {
+               return std::make_unique<MockHistoryService>();
              })}};
   }
 
@@ -106,6 +134,7 @@
       test_history_clusters_service_;
   std::unique_ptr<content::WebContents> web_contents_;
   raw_ptr<MockHistoryClustersTabHelper> mock_history_clusters_tab_helper_;
+  raw_ptr<MockHistoryService> mock_history_service_;
   std::unique_ptr<HistoryClustersPageHandler> handler_;
 };
 
@@ -302,3 +331,26 @@
   TabGroupModel* tab_group_model = tab_strip_model->group_model();
   ASSERT_EQ(1u, tab_group_model->ListTabGroups().size());
 }
+
+TEST_F(HistoryClustersPageHandlerTest, DismissCluster) {
+  std::vector<history::VisitID> visit_ids;
+  EXPECT_CALL(mock_history_service(), HideVisits)
+      .Times(1)
+      .WillOnce(testing::Invoke(
+          [&visit_ids](const std::vector<history::VisitID>& visit_ids_arg,
+                       base::OnceClosure callback_arg,
+                       base::CancelableTaskTracker* tracker_arg)
+              -> base::CancelableTaskTracker::TaskId {
+            visit_ids = visit_ids_arg;
+            return 0;
+          }));
+
+  auto visit_mojom = history_clusters::mojom::URLVisit::New();
+  visit_mojom->visit_id = 1;
+  std::vector<history_clusters::mojom::URLVisitPtr> visits_mojom;
+  visits_mojom.push_back(std::move(visit_mojom));
+
+  handler().DismissCluster(std::move(visits_mojom));
+  ASSERT_EQ(1u, visit_ids.size());
+  ASSERT_EQ(1u, visit_ids.front());
+}
diff --git a/chrome/browser/password_manager/web_app_profile_switcher.cc b/chrome/browser/password_manager/web_app_profile_switcher.cc
index 491937fc..2ea4668b3 100644
--- a/chrome/browser/password_manager/web_app_profile_switcher.cc
+++ b/chrome/browser/password_manager/web_app_profile_switcher.cc
@@ -25,6 +25,7 @@
   install_info.description =
       base::UTF8ToUTF16(web_app->untranslated_description());
   install_info.start_url = web_app->start_url();
+  install_info.manifest_id = web_app->manifest_id();
   install_info.manifest_url = web_app->manifest_url();
   install_info.scope = web_app->scope();
   install_info.manifest_icons = web_app->manifest_icons();
diff --git a/chrome/browser/password_manager/web_app_profile_switcher_browsertest.cc b/chrome/browser/password_manager/web_app_profile_switcher_browsertest.cc
index 6ff9b81..d001b054 100644
--- a/chrome/browser/password_manager/web_app_profile_switcher_browsertest.cc
+++ b/chrome/browser/password_manager/web_app_profile_switcher_browsertest.cc
@@ -31,6 +31,7 @@
   auto web_app_info = std::make_unique<WebAppInstallInfo>();
   web_app_info->start_url = GURL(kTestWebUIAppURL);
   web_app_info->title = u"Test app";
+  web_app_info->manifest_id = "";
   return web_app_info;
 }
 
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc
index f38368b..49fa250 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.cc
@@ -202,8 +202,6 @@
 
 bool SearchPrefetchRequest::StartPrefetchRequest(Profile* profile) {
   TRACE_EVENT0("loading", "SearchPrefetchRequest::StartPrefetchRequest");
-  net::NetworkTrafficAnnotationTag network_traffic_annotation =
-      NetworkAnnotationForPrefetch();
 
   url::Origin prefetch_origin = url::Origin::Create(prefetch_url_);
 
@@ -314,7 +312,6 @@
   SetSearchPrefetchStatus(SearchPrefetchStatus::kInFlight);
 
   StartPrefetchRequestInternal(profile, std::move(resource_request),
-                               network_traffic_annotation,
                                std::move(report_error_callback_));
   return true;
 }
@@ -474,18 +471,14 @@
 void SearchPrefetchRequest::StartPrefetchRequestInternal(
     Profile* profile,
     std::unique_ptr<network::ResourceRequest> resource_request,
-    const net::NetworkTrafficAnnotationTag& network_traffic_annotation,
     base::OnceCallback<void(bool)> report_error_callback) {
   TRACE_EVENT0("loading",
                "SearchPrefetchRequest::StartPrefetchRequestInternal");
   profile_ = profile;
-  network_traffic_annotation_ =
-      std::make_unique<net::NetworkTrafficAnnotationTag>(
-          network_traffic_annotation);
   prefetch_url_ = resource_request->url;
   streaming_url_loader_ = std::make_unique<StreamingSearchPrefetchURLLoader>(
       this, profile, navigation_prefetch_, std::move(resource_request),
-      network_traffic_annotation, std::move(report_error_callback));
+      NetworkAnnotationForPrefetch(), std::move(report_error_callback));
 }
 
 void SearchPrefetchRequest::StopPrefetch() {
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.h b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.h
index 393f8d9a..810b52d2 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.h
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_request.h
@@ -9,7 +9,6 @@
 
 #include "base/functional/callback.h"
 #include "base/state_transitions.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "url/gurl.h"
 
@@ -17,12 +16,17 @@
 class Profile;
 class SearchPrefetchURLLoader;
 class StreamingSearchPrefetchURLLoader;
+
 namespace content {
 class PreloadingAttempt;
 enum class PreloadingTriggeringOutcome;
 enum class PreloadingFailureReason;
 }  // namespace content
 
+namespace net {
+struct NetworkTrafficAnnotationTag;
+}  // namespace net
+
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 // Any updates to this class need to be propagated to enums.xml.
@@ -166,7 +170,6 @@
   void StartPrefetchRequestInternal(
       Profile* profile,
       std::unique_ptr<network::ResourceRequest> resource_request,
-      const net::NetworkTrafficAnnotationTag& traffic_annotation,
       base::OnceCallback<void(bool)> report_error_callback);
 
   // Stops the on-going prefetch and should mark |current_status_|
@@ -208,8 +211,6 @@
   // Whether this is for a navigation-time prefetch.
   bool navigation_prefetch_;
 
-  std::unique_ptr<net::NetworkTrafficAnnotationTag> network_traffic_annotation_;
-
   // The ongoing prefetch request. Null before and after the fetch.
   std::unique_ptr<StreamingSearchPrefetchURLLoader> streaming_url_loader_;
 
diff --git a/chrome/browser/preloading/prerender/prerender_browsertest.cc b/chrome/browser/preloading/prerender/prerender_browsertest.cc
index c859130..ea01c36 100644
--- a/chrome/browser/preloading/prerender/prerender_browsertest.cc
+++ b/chrome/browser/preloading/prerender/prerender_browsertest.cc
@@ -41,7 +41,6 @@
 // once it is exposed.
 constexpr int kFinalStatusActivated = 0;
 constexpr int kFinalStatusCrossSiteNavigation = 45;
-constexpr int kFinalStatusSameSiteCrossOriginNavigation = 47;
 
 }  // namespace
 
@@ -374,12 +373,10 @@
       kFinalStatusActivated, 1);
 }
 
-// TODO(crbug.com/1239281): Support the same-site cross-origin navigation.
 // Tests that the same-site cross-origin main frame navigation in an embedder
-// triggered prerendering page cancels the prerendering.
-IN_PROC_BROWSER_TEST_F(
-    PrerenderMainFrameNavigationBrowserTest,
-    SameSiteCrossOriginMainFrameNavigationCancelsEmbedderTriggeredPrerendering) {
+// triggered prerendering page succeeds.
+IN_PROC_BROWSER_TEST_F(PrerenderMainFrameNavigationBrowserTest,
+                       SameSiteCrossOriginMainFrameNavigation) {
   base::HistogramTester histogram_tester;
 
   // Navigate to an initial page.
@@ -387,8 +384,8 @@
   ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
 
   GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
-  GURL navigation_url =
-      embedded_test_server()->GetURL("b.a.test", "/title2.html");
+  GURL navigation_url = embedded_test_server()->GetURL(
+      "b.a.test", "/prerender_with_opt_in_header.html");
 
   // Start an embedder triggered prerendering.
   std::unique_ptr<content::PrerenderHandle> prerender_handle =
@@ -409,14 +406,24 @@
       *GetActiveWebContents(), host_id);
 
   // Start a same-site cross-origin main frame navigation in the prerender frame
-  // tree. It will cancel the initiator's prerendering.
+  // tree. It will not cancel the initiator's prerendering.
   prerender_helper().NavigatePrerenderedPage(host_id, navigation_url);
 
-  prerender_observer.WaitForDestroyed();
+  // Activate.
+  content::TestActivationManager activation_manager(GetActiveWebContents(),
+                                                    prerender_url);
+  // Simulate a browser-initiated navigation.
+  GetActiveWebContents()->OpenURL(content::OpenURLParams(
+      prerender_url, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
+      ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
+                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+      /*is_renderer_initiated=*/false));
+  activation_manager.WaitForNavigationFinished();
+  EXPECT_TRUE(activation_manager.was_activated());
 
   histogram_tester.ExpectUniqueSample(
       "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_DirectURLInput",
-      kFinalStatusSameSiteCrossOriginNavigation, 1);
+      kFinalStatusActivated, 1);
 }
 
 // Tests that the cross-site main frame navigation in an embedder triggered
@@ -431,8 +438,8 @@
   ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
 
   GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
-  GURL navigation_url =
-      embedded_test_server()->GetURL("b.test", "/title2.html");
+  GURL navigation_url = embedded_test_server()->GetURL(
+      "b.test", "/prerender_with_opt_in_header.html");
 
   // Start an embedder triggered prerendering.
   std::unique_ptr<content::PrerenderHandle> prerender_handle =
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 56ac06f7..b43f5bfa 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -251,6 +251,7 @@
 #include "chrome/browser/ash/account_manager/account_apps_availability_factory.h"
 #include "chrome/browser/ash/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ash/browser_context_keyed_service_factories.h"
+#include "chrome/browser/ash/crostini/crostini_export_import.h"
 #include "chrome/browser/ash/login/security_token_session_controller_factory.h"
 #include "chrome/browser/ash/system_extensions/api/window_management/cros_window_management_context_factory.h"
 #include "chrome/browser/ash/system_extensions/system_extensions_provider_factory.h"
@@ -711,6 +712,7 @@
   NavigationPredictorKeyedServiceFactory::GetInstance();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   NearbySharingServiceFactory::GetInstance();
+  crostini::CrostiniExportImport::EnsureFactoryBuilt();
 #endif
   NotificationDisplayServiceFactory::GetInstance();
   NotificationMetricsLoggerFactory::GetInstance();
diff --git a/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.html b/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.html
index f401c58..13148864 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.html
@@ -124,7 +124,7 @@
   <slot name="header-image"></slot>
 </div>
 <div class="body-wrapper">
-  <div id="content" autofocus tabindex="-1">
+  <div id="content" tabindex="-1">
     <slot name="title"></slot>
     <slot name="body"></slot>
   </div>
diff --git a/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.ts b/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.ts
index bd9498a..16fd1ef 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.ts
+++ b/chrome/browser/resources/chromeos/cloud_upload/base_setup_page.ts
@@ -58,6 +58,9 @@
     contentElement.addEventListener(
         'scroll', this.updateContentFade.bind(undefined, contentElement),
         {passive: true});
+    // Focus the contentElement so that the screen reader reads the title and
+    // the description.
+    contentElement.focus();
   }
 
   attributeChangedCallback(name: string, _oldValue: string, _newValue: string) {
diff --git a/chrome/browser/resources/chromeos/cloud_upload/drive_upload_page.html b/chrome/browser/resources/chromeos/cloud_upload/drive_upload_page.html
index c8503462..940744e 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/drive_upload_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/drive_upload_page.html
@@ -12,6 +12,10 @@
     font: var(--cros-display-7-font);
   }
 
+  #content:focus-visible {
+    outline: none;
+  }
+
   .normal-text {
     color: var(--cros-text-color-secondary);
     font: var(--cros-body-2-font);
@@ -23,17 +27,30 @@
     display: flex;
     justify-content: flex-end;
   }
+
+  cr-button {
+    border-radius: 16px;
+  }
+
+  cr-button:focus-visible {
+    /* disable cr-button default keyboard focus style */
+    box-shadow: none;
+    outline: 2px solid var(--cros-focus-ring-color);
+    outline-offset: 2px;
+  }
 </style>
 
 <!-- TODO(b/258071752): Use localized strings -->
-<div class="title">
-  Files will move to Google Drive when opening in Docs, Sheets, or Slides
-</div>
-<div class="normal-text">
-  <!-- TODO(b/258071752): Deep link to settings -->
-  You can change this at any time in Settings > Files > Office file handling
+<div id="content" autofocus tabindex="-1">
+  <div class="title">
+    Files will move to Google Drive when opening in Docs, Sheets, or Slides
+  </div>
+  <div class="normal-text">
+    <!-- TODO(b/258071752): Deep link to settings -->
+    You can change this at any time in Settings > Files > Office file handling
+  </div>
 </div>
 <div id="button-container">
   <cr-button class="cancel-button">$i18n{cancel}</cr-button>
   <cr-button class="action-button">Continue</cr-button>
-</div>
\ No newline at end of file
+</div>
diff --git a/chrome/browser/resources/chromeos/cloud_upload/file_handler_page.html b/chrome/browser/resources/chromeos/cloud_upload/file_handler_page.html
index b351d9f..f874bad 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/file_handler_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/file_handler_page.html
@@ -64,6 +64,10 @@
     padding-inline: 24px;
   }
 
+  cr-button {
+    border-radius: 16px;
+  }
+
   cr-button:focus-visible {
     /* disable cr-button default keyboard focus style */
     box-shadow: none;
diff --git a/chrome/browser/resources/chromeos/cloud_upload/move_confirmation_page.html b/chrome/browser/resources/chromeos/cloud_upload/move_confirmation_page.html
index b503185..577eae6 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/move_confirmation_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/move_confirmation_page.html
@@ -10,6 +10,10 @@
     padding: 32px;
   }
 
+  #content:focus-visible {
+    outline: none;
+  }
+
   .title {
     color: var(--cros-text-color-primary);
     font: var(--cros-display-6-font);
@@ -43,19 +47,32 @@
   cr-lottie {
     height: 236px;
   }
+
+  cr-button {
+    border-radius: 16px;
+  }
+
+  cr-button:focus-visible {
+    /* disable cr-button default keyboard focus style */
+    box-shadow: none;
+    outline: 2px solid var(--cros-focus-ring-color);
+    outline-offset: 2px;
+  }
 </style>
 
 <!-- TODO(b/258071752): Use localized strings -->
 <cr-lottie id="animation" aria-hidden="true" autoplay>
 </cr-lottie>
 <div class="body-wrapper">
-  <div class="title">
-    Move file to <span id="provider-name"></span> to open
-  </div>
-  <div class="normal-text">
-    The <span id="app-name"></span> app requires files to be in
-    <span id="provider-short-name"></span>. We can move your files
-    whenever you choose to open them.
+  <div id="content" autofocus tabindex="-1">
+    <div class="title">
+      Move file to <span id="provider-name"></span> to open
+    </div>
+    <div class="normal-text">
+      The <span id="app-name"></span> app requires files to be in
+      <span id="provider-short-name"></span>. We can move your files
+      whenever you choose to open them.
+    </div>
   </div>
   <cr-checkbox id="always-move-checkbox">
     <span class="checkbox-text">Do this automatically next time</span>
diff --git a/chrome/browser/resources/chromeos/cloud_upload/office_pwa_install_page.html b/chrome/browser/resources/chromeos/cloud_upload/office_pwa_install_page.html
index 58ea891..28ae962b 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/office_pwa_install_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/office_pwa_install_page.html
@@ -26,6 +26,17 @@
   .installed::before {
     -webkit-mask-image: url(icons/check.svg);
   }
+
+  cr-button {
+    border-radius: 16px;
+  }
+
+  cr-button:focus-visible {
+    /* disable cr-button default keyboard focus style */
+    box-shadow: none;
+    outline: 2px solid var(--cros-focus-ring-color);
+    outline-offset: 2px;
+  }
 </style>
 <picture slot="header-image">
   <source srcset="images/install_office_dark_mode.svg"
diff --git a/chrome/browser/resources/chromeos/cloud_upload/one_drive_upload_page.html b/chrome/browser/resources/chromeos/cloud_upload/one_drive_upload_page.html
index a289e39..52d7cd2d 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/one_drive_upload_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/one_drive_upload_page.html
@@ -1,4 +1,16 @@
 <!-- TODO: Use localized strings -->
+<style>
+  cr-button {
+    border-radius: 16px;
+  }
+
+  cr-button:focus-visible {
+    /* disable cr-button default keyboard focus style */
+    box-shadow: none;
+    outline: 2px solid var(--cros-focus-ring-color);
+    outline-offset: 2px;
+  }
+</style>
 <svg slot="header-image" viewBox="0 0 448 200">
   <use href="images/one_drive_success.svg#EXPORT_image"></use>
 </svg>
diff --git a/chrome/browser/resources/chromeos/cloud_upload/setup_cancel_dialog.html b/chrome/browser/resources/chromeos/cloud_upload/setup_cancel_dialog.html
index 85dd6be6..65262241 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/setup_cancel_dialog.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/setup_cancel_dialog.html
@@ -20,6 +20,16 @@
     padding: 49px 24px 24px;
   }
 
+  cr-button {
+    border-radius: 16px;
+  }
+
+  cr-button:focus-visible {
+    /* disable cr-button default keyboard focus style */
+    box-shadow: none;
+    outline: 2px solid var(--cros-focus-ring-color);
+    outline-offset: 2px;
+  }
 </style>
 
 <!-- TODO(b/254586358): Use localized strings -->
diff --git a/chrome/browser/resources/chromeos/cloud_upload/sign_in_page.html b/chrome/browser/resources/chromeos/cloud_upload/sign_in_page.html
index 5d7bf69..35774ab 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/sign_in_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/sign_in_page.html
@@ -24,6 +24,17 @@
   .error-text {
     margin-inline-start: 5px;
   }
+
+  cr-button {
+    border-radius: 16px;
+  }
+
+  cr-button:focus-visible {
+    /* disable cr-button default keyboard focus style */
+    box-shadow: none;
+    outline: 2px solid var(--cros-focus-ring-color);
+    outline-offset: 2px;
+  }
 </style>
 
 <!-- TODO(b/254586358): Use localized strings -->
diff --git a/chrome/browser/resources/chromeos/cloud_upload/welcome_page.html b/chrome/browser/resources/chromeos/cloud_upload/welcome_page.html
index cd8d6062..4dd7bca 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/welcome_page.html
+++ b/chrome/browser/resources/chromeos/cloud_upload/welcome_page.html
@@ -10,6 +10,17 @@
   ol {
     padding-inline-start: 20px;
   }
+
+  cr-button {
+    border-radius: 16px;
+  }
+
+  cr-button:focus-visible {
+    /* disable cr-button default keyboard focus style */
+    box-shadow: none;
+    outline: 2px solid var(--cros-focus-ring-color);
+    outline-offset: 2px;
+  }
 </style>
 
 <!-- TODO(b/254586358): Use localized strings -->
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui.html b/chrome/browser/resources/chromeos/network_ui/network_ui.html
index ec849ee..72301bd 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui.html
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui.html
@@ -137,12 +137,18 @@
   </div>
 
   <div class="tabpanel" id="select">
-    <div id="select-div">
-      <network-select
-          on-network-item-selected="onNetworkItemSelected_"
-          on-custom-item-selected="onCustomItemSelected_">
-      </network-select>
-    </div>
+    <div>$i18n{renderNetworkSelectButtonDescription}</div>
+    <cr-button class="action-button" on-click="renderNetworkSelect_">
+      $i18n{renderNetworkSelectButtonText}
+    </cr-button>
+    <template is="dom-if" if="[[showNetworkSelect_]]">
+      <div id="select-div">
+        <network-select
+            on-network-item-selected="onNetworkItemSelected_"
+            on-custom-item-selected="onCustomItemSelected_">
+        </network-select>
+      </div>
+    </template>
   </div>
 
   <div class="tabpanel" id="counters">
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui.js b/chrome/browser/resources/chromeos/network_ui/network_ui.js
index 3e3d7cd..e2dcef3 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui.js
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui.js
@@ -21,7 +21,7 @@
 import {loadTimeData} from 'chrome://resources/ash/common/load_time_data.m.js';
 import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
 import {CrosNetworkConfig, CrosNetworkConfigRemote, StartConnectResult} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
-import {Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './network_ui.html.js';
 import {NetworkUIBrowserProxy, NetworkUIBrowserProxyImpl} from './network_ui_browser_proxy.js';
@@ -116,6 +116,12 @@
       value: false,
     },
 
+    /** @private */
+    showNetworkSelect_: {
+      type: Boolean,
+      value: false,
+    },
+
   },
 
   /** @type {?CrosNetworkConfigRemote} */
@@ -128,15 +134,6 @@
   attached() {
     this.networkConfig_ = CrosNetworkConfig.getRemote();
 
-    const select = this.$$('network-select');
-    select.customItems = [
-      {
-        customItemName: 'addWiFiListItemName',
-        polymerIcon: 'cr:add',
-        customData: 'WiFi',
-      },
-    ];
-
     this.$$('#import-onc').value = '';
 
     this.requestGlobalPolicy_();
@@ -399,6 +396,21 @@
         this.$$('#network-diagnostics'));
   },
 
+  /** @private */
+  renderNetworkSelect_() {
+    this.showNetworkSelect_ = true;
+    flush();
+
+    const select = this.$$('network-select');
+    select.customItems = [
+      {
+        customItemName: 'addWiFiListItemName',
+        polymerIcon: 'cr:add',
+        customData: 'WiFi',
+      },
+    ];
+  },
+
   /**
    * Handles requests to open the feedback report dialog. The provided string
    * in the event will be sent as a part of the feedback report.
diff --git a/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest b/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest
index 55ee2053..cf0c669 100644
--- a/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest
+++ b/chrome/browser/resources/password_manager/chrome_branded_manifest.webmanifest
@@ -9,6 +9,7 @@
     }
   ],
   "start_url": "/?source=pwa",
+  "id": "chrome://password-manager/",
   "display": "standalone",
   "scope": "chrome://password-manager/"
 }
diff --git a/chrome/browser/resources/password_manager/manifest.webmanifest b/chrome/browser/resources/password_manager/manifest.webmanifest
index ea1841ef..3a4278be 100644
--- a/chrome/browser/resources/password_manager/manifest.webmanifest
+++ b/chrome/browser/resources/password_manager/manifest.webmanifest
@@ -9,6 +9,7 @@
     }
   ],
   "start_url": "/?source=pwa",
+  "id": "chrome://password-manager/",
   "display": "standalone",
   "scope": "chrome://password-manager/"
 }
\ No newline at end of file
diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
index fb3afaf..e52a13d0 100644
--- a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
+++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
@@ -163,7 +163,9 @@
     int64_t offset,
     int64_t max_bytes_to_read,
     const base::Time& expected_modification_time,
-    storage::FileSystemContext* context) const {
+    storage::FileSystemContext* context,
+    file_access::ScopedFileAccessDelegate::
+        RequestFilesAccessIOCallback /*file_access*/) const {
   DCHECK(CanHandleType(url.type()));
   return GetDelegate()->CreateFileStreamReader(
       url, offset, expected_modification_time, context);
diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.h b/chrome/browser/sync_file_system/local/sync_file_system_backend.h
index e142158..2748e3922 100644
--- a/chrome/browser/sync_file_system/local/sync_file_system_backend.h
+++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.h
@@ -12,6 +12,7 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/sync_file_system/sync_callbacks.h"
 #include "chrome/browser/sync_file_system/sync_status_code.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/file_system_quota_util.h"
 #include "storage/browser/file_system/sandbox_file_system_backend_delegate.h"
@@ -60,7 +61,9 @@
       int64_t offset,
       int64_t max_bytes_to_read,
       const base::Time& expected_modification_time,
-      storage::FileSystemContext* context) const override;
+      storage::FileSystemContext* context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) const override;
   std::unique_ptr<storage::FileStreamWriter> CreateFileStreamWriter(
       const storage::FileSystemURL& url,
       int64_t offset,
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 570301d..1c8a7731 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/url_identity.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -183,6 +184,15 @@
   agent->SetAllowRunningInsecureContent();
 }
 
+constexpr UrlIdentity::TypeSet allowed_types = {
+    UrlIdentity::Type::kDefault, UrlIdentity::Type::kFile,
+    UrlIdentity::Type::kIsolatedWebApp};
+constexpr UrlIdentity::FormatOptions options;
+
+UrlIdentity GetUrlIdentity(Profile* profile, const GURL& url) {
+  return UrlIdentity::CreateFromUrl(profile, url, allowed_types, options);
+}
+
 }  // namespace
 
 // ContentSettingSimpleBubbleModel ---------------------------------------------
@@ -556,7 +566,7 @@
 // content type and setting the default value based on the content setting.
 void ContentSettingSingleRadioGroup::SetRadioGroup() {
   const GURL& url = web_contents()->GetURL();
-  std::u16string display_host = url_formatter::FormatUrlForSecurityDisplay(url);
+  const UrlIdentity url_identity = GetUrlIdentity(GetProfile(), url);
 
   PageSpecificContentSettings* content_settings =
       PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
@@ -605,7 +615,7 @@
     radio_allow_label = l10n_util::GetStringFUTF16(
         GetIdForContentType(kBlockedAllowIDs, std::size(kBlockedAllowIDs),
                             content_type()),
-        display_host);
+        url_identity.name);
   }
 
   static const ContentSettingsTypeIdEntry kBlockedBlockIDs[] = {
@@ -632,7 +642,8 @@
   if (allowed) {
     int resource_id = GetIdForContentType(
         kAllowedBlockIDs, std::size(kAllowedBlockIDs), content_type());
-    radio_block_label = l10n_util::GetStringFUTF16(resource_id, display_host);
+    radio_block_label =
+        l10n_util::GetStringFUTF16(resource_id, url_identity.name);
   } else {
     radio_block_label = l10n_util::GetStringUTF16(GetIdForContentType(
         kBlockedBlockIDs, std::size(kBlockedBlockIDs), content_type()));
@@ -968,8 +979,7 @@
   GURL url = content_settings->media_stream_access_origin();
   RadioGroup radio_group;
   radio_group.url = url;
-
-  std::u16string display_host = url_formatter::FormatUrlForSecurityDisplay(url);
+  const UrlIdentity url_identity = GetUrlIdentity(GetProfile(), url);
 
   DCHECK(CameraAccessed() || MicrophoneAccessed());
   int radio_allow_label_id = 0;
@@ -1023,7 +1033,7 @@
   }
 
   std::u16string radio_allow_label =
-      l10n_util::GetStringFUTF16(radio_allow_label_id, display_host);
+      l10n_util::GetStringFUTF16(radio_allow_label_id, url_identity.name);
   std::u16string radio_block_label =
       l10n_util::GetStringUTF16(radio_block_label_id);
 
@@ -1427,8 +1437,10 @@
       g_browser_process->download_request_limiter();
   const GURL& download_origin =
       download_request_limiter->GetDownloadOrigin(web_contents());
-  std::u16string display_host =
-      url_formatter::FormatUrlForSecurityDisplay(download_origin);
+
+  const UrlIdentity url_identity =
+      GetUrlIdentity(GetProfile(), download_origin);
+
   DCHECK(download_request_limiter);
 
   RadioGroup radio_group;
@@ -1437,13 +1449,14 @@
     case DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED:
       radio_group.radio_items = {
           l10n_util::GetStringUTF16(IDS_ALLOWED_DOWNLOAD_NO_ACTION),
-          l10n_util::GetStringFUTF16(IDS_ALLOWED_DOWNLOAD_BLOCK, display_host)};
+          l10n_util::GetStringFUTF16(IDS_ALLOWED_DOWNLOAD_BLOCK,
+                                     url_identity.name)};
       radio_group.default_item = kAllowButtonIndex;
       break;
     case DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED:
       radio_group.radio_items = {
           l10n_util::GetStringFUTF16(IDS_BLOCKED_DOWNLOAD_UNBLOCK,
-                                     display_host),
+                                     url_identity.name),
           l10n_util::GetStringUTF16(IDS_BLOCKED_DOWNLOAD_NO_ACTION)};
       radio_group.default_item = 1;
       break;
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 01ebae1..4173d07 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -59,6 +59,12 @@
 #include "services/device/public/cpp/test/fake_geolocation_manager.h"
 #endif
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_test_utils.h"
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 using content::WebContentsTester;
 using content_settings::PageSpecificContentSettings;
 using custom_handlers::ProtocolHandler;
@@ -1020,6 +1026,37 @@
   ASSERT_NE(std::u16string::npos, title.find(base::UTF8ToUTF16(file_url)));
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+class ContentSettingBubbleModelIsolatedWebAppTest
+    : public ContentSettingBubbleModelTest {
+ protected:
+  void InstallIsolatedWebApp(const std::string& app_name, const GURL& url) {
+    web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
+    web_app::AddDummyIsolatedAppToRegistry(profile(), url, app_name);
+  }
+};
+
+#include "chrome/browser/web_applications/web_app_provider.h"
+TEST_F(ContentSettingBubbleModelIsolatedWebAppTest, IsolatedWebAppUrl) {
+  const GURL app_url(
+      "isolated-app://"
+      "berugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic");
+  const std::string app_name("Test IWA Name");
+
+  InstallIsolatedWebApp(app_name, app_url);
+  NavigateAndCommit(app_url);
+  PageSpecificContentSettings::GetForFrame(
+      web_contents()->GetPrimaryMainFrame())
+      ->OnContentBlocked(ContentSettingsType::IMAGES);
+  std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model(
+      ContentSettingBubbleModel::CreateContentSettingBubbleModel(
+          nullptr, web_contents(), ContentSettingsType::IMAGES));
+  std::u16string title =
+      content_setting_bubble_model->bubble_content().radio_group.radio_items[0];
+  ASSERT_NE(std::u16string::npos, title.find(base::UTF8ToUTF16(app_name)));
+}
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 TEST_F(ContentSettingBubbleModelTest, RegisterProtocolHandler) {
   const GURL page_url("https://toplevel.example/");
   NavigateAndCommit(page_url);
diff --git a/chrome/browser/ui/global_media_controls/cast_device_list_host.cc b/chrome/browser/ui/global_media_controls/cast_device_list_host.cc
index 503004d..fe2f3715 100644
--- a/chrome/browser/ui/global_media_controls/cast_device_list_host.cc
+++ b/chrome/browser/ui/global_media_controls/cast_device_list_host.cc
@@ -163,7 +163,9 @@
 void CastDeviceListHost::StartCasting(const media_router::UIMediaSink& sink) {
   auto cast_mode = GetPreferredCastMode(sink.cast_modes, sink.icon_type);
   if (!cast_mode) {
-    NOTREACHED() << "Cast mode is not supported.";
+    // The UI calls this method asynchronously over Mojo, so by the time this
+    // gets called it's possible for the set of Cast modes to no longer be
+    // valid.
     return;
   }
   cast_controller_->StartCasting(sink.id, cast_mode.value());
diff --git a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
index 1f6da8f2..fb46c5cf 100644
--- a/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_icon_controller_unittest.cc
@@ -26,10 +26,13 @@
 #include "ash/constants/ash_features.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/user_manager/fake_user_manager.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
+
+using ash::standalone_browser::BrowserSupport;
 #endif
 
 namespace {
@@ -157,8 +160,7 @@
 // and severity when an upgrade is detected.
 TEST_P(AppMenuIconControllerTest, UpgradeNotification) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  auto set_lacros_enabled =
-      crosapi::browser_util::SetLacrosEnabledForTest(true);
+  auto set_lacros_enabled = BrowserSupport::SetLacrosEnabledForTest(true);
 #endif
 
   ::testing::StrictMock<MockAppMenuIconControllerDelegate> mock_delegate;
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index a2c89853..80f954b 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -35,8 +35,10 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
-#include "chrome/browser/ash/crosapi/browser_util.h"
+#include "chromeos/ash/components/standalone_browser/browser_support.h"
 #include "components/user_manager/fake_user_manager.h"
+
+using ash::standalone_browser::BrowserSupport;
 #endif
 
 namespace {
@@ -160,8 +162,7 @@
   EXPECT_TRUE(detector->notify_upgrade());
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  auto set_lacros_enabled =
-      crosapi::browser_util::SetLacrosEnabledForTest(true);
+  auto set_lacros_enabled = BrowserSupport::SetLacrosEnabledForTest(true);
 #endif
 
   FakeIconDelegate fake_delegate;
diff --git a/chrome/browser/ui/url_identity.cc b/chrome/browser/ui/url_identity.cc
index ec868fd..592e06b 100644
--- a/chrome/browser/ui/url_identity.cc
+++ b/chrome/browser/ui/url_identity.cc
@@ -11,10 +11,8 @@
 #include "base/types/expected.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/url_constants.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/url_formatter/elide_url.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -119,10 +117,7 @@
 
   return UrlIdentity{
       .type = Type::kFile,
-      // TODO(zelin): Rename IDS_PERMISSIONS_BUBBLE_PROMPT_THIS_FILE and move to
-      // "Generic terms"
-      .name =
-          l10n_util::GetStringUTF16(IDS_PERMISSIONS_BUBBLE_PROMPT_THIS_FILE),
+      .name = url_formatter::FormatUrlForSecurityDisplay(url),
   };
 }
 }  // namespace
diff --git a/chrome/browser/ui/url_identity_unittest.cc b/chrome/browser/ui/url_identity_unittest.cc
index be3bda3e..5cadf83 100644
--- a/chrome/browser/ui/url_identity_unittest.cc
+++ b/chrome/browser/ui/url_identity_unittest.cc
@@ -113,7 +113,7 @@
      {},
      {
          .type = Type::kFile,
-         .name = u"This file",
+         .name = u"file:///tmp/index.html",
      }},
   };
 
@@ -198,7 +198,7 @@
        {},
        {
            .type = Type::kFile,
-           .name = u"This file",
+           .name = u"file:///tmp/index.html",
        }},
   };
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
index 5ed1317b..ceeccde 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos_test_utils.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -43,7 +44,9 @@
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "ash/shell.h"
 #include "ash/wm/overview/overview_controller.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
+#include "ash/wm/tablet_mode/tablet_mode_multitask_menu_event_handler.h"
+#include "ash/wm/tablet_mode/tablet_mode_window_manager.h"
 #include "base/functional/callback_helpers.h"
 #include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
@@ -96,6 +99,7 @@
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_features.h"
+#include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/ui/base/chromeos_ui_constants.h"
 #include "chromeos/ui/base/window_pin_type.h"
@@ -1299,6 +1303,8 @@
   EXPECT_EQ(inset_normal, inset_in_overview_mode);
 }
 
+// TODO(b/270175923): Consider using WebUiTabStripOverrideTest, since it
+// makes sense for it to always be enabled.
 class FloatBrowserNonClientFrameViewChromeOSTest
     : public TopChromeMdParamTest<InProcessBrowserTest> {
  public:
@@ -1314,6 +1320,56 @@
       chromeos::wm::features::kWindowLayoutMenu};
 };
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+IN_PROC_BROWSER_TEST_P(FloatBrowserNonClientFrameViewChromeOSTest,
+                       TabletModeMultitaskMenu) {
+  ui::ScopedAnimationDurationScaleMode test_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+  ASSERT_NO_FATAL_FAILURE(
+      ash::ShellTestApi().SetTabletModeEnabledForTest(true));
+
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  Widget* widget = browser_view->GetWidget();
+  aura::Window* window = widget->GetNativeWindow();
+  ui::test::EventGenerator event_generator(window->GetRootWindow());
+
+  // A normal tap on the top center of the window and in the omnibox
+  // bounds will focus the omnibox.
+  const gfx::Rect omnibox_bounds =
+      browser_view->GetViewByID(VIEW_ID_OMNIBOX)->GetBoundsInScreen();
+  ASSERT_NO_FATAL_FAILURE(
+      event_generator.GestureTapAt(omnibox_bounds.top_center()));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::WaitForViewFocus(browser(), VIEW_ID_OMNIBOX, true));
+
+  // Swipe down from the top center opens the multitask menu.
+  event_generator.SetTouchRadius(10, 5);
+  const gfx::Point top_center(window->bounds().CenterPoint().x(), -1);
+  event_generator.PressTouch(top_center);
+  event_generator.MoveTouchBy(0, 100);
+  event_generator.ReleaseTouch();
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::WaitForViewFocus(browser(), VIEW_ID_OMNIBOX, false));
+  auto* multitask_menu_event_handler =
+      ash::TabletModeControllerTestApi()
+          .tablet_mode_window_manager()
+          ->tablet_mode_multitask_menu_event_handler();
+  EXPECT_TRUE(multitask_menu_event_handler->multitask_menu());
+
+  if (browser_view->webui_tab_strip()) {
+    // The tab strip doesn't get shown if the menu is.
+    ASSERT_FALSE(browser_view->webui_tab_strip()->GetVisible());
+  }
+
+  // Tap on the omnibox outside the menu takes focus and closes the menu.
+  ASSERT_NO_FATAL_FAILURE(
+      event_generator.GestureTapAt(omnibox_bounds.left_center()));
+  ASSERT_NO_FATAL_FAILURE(
+      ui_test_utils::WaitForViewFocus(browser(), VIEW_ID_OMNIBOX, true));
+  EXPECT_FALSE(multitask_menu_event_handler->multitask_menu());
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 IN_PROC_BROWSER_TEST_P(FloatBrowserNonClientFrameViewChromeOSTest,
                        BrowserHeaderVisibilityInTabletModeTest) {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_view.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_view.cc
index 1af672e8..3cdb4ef 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_view.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_view.cc
@@ -68,8 +68,17 @@
   DCHECK(!delegate.Requests().empty());
   GURL origin_url = delegate.GetRequestingOrigin();
 
-  return UrlIdentity::CreateFromUrl(browser ? browser->profile() : nullptr,
-                                    origin_url, allowed_types, options);
+  UrlIdentity url_identity =
+      UrlIdentity::CreateFromUrl(browser ? browser->profile() : nullptr,
+                                 origin_url, allowed_types, options);
+
+  if (url_identity.type == UrlIdentity::Type::kFile) {
+    // File URLs will show the same constant.
+    url_identity.name =
+        l10n_util::GetStringUTF16(IDS_PERMISSIONS_BUBBLE_PROMPT_THIS_FILE);
+  }
+
+  return url_identity;
 }
 
 // Determines whether the current request should also display an
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 4f2899bf..86096f1 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -49,6 +49,8 @@
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/controls/hover_button.h"
 #include "chrome/browser/ui/views/profiles/badged_profile_photo.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
@@ -159,11 +161,18 @@
   } else {
     CHECK(!profile->IsOffTheRecord());
     BuildIdentity();
-    BuildSyncInfo();
-    BuildAutofillButtons();
+
+    // Users should not be able to open chrome settings from WebApps.
+    if (!web_app::AppBrowserController::IsWebApp(browser())) {
+      BuildSyncInfo();
+      BuildAutofillButtons();
+    }
   }
 
-  BuildFeatureButtons();
+  // Users should not be able to use features from WebApps.
+  if (!web_app::AppBrowserController::IsWebApp(browser())) {
+    BuildFeatureButtons();
+  }
 
 //  ChromeOS doesn't support multi-profile.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -171,7 +180,11 @@
     SetProfileManagementHeading(
         l10n_util::GetStringUTF16(IDS_PROFILES_LIST_PROFILES_TITLE));
     BuildAvailableProfiles();
-    BuildProfileManagementFeatureButtons();
+
+    // Users should not be able to manage profiles from WebApps.
+    if (!web_app::AppBrowserController::IsWebApp(browser())) {
+      BuildProfileManagementFeatureButtons();
+    }
   }
 #endif
 }
@@ -391,8 +404,34 @@
   RecordClick(ActionableItem::kOtherProfileButton);
   if (!perform_menu_actions())
     return;
-  GetWidget()->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
-  profiles::SwitchToProfile(profile_path, /*always_create=*/false);
+
+  if (!web_app::AppBrowserController::IsWebApp(browser())) {
+    GetWidget()->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
+    profiles::SwitchToProfile(profile_path, /*always_create=*/false);
+  } else {
+#if !BUILDFLAG(IS_CHROMEOS)
+    // Open the same web app for another profile.
+    // So far the only allowlisted case is PasswordManager WebApp.
+    const web_app::AppId& app_id = browser()->app_controller()->app_id();
+    CHECK_EQ(app_id, web_app::kPasswordManagerAppId);
+
+    app_profile_switcher_.emplace(
+        app_id, *browser()->profile(),
+        base::BindOnce(
+            [](views::Widget* widget) {
+              widget->CloseWithReason(
+                  views::Widget::ClosedReason::kUnspecified);
+            },
+            // It's safe to use base::Unretained, because the profile
+            // switcher is onwned by ProfileMenuView and is destroyed
+            // before the widget is destroyed.
+            base::Unretained(GetWidget())));
+    app_profile_switcher_->SwitchToProfile(profile_path);
+#else
+    // WebApps are can only be installed for the main profile on ChromeOS.
+    NOTREACHED();
+#endif
+  }
 }
 
 void ProfileMenuView::OnAddNewProfileButtonClicked() {
@@ -449,11 +488,14 @@
 // Profile names are not supported on ChromeOS.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
   profile_name = profile_attributes->GetLocalProfileName();
-  edit_button_params = EditButtonParams(
-      &vector_icons::kEditIcon,
-      l10n_util::GetStringUTF16(IDS_PROFILES_CUSTOMIZE_PROFILE_BUTTON_TOOLTIP),
-      base::BindRepeating(&ProfileMenuView::OnEditProfileButtonClicked,
-                          base::Unretained(this)));
+  if (!web_app::AppBrowserController::IsWebApp(browser())) {
+    edit_button_params = EditButtonParams(
+        &vector_icons::kEditIcon,
+        l10n_util::GetStringUTF16(
+            IDS_PROFILES_CUSTOMIZE_PROFILE_BUTTON_TOOLTIP),
+        base::BindRepeating(&ProfileMenuView::OnEditProfileButtonClicked,
+                            base::Unretained(this)));
+  }
 #endif
 
   SkColor background_color =
@@ -690,7 +732,8 @@
                         profile_entries.size() > 1);
 
   if (!browser()->profile()->IsGuestSession() &&
-      profiles::IsGuestModeEnabled()) {
+      profiles::IsGuestModeEnabled() &&
+      !web_app::AppBrowserController::IsWebApp(browser())) {
     AddAvailableProfile(
         profiles::GetGuestAvatar(),
         l10n_util::GetStringUTF16(IDS_GUEST_PROFILE_NAME),
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.h b/chrome/browser/ui/views/profiles/profile_menu_view.h
index 7e205d46..b4a54bb 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.h
@@ -13,6 +13,7 @@
 
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/password_manager/web_app_profile_switcher.h"
 #include "chrome/browser/profiles/avatar_menu.h"
 #include "chrome/browser/profiles/avatar_menu_observer.h"
 #include "chrome/browser/sync/sync_ui_util.h"
@@ -97,6 +98,12 @@
 
   std::u16string menu_title_;
   std::u16string menu_subtitle_;
+
+#if !BUILDFLAG(IS_CHROMEOS)
+  // A profile switcher object needed if the user triggers opening other
+  // profile in a web app.
+  absl::optional<WebAppProfileSwitcher> app_profile_switcher_;
+#endif
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_MENU_VIEW_H_
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index beaf45e..008a82b 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -48,7 +48,14 @@
 #include "chrome/browser/ui/views/profiles/profile_menu_coordinator.h"
 #include "chrome/browser/ui/views/profiles/profile_menu_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_test_helper.h"
+#include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
+#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 #include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_test_utils.h"
+#include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -60,6 +67,7 @@
 #include "components/feature_engagement/public/tracker.h"
 #include "components/feature_engagement/test/test_tracker.h"
 #include "components/google/core/common/google_util.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/base/signin_pref_names.h"
@@ -139,6 +147,20 @@
   return feature_engagement::CreateTestTracker();
 }
 
+#if !BUILDFLAG(IS_CHROMEOS)
+
+const char kPasswordManagerPWAUrl[] = "chrome://password-manager/?source=pwa";
+
+std::unique_ptr<WebAppInstallInfo> CreatePasswordManagerWebAppInfo() {
+  auto web_app_info = std::make_unique<WebAppInstallInfo>();
+  web_app_info->start_url = GURL(kPasswordManagerPWAUrl);
+  web_app_info->title = u"Password Manager";
+  web_app_info->manifest_id = "";
+  return web_app_info;
+}
+
+#endif
+
 }  // namespace
 
 class ProfileMenuViewTestBase {
@@ -147,10 +169,12 @@
   void OpenProfileMenu() {
     BrowserView* browser_view =
         BrowserView::GetBrowserViewForBrowser(target_browser_);
+    OpenProfileMenuFromToolbar(browser_view->toolbar_button_provider());
+  }
 
+  void OpenProfileMenuFromToolbar(ToolbarButtonProvider* toolbar) {
     // Click the avatar button to open the menu.
-    views::View* avatar_button =
-        browser_view->toolbar_button_provider()->GetAvatarToolbarButton();
+    views::View* avatar_button = toolbar->GetAvatarToolbarButton();
     ASSERT_TRUE(avatar_button);
     Click(avatar_button);
 
@@ -765,6 +789,10 @@
   void SetUpInProcessBrowserTestFixture() override {
     test_signin_client_subscription_ =
         secondary_account_helper::SetUpSigninClient(&test_url_loader_factory_);
+
+    // Needed by ProfileMenuClickTest_PasswordManagerWebApp test.
+    feature_list_.InitAndEnableFeature(
+        password_manager::features::kPasswordManagerRedesign);
   }
 
   // SyncTest:
@@ -849,6 +877,7 @@
   base::HistogramTester histogram_tester_;
   std::unique_ptr<SyncServiceImplHarness> sync_harness_;
   raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 #define PROFILE_MENU_CLICK_TEST(actionable_item_list, test_case_name)     \
@@ -1224,3 +1253,81 @@
 
   RunTest();
 }
+
+#if !BUILDFLAG(IS_CHROMEOS)
+// List of actionable items in the correct order as they appear in the menu.
+// If a new button is added to the menu, it should also be added to this list.
+constexpr ProfileMenuViewBase::ActionableItem
+    kActionableItems_PasswordManagerWebApp[] = {
+        ProfileMenuViewBase::ActionableItem::kOtherProfileButton};
+
+PROFILE_MENU_CLICK_TEST(kActionableItems_PasswordManagerWebApp,
+                        ProfileMenuClickTest_PasswordManagerWebApp) {
+  // Add an additional profile.
+  CreateAdditionalProfile();
+
+  // Install and launch an application for the first profile.
+  WebAppFrameToolbarTestHelper toolbar_helper;
+  web_app::AppId app_id = toolbar_helper.InstallAndLaunchCustomWebApp(
+      browser(), CreatePasswordManagerWebAppInfo(),
+      GURL(kPasswordManagerPWAUrl));
+  SetTargetBrowser(toolbar_helper.app_browser());
+  RunTest();
+}
+
+class ProfileMenuViewWebAppTest : public ProfileMenuViewTestBase,
+                                  public web_app::WebAppControllerBrowserTest {
+ protected:
+  void SetUp() override {
+    // Enable the installable PasswordManager WebUI.
+    feature_list_.InitAndEnableFeature(
+        password_manager::features::kPasswordManagerRedesign);
+    web_app::WebAppControllerBrowserTest::SetUp();
+  }
+
+  WebAppFrameToolbarTestHelper* toolbar_helper() {
+    return &web_app_frame_toolbar_helper_;
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  WebAppFrameToolbarTestHelper web_app_frame_toolbar_helper_;
+};
+
+IN_PROC_BROWSER_TEST_F(ProfileMenuViewWebAppTest, SelectingOtherProfile) {
+  // Create a second profile.
+  Profile* second_profile = CreateAdditionalProfile();
+  web_app::test::WaitUntilWebAppProviderAndSubsystemsReady(
+      web_app::WebAppProvider::GetForTest(second_profile));
+  ASSERT_FALSE(chrome::FindBrowserWithProfile(second_profile));
+
+  // Install and launch an application for the first profile.
+  web_app::AppId app_id = toolbar_helper()->InstallAndLaunchCustomWebApp(
+      browser(), CreatePasswordManagerWebAppInfo(),
+      GURL(kPasswordManagerPWAUrl));
+  SetTargetBrowser(toolbar_helper()->app_browser());
+
+  // Open profile menu.
+  auto* toolbar =
+      toolbar_helper()->browser_view()->web_app_frame_toolbar_for_testing();
+  ASSERT_TRUE(toolbar);
+  OpenProfileMenuFromToolbar(toolbar);
+
+  // Select other profile by advancing the focus one step forward
+  profile_menu_view()->GetFocusManager()->AdvanceFocus(/*reverse=*/false);
+  auto* focused_item = profile_menu_view()->GetFocusManager()->GetFocusedView();
+  ASSERT_TRUE(focused_item);
+
+  // Wait for the new app window to be open for the second profile.
+  ui_test_utils::AllBrowserTabAddedWaiter waiter;
+  Click(focused_item);
+  content::WebContents* new_web_contents = waiter.Wait();
+  ASSERT_TRUE(new_web_contents);
+  Browser* new_browser = chrome::FindBrowserWithProfile(second_profile);
+  ASSERT_TRUE(new_browser);
+  EXPECT_TRUE(new_browser->is_type_app());
+  EXPECT_EQ(new_browser->tab_strip_model()->GetActiveWebContents(),
+            new_web_contents);
+  EXPECT_EQ(new_web_contents->GetVisibleURL(), GURL(kPasswordManagerPWAUrl));
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
index 063a9ad6..5e25417 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
@@ -245,7 +245,7 @@
 }
 
 AvatarToolbarButton* WebAppFrameToolbarView::GetAvatarToolbarButton() {
-  return nullptr;
+  return right_container_ ? right_container_->avatar_button() : nullptr;
 }
 
 ToolbarButton* WebAppFrameToolbarView::GetBackButton() {
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
index 3138e43..97cc8ee7 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
@@ -165,6 +165,16 @@
     views::SetHitTestComponent(download_button_, static_cast<int>(HTCLIENT));
   }
 
+#if !BUILDFLAG(IS_CHROMEOS)
+  if (app_controller->HasProfileMenuButton()) {
+    avatar_button_ =
+        AddChildView(std::make_unique<AvatarToolbarButton>(browser_view_));
+    avatar_button_->SetID(VIEW_ID_AVATAR_BUTTON);
+    ConfigureWebAppToolbarButton(avatar_button_, toolbar_button_provider_);
+    views::SetHitTestComponent(avatar_button_, static_cast<int>(HTCLIENT));
+  }
+#endif
+
   if (app_controller->HasTitlebarMenuButton()) {
     web_app_menu_button_ =
         AddChildView(std::make_unique<WebAppMenuButton>(browser_view_));
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h
index 4668ba5..00deb04 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_container.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "chrome/browser/ui/views/profiles/avatar_toolbar_button.h"
 #include "chrome/browser/ui/web_applications/web_app_menu_model.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -85,6 +86,8 @@
     return window_controls_overlay_toggle_button_;
   }
 
+  AvatarToolbarButton* avatar_button() { return avatar_button_; }
+
   static void DisableAnimationForTesting();
 
  private:
@@ -155,6 +158,7 @@
   raw_ptr<WebAppMenuButton> web_app_menu_button_ = nullptr;
   raw_ptr<SystemAppAccessibleName> system_app_accessible_name_ = nullptr;
   raw_ptr<DownloadToolbarButtonView> download_button_ = nullptr;
+  raw_ptr<AvatarToolbarButton> avatar_button_ = nullptr;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_FRAME_TOOLBAR_WEB_APP_TOOLBAR_BUTTON_CONTAINER_H_
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc
index e6384c6e..230d9c26 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -296,6 +296,12 @@
   return true;
 }
 
+#if !BUILDFLAG(IS_CHROMEOS)
+bool AppBrowserController::HasProfileMenuButton() const {
+  return false;
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 const ash::SystemWebAppDelegate* AppBrowserController::system_app() const {
   return nullptr;
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.h b/chrome/browser/ui/web_applications/app_browser_controller.h
index b80bff9..04c4a088 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/app_browser_controller.h
@@ -202,6 +202,13 @@
   // Whether the browser should show the reload button in the toolbar.
   virtual bool HasReloadButton() const;
 
+#if !BUILDFLAG(IS_CHROMEOS)
+  // Whether the browser should show the profile menu button in the toolbar.
+  // Not appliccable to ChromeOS, because apps can be installed only for
+  // one main profile there.
+  virtual bool HasProfileMenuButton() const;
+#endif  // !BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Returns the SystemWebAppDelegate if any for this controller.
   virtual const ash::SystemWebAppDelegate* system_app() const;
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index cb00198..90433e7e1 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -31,11 +31,13 @@
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_icon_manager.h"
 #include "chrome/browser/web_applications/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
@@ -223,6 +225,14 @@
   return true;
 }
 
+#if !BUILDFLAG(IS_CHROMEOS)
+bool WebAppBrowserController::HasProfileMenuButton() const {
+  return (app_id() == web_app::kPasswordManagerAppId) &&
+         base::FeatureList::IsEnabled(
+             password_manager::features::kPasswordManagerRedesign);
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 const ash::SystemWebAppDelegate* WebAppBrowserController::system_app() const {
   return system_app_;
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.h b/chrome/browser/ui/web_applications/web_app_browser_controller.h
index c7d61726..fd5724d 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.h
@@ -105,6 +105,9 @@
   void SetIsolatedWebAppTrueForTesting() override;
   gfx::Rect GetDefaultBounds() const override;
   bool HasReloadButton() const override;
+#if !BUILDFLAG(IS_CHROMEOS)
+  bool HasProfileMenuButton() const override;
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   const ash::SystemWebAppDelegate* system_app() const override;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
index 2d8d70b..0477dcc2 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
@@ -22,14 +22,4 @@
   return file_system_context->CrackURLInFirstPartyContext(url);
 }
 
-void CreateDirectoryOnIOThread(
-    scoped_refptr<storage::FileSystemContext> file_system_context,
-    storage::FileSystemURL destination_folder_url,
-    base::OnceCallback<void(base::File::Error)> complete_callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  file_system_context->operation_runner()->CreateDirectory(
-      destination_folder_url, /*exclusive=*/false, /*recursive=*/false,
-      std::move(complete_callback));
-}
-
 }  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
index 0cd5b19..16a6552 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
@@ -16,9 +16,6 @@
 
 namespace ash::cloud_upload {
 
-// The default folder where the file should be uploaded.
-const char kDestinationFolder[] = "from Chromebook";
-
 // Converts an absolute FilePath into a filesystem URL.
 storage::FileSystemURL FilePathToFileSystemURL(
     Profile* profile,
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
index 19de7773..e19aa0d 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
@@ -128,8 +128,7 @@
 
   // Destination url.
   base::FilePath destination_folder_path =
-      drive_integration_service_->GetMountPointPath().Append("root").Append(
-          kDestinationFolder);
+      drive_integration_service_->GetMountPointPath().Append("root");
   FileSystemURL destination_folder_url = FilePathToFileSystemURL(
       profile_, file_system_context_, destination_folder_path);
   // TODO (b/243095484) Define error behavior.
@@ -138,13 +137,14 @@
     return;
   }
 
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&CreateDirectoryOnIOThread, file_system_context_,
-                     destination_folder_url,
-                     google_apis::CreateRelayCallback(base::BindOnce(
-                         &DriveUploadHandler::OnDestinationDirectoryCreated,
-                         this, destination_folder_url))));
+  std::vector<FileSystemURL> source_urls{source_url_};
+  std::unique_ptr<file_manager::io_task::IOTask> task =
+      std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
+          file_manager::io_task::OperationType::kMove, std::move(source_urls),
+          std::move(destination_folder_url), profile_, file_system_context_,
+          /*show_notification=*/false);
+
+  observed_task_id_ = io_task_controller_->Add(std::move(task));
 }
 
 void DriveUploadHandler::UpdateProgressNotification() {
@@ -174,28 +174,6 @@
   }
 }
 
-void DriveUploadHandler::OnDestinationDirectoryCreated(
-    FileSystemURL destination_folder_url,
-    base::File::Error error) {
-  if (error != base::File::FILE_OK) {
-    OnEndUpload(GURL(), "Unable to create destination folder");
-    return;
-  }
-  if (!destination_folder_url.is_valid()) {
-    OnEndUpload(GURL(), "Received destination URL is invalid");
-    return;
-  }
-
-  std::vector<FileSystemURL> source_urls{source_url_};
-  std::unique_ptr<file_manager::io_task::IOTask> task =
-      std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
-          file_manager::io_task::OperationType::kMove, std::move(source_urls),
-          std::move(destination_folder_url), profile_, file_system_context_,
-          /*show_notification=*/false);
-
-  observed_task_id_ = io_task_controller_->Add(std::move(task));
-}
-
 void DriveUploadHandler::OnIOTaskStatus(
     const file_manager::io_task::ProgressStatus& status) {
   if (status.task_id != observed_task_id_) {
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
index 3f1593759..4d055cc1 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
@@ -59,10 +59,6 @@
   // Ends upload and runs Upload callback.
   void OnEndUpload(GURL hosted_url, std::string error_message = "");
 
-  void OnDestinationDirectoryCreated(
-      storage::FileSystemURL destination_folder_url,
-      base::File::Error error);
-
   // IOTaskController::Observer:
   void OnIOTaskStatus(
       const ::file_manager::io_task::ProgressStatus& status) override;
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc
index fefc1199..cb783c2 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc
@@ -168,8 +168,7 @@
         drive::DriveIntegrationServiceFactory::FindForProfile(profile());
     base::FilePath observed_relative_drive_path;
     drive_integration_service->GetRelativeDrivePath(
-        drive_root_dir_.AppendASCII(kDestinationFolder)
-            .AppendASCII(test_file_name_),
+        drive_root_dir_.AppendASCII(test_file_name_),
         &observed_relative_drive_path);
     return observed_relative_drive_path;
   }
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
index 07e1dbb..9948ada 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
@@ -106,8 +106,7 @@
     OnEndUpload(FileSystemURL(), error_message);
     return;
   }
-  base::FilePath destination_folder_path =
-      file_systems[0].mount_path().Append(kDestinationFolder);
+  base::FilePath destination_folder_path = file_systems[0].mount_path();
   FileSystemURL destination_folder_url = FilePathToFileSystemURL(
       profile_, file_system_context_, destination_folder_path);
   // TODO (b/243095484) Define error behavior.
@@ -116,13 +115,14 @@
     return;
   }
 
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&CreateDirectoryOnIOThread, file_system_context_,
-                     destination_folder_url,
-                     google_apis::CreateRelayCallback(base::BindOnce(
-                         &OneDriveUploadHandler::OnDestinationDirectoryCreated,
-                         this, destination_folder_url))));
+  std::vector<FileSystemURL> source_urls{source_url_};
+  std::unique_ptr<file_manager::io_task::IOTask> task =
+      std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
+          file_manager::io_task::OperationType::kMove, std::move(source_urls),
+          std::move(destination_folder_url), profile_, file_system_context_,
+          /*show_notification=*/false);
+
+  observed_task_id_ = io_task_controller_->Add(std::move(task));
 }
 
 void OneDriveUploadHandler::OnEndUpload(const FileSystemURL& uploaded_file_url,
@@ -141,28 +141,6 @@
   }
 }
 
-void OneDriveUploadHandler::OnDestinationDirectoryCreated(
-    FileSystemURL destination_folder_url,
-    base::File::Error error) {
-  if (error != base::File::FILE_OK) {
-    OnEndUpload(FileSystemURL(), "Unable to create destination folder");
-    return;
-  }
-  if (!destination_folder_url.is_valid()) {
-    OnEndUpload(FileSystemURL(), "Received destination URL is invalid");
-    return;
-  }
-
-  std::vector<FileSystemURL> source_urls{source_url_};
-  std::unique_ptr<file_manager::io_task::IOTask> task =
-      std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
-          file_manager::io_task::OperationType::kMove, std::move(source_urls),
-          std::move(destination_folder_url), profile_, file_system_context_,
-          /*show_notification=*/false);
-
-  observed_task_id_ = io_task_controller_->Add(std::move(task));
-}
-
 void OneDriveUploadHandler::OnIOTaskStatus(
     const file_manager::io_task::ProgressStatus& status) {
   if (status.task_id != observed_task_id_) {
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
index 6c466ec0..719ae56 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
@@ -55,10 +55,6 @@
   void OnEndUpload(const storage::FileSystemURL& uploaded_file_url,
                    std::string error_message = "");
 
-  void OnDestinationDirectoryCreated(
-      storage::FileSystemURL destination_folder_url,
-      base::File::Error error);
-
   // IOTaskController::Observer:
   void OnIOTaskStatus(
       const ::file_manager::io_task::ProgressStatus& status) override;
diff --git a/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc
index 214396fa..dd29e0bf85 100644
--- a/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.cc
@@ -64,6 +64,8 @@
     {"setupSucceededPageMessage",
      IDS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_MESSAGE},
     {"startSetupPageHeader", IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_HEADER},
+    {"startSetupPageAfterQuickStartHeader",
+     IDS_MULTIDEVICE_SETUP_START_SETUP_PAGE_AFTER_QUICK_START_HEADER},
     {"tryAgain", IDS_MULTIDEVICE_SETUP_TRY_AGAIN_LABEL},
     {"dialogAccessibilityTitle",
      IDS_MULTIDEVICE_SETUP_DIALOG_ACCESSIBILITY_TITLE},
diff --git a/chrome/browser/ui/webui/ash/network_ui.cc b/chrome/browser/ui/webui/ash/network_ui.cc
index 66bb2f5..0b95deba 100644
--- a/chrome/browser/ui/webui/ash/network_ui.cc
+++ b/chrome/browser/ui/webui/ash/network_ui.cc
@@ -881,6 +881,13 @@
   localized_strings.Set(
       "NetworkDiagnosticsSendFeedback",
       l10n_util::GetStringUTF16(IDS_NETWORK_DIAGNOSTICS_SEND_FEEDBACK));
+  localized_strings.Set("renderNetworkSelectButtonText",
+                        l10n_util::GetStringUTF16(
+                            IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_TEXT));
+  localized_strings.Set(
+      "renderNetworkSelectButtonDescription",
+      l10n_util::GetStringUTF16(
+          IDS_NETWORK_UI_RENDER_NETWORK_SELECT_BUTTON_DESCRIPTION));
 
   // Network Metrics
   localized_strings.Set(
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc
index 7ab9780..ee9eb71e 100644
--- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc
+++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc
@@ -32,6 +32,15 @@
   receiver_.Bind(std::move(receiver));
 }
 
+void InputDeviceSettingsProvider::SetKeyboardSettings(
+    uint32_t device_id,
+    ::ash::mojom::KeyboardSettingsPtr settings) {
+  DCHECK(features::IsInputDeviceSettingsSplitEnabled());
+  DCHECK(InputDeviceSettingsController::Get());
+  InputDeviceSettingsController::Get()->SetKeyboardSettings(
+      device_id, std::move(settings));
+}
+
 void InputDeviceSettingsProvider::GetConnectedKeyboards(
     GetConnectedKeyboardsCallback callback) {
   DCHECK(features::IsInputDeviceSettingsSplitEnabled());
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h
index 8bf4927..f435ec94 100644
--- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h
+++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h
@@ -39,6 +39,8 @@
       override;
   void ObserveMouseSettings(
       mojo::PendingRemote<mojom::MouseSettingsObserver> observer) override;
+  void SetKeyboardSettings(uint32_t device_id,
+                           ::ash::mojom::KeyboardSettingsPtr settings) override;
 
   // InputDeviceSettingsController::Observer:
   void OnKeyboardConnected(const ::ash::mojom::Keyboard& keyboard) override;
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom
index 93019b0..52c7b427 100644
--- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom
+++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom
@@ -59,4 +59,6 @@
   // The observer which is registered is immediately informed
   // of the current state via its ObserveMouseSettings function.
   ObserveMouseSettings(pending_remote<MouseSettingsObserver> observer);
+  // Sets the keyboard settings based on its id.
+  SetKeyboardSettings(uint32 device_id, ash.mojom.KeyboardSettings settings);
 };
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
index 521dc54..1cbc38ff 100644
--- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
+++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
@@ -205,7 +205,9 @@
   }
   void SetKeyboardSettings(
       DeviceId id,
-      ::ash::mojom::KeyboardSettingsPtr settings) override {}
+      ::ash::mojom::KeyboardSettingsPtr settings) override {
+    ++num_times_set_keyboard_settings_called_;
+  }
   void AddObserver(Observer* observer) override { observer_ = observer; }
   void RemoveObserver(Observer* observer) override { observer_ = nullptr; }
   void SetTouchpadSettings(
@@ -280,6 +282,9 @@
     pointing_sticks_.erase(iter);
     observer_->OnPointingStickDisconnected(*temp_pointing_stick);
   }
+  int num_times_set_keyboard_settings_called() {
+    return num_times_set_keyboard_settings_called_;
+  }
 
  private:
   std::vector<::ash::mojom::KeyboardPtr> keyboards_;
@@ -287,6 +292,7 @@
   std::vector<::ash::mojom::MousePtr> mice_;
   std::vector<::ash::mojom::PointingStickPtr> pointing_sticks_;
   raw_ptr<InputDeviceSettingsController::Observer> observer_ = nullptr;
+  int num_times_set_keyboard_settings_called_ = 0;
 };
 
 }  // namespace
@@ -332,6 +338,20 @@
                      CloneMojomVector(expected_keyboards)));
 }
 
+TEST_F(InputDeviceSettingsProviderTest, TestSetKeyboardSettings) {
+  controller_->AddKeyboard(kKeyboard1.Clone());
+  provider_->SetKeyboardSettings(kKeyboard1.id, kKeyboard1.settings->Clone());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, controller_->num_times_set_keyboard_settings_called());
+
+  controller_->AddKeyboard(kKeyboard2.Clone());
+  provider_->SetKeyboardSettings(kKeyboard2.id, kKeyboard1.settings->Clone());
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, controller_->num_times_set_keyboard_settings_called());
+}
+
 TEST_F(InputDeviceSettingsProviderTest, TestKeyboardSettingsObeserver) {
   std::vector<::ash::mojom::KeyboardPtr> expected_keyboards;
   expected_keyboards.push_back(kKeyboard1.Clone());
diff --git a/chrome/browser/web_applications/web_app_id_constants.h b/chrome/browser/web_applications/web_app_id_constants.h
index 14cdd28..20bc08a 100644
--- a/chrome/browser/web_applications/web_app_id_constants.h
+++ b/chrome/browser/web_applications/web_app_id_constants.h
@@ -182,6 +182,11 @@
     "jalmdcokfklmaoadompgacjlcomfckcf";
 #endif  // !defined(OFFICIAL_BUILD)
 
+// Generated as: web_app::GenerateAppId(/*manifest_id=*/"", GURL(
+//     "chrome://password-manager/?source=pwa"))
+inline constexpr char kPasswordManagerAppId[] =
+    "kajebgjangihfbkjfejcanhanjmmbcfd";
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_ID_CONSTANTS_H_
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 726d2c59..104c38a 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1678298389-d815960de4637eeb8aefb8ae470bcd9efe174dc1.profdata
+chrome-linux-main-1678341543-c012d31a6546d0364cb5ce175b7946ece282aeb7.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 250b38c..6b5051a1 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1678312782-69ec178d299e07fd1f5efc2554323539df4098cd.profdata
+chrome-mac-arm-main-1678348492-3a34c7456f984460d4d126810a57d0ef04b2c467.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index d9922e0f..711d28dc 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1678276355-5ffd8a4dc9c5520113500ce5af5ea8756214319e.profdata
+chrome-mac-main-1678341543-f3497436ff145006389766298ca4b0ed4e9ba569.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index dd4f835..c9f5f7b 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1678309150-6adc10d823bccf625ce70c53fe39cf9939693858.profdata
+chrome-win32-main-1678341543-ff6dce6eebcd2e22c05ded73e68a20539365b004.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5d0b20dc..83c7bbc7 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1678309150-b944feaf3fd50d8bc1f4610ded1d0658833c4699.profdata
+chrome-win64-main-1678341543-6410cc9803997341b7c3c01c7e2ad4047939ec4f.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4c97281..cf8ef75 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7694,6 +7694,7 @@
       "//chromeos/ash/components/multidevice:test_support",
       "//chromeos/ash/components/proximity_auth",
       "//chromeos/ash/components/proximity_auth:test_support",
+      "//chromeos/ash/components/standalone_browser:standalone_browser",
       "//chromeos/ash/components/string_matching",
       "//chromeos/ash/components/sync_wifi",
       "//chromeos/ash/components/system",
diff --git a/chrome/test/data/prerender_with_opt_in_header.html b/chrome/test/data/prerender_with_opt_in_header.html
new file mode 100644
index 0000000..ca36b37
--- /dev/null
+++ b/chrome/test/data/prerender_with_opt_in_header.html
@@ -0,0 +1,4 @@
+<html>
+  <head></head>
+  <body>This page has no title.</body>
+</html>
diff --git a/chrome/test/data/prerender_with_opt_in_header.html.mock-http-headers b/chrome/test/data/prerender_with_opt_in_header.html.mock-http-headers
new file mode 100644
index 0000000..17d81e41
--- /dev/null
+++ b/chrome/test/data/prerender_with_opt_in_header.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Supports-Loading-Mode: credentialed-prerender
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 12ccfa6..1827465 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
@@ -525,6 +525,42 @@
     assertFalse(report.feedbackContext.fromAssistant);
   });
 
+  /**
+   * Test that when when the send button is clicked, an on-continue is fired.
+   * Case 11: Share autofill metadata.
+   */
+  test('SendAutofillMetadataChecked', async () => {
+    await initializePage();
+    page.feedbackContext = fakeInternalUserFeedbackContext;
+    page.feedbackContext.fromAutofill = true;
+
+    assertTrue(isVisible(getElement('#autofillCheckboxContainer')));
+    getElement('#autofillCheckbox').checked = true;
+
+    const request = (await clickSendAndWait(page)).report;
+
+    assertTrue(!!request.feedbackContext.autofillMetadata);
+    assertTrue(request.includeAutofillMetadata);
+  });
+
+  /**
+   * Test that when when the send button is clicked, an on-continue is fired.
+   * Case 12: Do not share autofill metadata.
+   */
+  test('NotSendAutofillMetadataChecked', async () => {
+    await initializePage();
+    page.feedbackContext = fakeInternalUserFeedbackContext;
+    page.feedbackContext.fromAutofill = true;
+
+    assertTrue(isVisible(getElement('#autofillCheckboxContainer')));
+    getElement('#autofillCheckbox').checked = false;
+
+    const request = (await clickSendAndWait(page)).report;
+
+    assertFalse(!!request.feedbackContext.autofillMetadata);
+    assertFalse(request.includeAutofillMetadata);
+  });
+
   // Test that the send button will be disabled once clicked.
   test('DisableSendButtonAfterClick', async () => {
     await initializePage();
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index af0fe7f..e41a757 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -180,7 +180,7 @@
 base::RepeatingCallback<bool(const std::string&)> MatchAppPriority(
     const std::string& app_id,
     UpdateService::Priority priority) {
-  return base::BindLambdaForTesting([&app_id, priority](
+  return base::BindLambdaForTesting([app_id, priority](
                                         const std::string& request_body) {
     const bool is_match = [&app_id, priority, &request_body]() {
       const absl::optional<base::Value> doc =
diff --git a/chromeos/ash/components/drivefs/drivefs_pin_manager.cc b/chromeos/ash/components/drivefs/drivefs_pin_manager.cc
index 0b9f7ea1..142949d 100644
--- a/chromeos/ash/components/drivefs/drivefs_pin_manager.cc
+++ b/chromeos/ash/components/drivefs/drivefs_pin_manager.cc
@@ -13,6 +13,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
+#include "base/notreached.h"
 #include "base/task/sequenced_task_runner.h"
 #include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 #include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
@@ -29,7 +30,23 @@
 using Path = PinManager::Path;
 
 bool InProgress(const Stage stage) {
-  return stage > Stage::kNotStarted && stage < Stage::kSuccess;
+  switch (stage) {
+    case Stage::kGettingFreeSpace:
+    case Stage::kListingFiles:
+    case Stage::kSyncing:
+      return true;
+
+    case Stage::kNotStarted:
+    case Stage::kPaused:
+    case Stage::kSuccess:
+    case Stage::kStopped:
+    case Stage::kCannotGetFreeSpace:
+    case Stage::kCannotListFiles:
+    case Stage::kNotEnoughSpace:
+      return false;
+  }
+
+  NOTREACHED_NORETURN() << "Unexpected Stage " << stage;
 }
 
 int Percentage(const int64_t a, const int64_t b) {
@@ -234,6 +251,18 @@
              << " " << Quote(e.path) << "}";
 }
 
+ostream& operator<<(ostream& out, Quoter<ash::NetworkState> q) {
+  const auto& ip = q.value.GetIpAddress();
+  return out << "{type: " << q.value.type()
+             << ", device: " << q.value.device_path()
+             << ", guid: " << q.value.guid()
+             << ", ip: " << (ip.empty() ? "(none)" : ip)
+             << ", connection_state: " << q.value.connection_state()
+             << ", portal_state: " << q.value.GetPortalState()
+             << ", connected: " << q.value.IsConnectedState()
+             << ", online: " << q.value.IsOnline() << "}";
+}
+
 // Rounds the given size to the next multiple of 4-KB.
 int64_t RoundToBlockSize(int64_t size) {
   const int64_t block_size = 4 << 10;  // 4 KB
@@ -294,6 +323,7 @@
   case Stage::k##s: \
     return out << #s;
     PRINT(NotStarted)
+    PRINT(Paused)
     PRINT(GettingFreeSpace)
     PRINT(ListingFiles)
     PRINT(Syncing)
@@ -569,13 +599,22 @@
 
 void PinManager::Start() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!InProgress(progress_.stage)) << "Pin manager is " << progress_.stage;
+
+  if (InProgress(progress_.stage)) {
+    LOG(ERROR) << "Pin manager is already started: " << progress_.stage;
+    return;
+  }
 
   progress_ = {};
   files_to_pin_.clear();
   files_to_track_.clear();
   DCHECK_EQ(progress_.syncing_files, 0);
 
+  if (!is_online_) {
+    LOG(WARNING) << "Device is currently offline";
+    return Complete(Stage::kPaused);
+  }
+
   VLOG(2) << "Getting free space...";
   timer_ = base::ElapsedTimer();
   progress_.stage = Stage::kGettingFreeSpace;
@@ -598,15 +637,8 @@
 void PinManager::Enable(bool enabled) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (enabled == InProgress(progress_.stage)) {
-    VLOG(1) << "Pin manager is already " << (enabled ? "enabled" : "disabled");
-    return;
-  }
-
   if (enabled) {
-    VLOG(1) << "Starting";
     Start();
-    VLOG(1) << "Started";
   } else {
     Stop();
   }
@@ -614,6 +646,7 @@
 
 void PinManager::OnFreeSpaceRetrieved1(const int64_t free_space) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(progress_.stage, Stage::kGettingFreeSpace);
 
   if (free_space < 0) {
     LOG(ERROR) << "Cannot get free space: " << free_space;
@@ -668,6 +701,7 @@
     const drive::FileError error,
     const absl::optional<std::vector<mojom::QueryItemPtr>> items) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(progress_.stage, Stage::kListingFiles);
 
   if (error != drive::FILE_ERROR_OK || !items) {
     LOG(ERROR) << "Cannot list files: " << error;
@@ -707,6 +741,10 @@
       VLOG(1) << "Finished with success";
       break;
 
+    case Stage::kPaused:
+      VLOG(1) << "Paused";
+      break;
+
     case Stage::kStopped:
       VLOG(1) << "Stopped";
       break;
@@ -1162,9 +1200,7 @@
   DCHECK(network_state_handler_);
   network_state_handler_->AddObserver(this, FROM_HERE);
 
-  const NetworkState* const network = network_state_handler_->DefaultNetwork();
-  portal_state_ = network ? network->GetPortalState() : PortalState::kUnknown;
-  VLOG(1) << "Network is " << portal_state_;
+  DefaultNetworkChanged(network_state_handler_->DefaultNetwork());
 }
 
 void PinManager::OnShuttingDown() {
@@ -1174,12 +1210,62 @@
   network_state_handler_ = nullptr;
 }
 
-void PinManager::PortalStateChanged(
-    [[maybe_unused]] const NetworkState* const network,
-    const PortalState state) {
+void PinManager::DefaultNetworkChanged(const NetworkState* const network) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  VLOG(1) << "Network changed from " << portal_state_ << " to " << state;
-  portal_state_ = state;
+
+  if (network) {
+    VLOG(1) << "Default network changed to " << Quote(*network);
+    is_online_ = network->IsOnline();
+  } else {
+    VLOG(1) << "Default network changed to no network";
+    is_online_ = false;
+  }
+
+  if (!is_online_ && InProgress(progress_.stage)) {
+    VLOG(1) << "Going offline...";
+    return Complete(Stage::kPaused);
+  }
+
+  if (is_online_ && progress_.stage == Stage::kPaused) {
+    VLOG(1) << "Coming back online...";
+    return Start();
+  }
+}
+
+void PinManager::PortalStateChanged(const NetworkState* const network,
+                                    const PortalState portal_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (network) {
+    DCHECK_EQ(portal_state, network->GetPortalState());
+    VLOG(2) << "Network portal state changed to " << Quote(*network);
+  } else {
+    DCHECK_EQ(portal_state, PortalState::kUnknown);
+    VLOG(2) << "Network portal state changed to no network";
+  }
+}
+
+void PinManager::ActiveNetworksChanged(
+    const std::vector<const NetworkState*>& networks) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  switch (networks.size()) {
+    case 0:
+      VLOG(2) << "There are no active networks";
+      break;
+
+    case 1:
+      VLOG(2) << "There is 1 active network";
+      break;
+
+    default:
+      VLOG(2) << "There are " << networks.size() << " active networks";
+  }
+
+  int i = 0;
+  for (const NetworkState* const network : networks) {
+    DCHECK(network);
+    VLOG(2) << "Network #" << i++ << ": " << Quote(*network);
+  }
 }
 
 }  // namespace drivefs::pinning
diff --git a/chromeos/ash/components/drivefs/drivefs_pin_manager.h b/chromeos/ash/components/drivefs/drivefs_pin_manager.h
index 23622a50..daa6b3e63 100644
--- a/chromeos/ash/components/drivefs/drivefs_pin_manager.h
+++ b/chromeos/ash/components/drivefs/drivefs_pin_manager.h
@@ -46,6 +46,9 @@
   // Initial stage.
   kNotStarted,
 
+  // Paused because of unfavorable network conditions.
+  kPaused,
+
   // In-progress stages.
   kGettingFreeSpace,
   kListingFiles,
@@ -370,9 +373,14 @@
 
   // Called by the NetworkStateHandler to signal that the network conditions
   // have changed.
+  void DefaultNetworkChanged(const NetworkState* network) override;
+
   void PortalStateChanged(const NetworkState* default_network,
                           PortalState portal_state) override;
 
+  void ActiveNetworksChanged(
+      const std::vector<const NetworkState*>& active_networks) override;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   const Path profile_path_ GUARDED_BY_CONTEXT(sequence_checker_);
@@ -383,8 +391,9 @@
   raw_ptr<NetworkStateHandler> network_state_handler_
       GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
 
-  // Network condition. By default, and for tests, assume it is online.
-  PortalState portal_state_ = PortalState::kOnline;
+  // Is the device connected to a suitable network? Assume it is online for
+  // tests.
+  bool is_online_ GUARDED_BY_CONTEXT(sequence_checker_) = true;
 
   // Should the feature actually pin files, or should it stop after checking the
   // space requirements?
diff --git a/chromeos/ash/components/standalone_browser/browser_support.cc b/chromeos/ash/components/standalone_browser/browser_support.cc
index 2a840d2..0a758d4 100644
--- a/chromeos/ash/components/standalone_browser/browser_support.cc
+++ b/chromeos/ash/components/standalone_browser/browser_support.cc
@@ -5,6 +5,7 @@
 #include "chromeos/ash/components/standalone_browser/browser_support.h"
 
 #include "base/check_op.h"
+#include "base/logging.h"
 
 namespace ash::standalone_browser {
 namespace {
@@ -44,4 +45,18 @@
   return g_instance;
 }
 
+// static
+base::AutoReset<bool> BrowserSupport::SetLacrosEnabledForTest(
+    bool force_enabled) {
+  return base::AutoReset<bool>(&lacros_enabled_for_test_, force_enabled);
+}
+
+// static
+bool BrowserSupport::GetLacrosEnabledForTest() {
+  return lacros_enabled_for_test_;
+}
+
+// static
+bool BrowserSupport::lacros_enabled_for_test_ = false;
+
 }  // namespace ash::standalone_browser
diff --git a/chromeos/ash/components/standalone_browser/browser_support.h b/chromeos/ash/components/standalone_browser/browser_support.h
index a4838a5..934ce33d 100644
--- a/chromeos/ash/components/standalone_browser/browser_support.h
+++ b/chromeos/ash/components/standalone_browser/browser_support.h
@@ -5,6 +5,7 @@
 #ifndef CHROMEOS_ASH_COMPONENTS_STANDALONE_BROWSER_BROWSER_SUPPORT_H_
 #define CHROMEOS_ASH_COMPONENTS_STANDALONE_BROWSER_BROWSER_SUPPORT_H_
 
+#include "base/auto_reset.h"
 #include "base/component_export.h"
 
 namespace ash::standalone_browser {
@@ -21,9 +22,17 @@
   // Returns the global instance of BrowserSupport.
   static BrowserSupport* Get();
 
+  // Forces IsLacrosEnabled() to return true or false for testing. Reset upon
+  // destruction of returned |base::AutoReset| object.
+  // TODO(andreaorru): remove these methods once the refactoring in complete.
+  static base::AutoReset<bool> SetLacrosEnabledForTest(bool force_enabled);
+  static bool GetLacrosEnabledForTest();
+
  private:
   BrowserSupport();
   ~BrowserSupport();
+
+  static bool lacros_enabled_for_test_;
 };
 
 }  // namespace ash::standalone_browser
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index b81f039..3c5afc2 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -3654,6 +3654,9 @@
         <message name="IDS_FEEDBACK_TOOL_ASSISTANT_LOGS_CHECKBOX" translateable="false" desc="Checkbox for Google internal account to attach assistant logs from the current session.">
         Include recent Google Assistant history and information. <ph name="BEGIN_LINK1">&lt;a id="assistantLogsLink"&gt;</ph>View more details<ph name="END_LINK1">&lt;/a&gt;</ph>.
         </message>
+        <message name="IDS_FEEDBACK_TOOL_AUTOFILL_LOGS_CHECKBOX" translateable="false" desc="Checkbox for Google internal account to attach autofill logs from the current page.">
+        Send <ph name="BEGIN_LINK1">&lt;a href="#" id="autofillMetadataUrl"&gt;</ph>autofill metadata<ph name="END_LINK1">&lt;/a&gt;</ph>
+        </message>
         <message name="IDS_FEEDBACK_TOOL_ASSISTANT_LOGS_MESSAGE" translateable="false" desc="Message shown after user clicks on the assistant logs hyperlink">
           Google Assistance captures recent history and information such as your identity and location to help us better understand your issue.&#10;&#10;The information is stored for up to 90 days and access is restricted to the team working on this issue.
         </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_AUTOFILL_LOGS_CHECKBOX.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_AUTOFILL_LOGS_CHECKBOX.png.sha1
new file mode 100644
index 0000000..08d83fa
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_AUTOFILL_LOGS_CHECKBOX.png.sha1
@@ -0,0 +1 @@
+e1576179d4f94da43a5f74b767d7ea228a1d26d1
\ No newline at end of file
diff --git a/components/feedback/feedback_common.h b/components/feedback/feedback_common.h
index 550ef0d..6384dbe 100644
--- a/components/feedback/feedback_common.h
+++ b/components/feedback/feedback_common.h
@@ -67,7 +67,7 @@
   int32_t product_id() const { return product_id_; }
   std::string user_agent() const { return user_agent_; }
   std::string locale() const { return locale_; }
-  const std::string& autofill_metadata() const { return autofill_metadata_; }
+  std::string& autofill_metadata() { return autofill_metadata_; }
 
   const AttachedFile* attachment(size_t i) const { return &attachments_[i]; }
   size_t attachments() const { return attachments_.size(); }
diff --git a/components/feedback/feedback_data.cc b/components/feedback/feedback_data.cc
index a077404a..ce42b09 100644
--- a/components/feedback/feedback_data.cc
+++ b/components/feedback/feedback_data.cc
@@ -90,10 +90,16 @@
 void FeedbackData::CompressAutofillMetadata() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const std::string& autofill_info = autofill_metadata();
+  std::string& autofill_info = autofill_metadata();
   if (autofill_info.empty()) {
     return;
   }
+  // If the user opts out of sharing the page URL, any URL related entries
+  // should be removed from the autofill logs.
+  if (page_url().empty()) {
+    feedback_util::RemoveUrlsFromAutofillData(autofill_info);
+  }
+
   ++pending_op_count_;
   base::ThreadPool::PostTaskAndReply(
       FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
diff --git a/components/feedback/feedback_util.cc b/components/feedback/feedback_util.cc
index 0cc7224..1a38822 100644
--- a/components/feedback/feedback_util.cc
+++ b/components/feedback/feedback_util.cc
@@ -9,6 +9,8 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/bind.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
@@ -80,6 +82,27 @@
   return syslogs_string;
 }
 
+void RemoveUrlsFromAutofillData(std::string& autofill_metadata) {
+  absl::optional<base::Value> properties = base::JSONReader::Read(
+      autofill_metadata, base::JSON_ALLOW_TRAILING_COMMAS);
+
+  if (!properties || !properties->is_dict()) {
+    LOG(ERROR) << "base::JSONReader::Read failed to translate to JSON";
+    return;
+  }
+
+  base::Value::Dict& autofill_data = properties->GetDict();
+  if (base::Value::List* form_structures =
+          autofill_data.FindList("form_structures")) {
+    for (base::Value& item : *form_structures) {
+      item.RemoveKey("source_url");
+      item.RemoveKey("main_frame_url");
+    }
+  }
+  base::JSONWriter::Write(properties.value(), &autofill_metadata);
+  return;
+}
+
 // Note: This function is excluded from win build because its unit tests do
 // not pass on OS_WIN.
 // This function is only called on ChromeOS and Lacros build.
diff --git a/components/feedback/feedback_util.h b/components/feedback/feedback_util.h
index 5d39882..6548a57 100644
--- a/components/feedback/feedback_util.h
+++ b/components/feedback/feedback_util.h
@@ -21,6 +21,9 @@
 // creating a system_logs.txt file attached to feedback reports.
 std::string LogsToString(const FeedbackCommon::SystemLogsMap& sys_info);
 
+// Removes URL fields from the autofill logs.
+void RemoveUrlsFromAutofillData(std::string& autofill_metadata);
+
 #if !BUILDFLAG(IS_WIN)
 // Returns true if the data from the file specified by |path| is read into
 // |contents| successfully.
diff --git a/components/feedback/feedback_util_unittest.cc b/components/feedback/feedback_util_unittest.cc
index 30737b6..65131322 100644
--- a/components/feedback/feedback_util_unittest.cc
+++ b/components/feedback/feedback_util_unittest.cc
@@ -9,7 +9,9 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/json/json_writer.h"
 #include "base/rand_util.h"
+#include "base/test/values_test_util.h"
 #include "components/feedback/feedback_report.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -138,4 +140,37 @@
   EXPECT_EQ("fake_key=fake_value\n", logs);
 }
 
+TEST_F(FeedbackUtilTest, RemoveUrlsFromAutofillData) {
+  base::Value::Dict autofill_data = base::test::ParseJsonDict(
+      R"({
+        "form_structures": [
+          {
+            "form_signature": "123",
+            "source_url": "https://www.example.com",
+            "main_frame_url": "https://www.example.com"
+          },
+          {
+            "form_signature": "456",
+            "source_url": "https://www.another-example.com",
+            "main_frame_url": "https://www.another-example.com"
+          }
+        ]})");
+  std::string autofill_data_str;
+  base::JSONWriter::Write(autofill_data, &autofill_data_str);
+
+  base::Value::List* form_structures =
+      autofill_data.FindList("form_structures");
+  ASSERT_TRUE(form_structures);
+  for (base::Value& item : *form_structures) {
+    item.RemoveKey("source_url");
+    item.RemoveKey("main_frame_url");
+  }
+
+  std::string expected_autofill_data_str;
+  base::JSONWriter::Write(autofill_data, &expected_autofill_data_str);
+
+  feedback_util::RemoveUrlsFromAutofillData(autofill_data_str);
+  EXPECT_EQ(autofill_data_str, expected_autofill_data_str);
+}
+
 }  // namespace feedback_util
diff --git a/components/file_access/scoped_file_access_delegate.cc b/components/file_access/scoped_file_access_delegate.cc
index 8762fc7..71208f4 100644
--- a/components/file_access/scoped_file_access_delegate.cc
+++ b/components/file_access/scoped_file_access_delegate.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "components/file_access/scoped_file_access_delegate.h"
+#include "base/files/file_path.h"
+#include "base/functional/bind.h"
+#include "components/file_access/scoped_file_access.h"
 
 namespace file_access {
 // static
@@ -35,6 +38,21 @@
   }
 }
 
+// static
+ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+ScopedFileAccessDelegate::GetCallbackForSystem() {
+  return base::BindRepeating(
+      [](const std::vector<base::FilePath>& file_paths,
+         base::OnceCallback<void(ScopedFileAccess)> callback) {
+        if (request_files_access_for_system_io_callback_) {
+          request_files_access_for_system_io_callback_->Run(
+              file_paths, std::move(callback));
+        } else {
+          std::move(callback).Run(ScopedFileAccess::Allowed());
+        }
+      });
+}
+
 ScopedFileAccessDelegate::ScopedFileAccessDelegate() {
   if (scoped_file_access_delegate_) {
     delete scoped_file_access_delegate_;
@@ -53,18 +71,18 @@
     ScopedFileAccessDelegate::scoped_file_access_delegate_ = nullptr;
 
 // static
-ScopedFileAccessDelegate::RequestFilesAccessForSystemIOCallback*
+ScopedFileAccessDelegate::RequestFilesAccessIOCallback*
     ScopedFileAccessDelegate::request_files_access_for_system_io_callback_ =
         nullptr;
 
 ScopedFileAccessDelegate::ScopedRequestFilesAccessCallbackForTesting::
     ScopedRequestFilesAccessCallbackForTesting(
-        RequestFilesAccessForSystemIOCallback callback,
+        RequestFilesAccessIOCallback callback,
         bool restore_original_callback)
     : restore_original_callback_(restore_original_callback) {
   original_callback_ = request_files_access_for_system_io_callback_;
   request_files_access_for_system_io_callback_ =
-      new RequestFilesAccessForSystemIOCallback(std::move(callback));
+      new RequestFilesAccessIOCallback(std::move(callback));
 }
 
 ScopedFileAccessDelegate::ScopedRequestFilesAccessCallbackForTesting::
diff --git a/components/file_access/scoped_file_access_delegate.h b/components/file_access/scoped_file_access_delegate.h
index 03bf00e..4df567c 100644
--- a/components/file_access/scoped_file_access_delegate.h
+++ b/components/file_access/scoped_file_access_delegate.h
@@ -29,7 +29,7 @@
 // to packages without direct access to the UI thread.
 class COMPONENT_EXPORT(FILE_ACCESS) ScopedFileAccessDelegate {
  public:
-  using RequestFilesAccessForSystemIOCallback =
+  using RequestFilesAccessIOCallback =
       base::RepeatingCallback<void(const std::vector<base::FilePath>&,
                                    base::OnceCallback<void(ScopedFileAccess)>)>;
 
@@ -62,6 +62,12 @@
       const std::vector<base::FilePath>& files,
       base::OnceCallback<void(file_access::ScopedFileAccess)> callback) = 0;
 
+  // Creates a callback to gain file access for the given `destination`. The
+  // callback should be called on the IO thread. The method itself from the UI
+  // thread.
+  virtual RequestFilesAccessIOCallback CreateFileAccessCallback(
+      const GURL& destination) const = 0;
+
   // Called from the IO thread. Switches to the UI thread and calls
   // RequestFilesAccessForSystem there. The `callback` is run on the IO thread
   // again.
@@ -105,7 +111,7 @@
     // Otherwise, it destroys the original callback when this class is
     // destroyed.
     explicit ScopedRequestFilesAccessCallbackForTesting(
-        RequestFilesAccessForSystemIOCallback callback,
+        RequestFilesAccessIOCallback callback,
         bool restore_original_callback = true);
 
     virtual ~ScopedRequestFilesAccessCallbackForTesting();
@@ -121,8 +127,12 @@
 
    private:
     bool restore_original_callback_;
-    RequestFilesAccessForSystemIOCallback* original_callback_ = nullptr;
+    RequestFilesAccessIOCallback* original_callback_ = nullptr;
   };
+  // Get a callback to get file access to files for system component
+  // destination. Can be called from IO or UI thread. The callback should be
+  // called on IO thread only.
+  static RequestFilesAccessIOCallback GetCallbackForSystem();
 
  protected:
   ScopedFileAccessDelegate();
@@ -136,7 +146,7 @@
   // A single instance for a callback living on the IO thread which switches to
   // the UI thread to call RequestFilesAccessForSystem from there and switch
   // back to IO thread handing the ScopedFileAccess to another (given) callback.
-  static RequestFilesAccessForSystemIOCallback*
+  static RequestFilesAccessIOCallback*
       request_files_access_for_system_io_callback_;
 };
 
diff --git a/components/file_access/scoped_file_access_delegate_unittest.cc b/components/file_access/scoped_file_access_delegate_unittest.cc
index 25f9f1f..6a30d71 100644
--- a/components/file_access/scoped_file_access_delegate_unittest.cc
+++ b/components/file_access/scoped_file_access_delegate_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
 #include "base/location.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
@@ -30,6 +31,10 @@
   void RequestFilesAccessForSystem(
       const std::vector<base::FilePath>& files,
       base::OnceCallback<void(ScopedFileAccess)> callback) override {}
+  RequestFilesAccessIOCallback CreateFileAccessCallback(
+      const GURL& destination) const override {
+    return base::DoNothing();
+  }
 };
 int ScopedFileAccessDelegateTestInstance::instance_counter = 0;
 
diff --git a/components/file_access/test/mock_scoped_file_access_delegate.h b/components/file_access/test/mock_scoped_file_access_delegate.h
index 72c9bce..db5d9fe 100644
--- a/components/file_access/test/mock_scoped_file_access_delegate.h
+++ b/components/file_access/test/mock_scoped_file_access_delegate.h
@@ -25,6 +25,10 @@
               (const std::vector<base::FilePath>&,
                base::OnceCallback<void(ScopedFileAccess)>),
               (override));
+  MOCK_METHOD((RequestFilesAccessIOCallback),
+              CreateFileAccessCallback,
+              (const GURL& destination),
+              (const override));
 };
 }  // namespace file_access
 
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h
index 23960e0..3789fa3 100644
--- a/components/history/core/browser/history_service.h
+++ b/components/history/core/browser/history_service.h
@@ -123,8 +123,8 @@
 
   // Context ids are used to scope page IDs (see AddPage). These contexts
   // must tell us when they are being invalidated so that we can clear
-  // out any cached data associated with that context.
-  void ClearCachedDataForContextID(ContextID context_id);
+  // out any cached data associated with that context. Virtual for testing.
+  virtual void ClearCachedDataForContextID(ContextID context_id);
 
   // Clears all on-demand favicons from thumbnail database.
   void ClearAllOnDemandFavicons();
@@ -616,8 +616,9 @@
       base::OnceClosure callback,
       base::CancelableTaskTracker* tracker);
 
-  // Sets scores of cluster visits to 0 to hide them from the webUI.
-  base::CancelableTaskTracker::TaskId HideVisits(
+  // Sets scores of cluster visits to 0 to hide them from the webUI. Virtual for
+  // testing.
+  virtual base::CancelableTaskTracker::TaskId HideVisits(
       const std::vector<VisitID>& visit_ids,
       base::OnceClosure callback,
       base::CancelableTaskTracker* tracker);
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc
index 5b3fc1c0..1ce0649 100644
--- a/components/omnibox/browser/search_provider.cc
+++ b/components/omnibox/browser/search_provider.cc
@@ -53,6 +53,7 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "third_party/metrics_proto/omnibox_focus_type.pb.h"
 #include "third_party/metrics_proto/omnibox_input_type.pb.h"
@@ -442,10 +443,10 @@
   // request we're constructing here for on-focus inputs.
   if (input_.focus_type() == metrics::OmniboxFocusType::INTERACTION_DEFAULT &&
       request_succeeded) {
-    std::unique_ptr<base::Value> data(
+    absl::optional<base::Value> data =
         SearchSuggestionParser::DeserializeJsonData(
             SearchSuggestionParser::ExtractJsonData(source,
-                                                    std::move(response_body))));
+                                                    std::move(response_body)));
     if (data) {
       SearchSuggestionParser::Results* results =
           is_keyword ? &keyword_results_ : &default_results_;
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index 330a4480..81f0f2cb 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -14,7 +14,6 @@
 #include "base/containers/fixed_flat_map.h"
 #include "base/i18n/icu_string_conversions.h"
 #include "base/json/json_reader.h"
-#include "base/json/json_string_value_serializer.h"
 #include "base/json/json_writer.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -37,6 +36,7 @@
 #include "net/http/http_response_headers.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/omnibox_proto/entity_info.pb.h"
 #include "ui/base/device_form_factor.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -498,7 +498,7 @@
 }
 
 // static
-std::unique_ptr<base::Value> SearchSuggestionParser::DeserializeJsonData(
+absl::optional<base::Value> SearchSuggestionParser::DeserializeJsonData(
     base::StringPiece json_data) {
   // The JSON response should be an array.
   for (size_t response_start_index = json_data.find("["), i = 0;
@@ -507,15 +507,13 @@
     // Remove any XSSI guards to allow for JSON parsing.
     json_data.remove_prefix(response_start_index);
 
-    JSONStringValueDeserializer deserializer(json_data,
-                                             base::JSON_ALLOW_TRAILING_COMMAS);
-    int error_code = 0;
-    std::unique_ptr<base::Value> data =
-        deserializer.Deserialize(&error_code, nullptr);
-    if (error_code == 0)
+    absl::optional<base::Value> data =
+        base::JSONReader::Read(json_data, base::JSON_ALLOW_TRAILING_COMMAS);
+    if (data) {
       return data;
+    }
   }
-  return nullptr;
+  return absl::nullopt;
 }
 
 // static
@@ -636,8 +634,7 @@
 
     // Store the metadata that came with the response in case we need to pass
     // it along with the prefetch query to Instant.
-    JSONStringValueSerializer json_serializer(&results->metadata);
-    json_serializer.Serialize(extras);
+    base::JSONWriter::Write(extras, &results->metadata);
   }
 
   // Processed list of match subtypes, one vector per match.
diff --git a/components/omnibox/browser/search_suggestion_parser.h b/components/omnibox/browser/search_suggestion_parser.h
index 9eace2d..045aaf43 100644
--- a/components/omnibox/browser/search_suggestion_parser.h
+++ b/components/omnibox/browser/search_suggestion_parser.h
@@ -343,7 +343,7 @@
   // Parses JSON response received from the provider, stripping XSSI
   // protection if needed. Returns the parsed data if successful, NULL
   // otherwise.
-  static std::unique_ptr<base::Value> DeserializeJsonData(
+  static absl::optional<base::Value> DeserializeJsonData(
       base::StringPiece json_data);
 
   // Parses results from the suggest server and updates the appropriate suggest
diff --git a/components/omnibox/browser/search_suggestion_parser_fuzzer.cc b/components/omnibox/browser/search_suggestion_parser_fuzzer.cc
index c7bdd09..3bb32ff 100644
--- a/components/omnibox/browser/search_suggestion_parser_fuzzer.cc
+++ b/components/omnibox/browser/search_suggestion_parser_fuzzer.cc
@@ -11,6 +11,7 @@
 #include "base/values.h"
 #include "components/omnibox/browser/search_suggestion_parser.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 
 // From crbug.com/774858
@@ -29,10 +30,10 @@
     return 0;
   std::unique_ptr<std::string> response_body =
       std::make_unique<std::string>(reinterpret_cast<const char*>(data), size);
-  std::unique_ptr<base::Value> value(
+  absl::optional<base::Value> value =
       SearchSuggestionParser::DeserializeJsonData(
           SearchSuggestionParser::ExtractJsonData(nullptr,
-                                                  std::move(response_body))));
+                                                  std::move(response_body)));
   if (value) {
     AutocompleteInput input;
     {
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc
index c7ed0fc..5f6414e2 100644
--- a/components/omnibox/browser/search_suggestion_parser_unittest.cc
+++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/omnibox/browser/test_scheme_classifier.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/omnibox_proto/entity_info.pb.h"
 
 namespace {
@@ -53,14 +54,14 @@
 
 TEST(SearchSuggestionParserTest, DeserializeNonListJsonIsInvalid) {
   std::string json_data = "{}";
-  std::unique_ptr<base::Value> result =
+  absl::optional<base::Value> result =
       SearchSuggestionParser::DeserializeJsonData(json_data);
   ASSERT_FALSE(result);
 }
 
 TEST(SearchSuggestionParserTest, DeserializeMalformedJsonIsInvalid) {
   std::string json_data = "} malformed json {";
-  std::unique_ptr<base::Value> result =
+  absl::optional<base::Value> result =
       SearchSuggestionParser::DeserializeJsonData(json_data);
   ASSERT_FALSE(result);
 }
@@ -70,7 +71,7 @@
   absl::optional<base::Value> manifest_value =
       base::JSONReader::Read(json_data);
   ASSERT_TRUE(manifest_value);
-  std::unique_ptr<base::Value> result =
+  absl::optional<base::Value> result =
       SearchSuggestionParser::DeserializeJsonData(json_data);
   ASSERT_TRUE(result);
   ASSERT_EQ(*manifest_value, *result);
@@ -82,7 +83,7 @@
   std::string json_data = R"([non-json [prefix [{"one": 1}])";
   // Parsing succeeds at:                      ^
 
-  std::unique_ptr<base::Value> result =
+  absl::optional<base::Value> result =
       SearchSuggestionParser::DeserializeJsonData(json_data);
   ASSERT_TRUE(result);
 
@@ -96,7 +97,7 @@
   // The comma in this string makes this badly formed JSON, but we explicitly
   // allow for this error in the JSON data.
   std::string json_data = R"([{"one": 1},])";
-  std::unique_ptr<base::Value> result =
+  absl::optional<base::Value> result =
       SearchSuggestionParser::DeserializeJsonData(json_data);
   ASSERT_TRUE(result);
 }
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 5ea0bb0..7d1c961 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -39,6 +39,7 @@
 #include "components/url_formatter/url_formatter.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "third_party/metrics_proto/omnibox_focus_type.pb.h"
 #include "third_party/metrics_proto/omnibox_input_type.pb.h"
@@ -304,7 +305,7 @@
     return false;
   }
 
-  std::unique_ptr<base::Value> response_data =
+  absl::optional<base::Value> response_data =
       SearchSuggestionParser::DeserializeJsonData(response_json);
   if (!response_data) {
     return false;
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index bb78564..81783761 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -125,6 +125,11 @@
   return id_;
 }
 
+int ContentPasswordManagerDriver::GetFrameId() const {
+  // Use the associated FrameTreeNode ID as the Frame ID.
+  return render_frame_host_->GetFrameTreeNodeId();
+}
+
 void ContentPasswordManagerDriver::SetPasswordFillData(
     const autofill::PasswordFormFillData& form_data) {
   password_autofill_manager_.OnAddPasswordFillData(form_data);
diff --git a/components/password_manager/content/browser/content_password_manager_driver.h b/components/password_manager/content/browser/content_password_manager_driver.h
index 15016ee..9c64042c 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.h
+++ b/components/password_manager/content/browser/content_password_manager_driver.h
@@ -91,6 +91,7 @@
   bool IsInPrimaryMainFrame() const override;
   bool CanShowAutofillUi() const override;
   ::ui::AXTreeID GetAxTreeId() const override;
+  int GetFrameId() const override;
   const GURL& GetLastCommittedURL() const override;
   void AnnotateFieldsWithParsingResult(
       const autofill::ParsingResult& parsing_result) override;
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 967c477..fa17a17 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -166,8 +166,10 @@
                           std::move(password_save_manager),
                           metrics_recorder) {
   driver_ = driver;
-  if (driver_)
+  if (driver_) {
     driver_id_ = driver->GetId();
+    cached_driver_frame_id_ = driver->GetFrameId();
+  }
 
   metrics_recorder_->RecordFormSignature(
       CalculateFormSignature(*observed_form()));
@@ -281,6 +283,15 @@
   return form_fetcher_->GetInsecureCredentials();
 }
 
+int PasswordFormManager::GetFrameId() {
+  if (driver_) {
+    // When possible, use the most up-to-date frame id, as it can change after
+    // events such as prerender activations.
+    cached_driver_frame_id_ = driver_->GetFrameId();
+  }
+  return cached_driver_frame_id_;
+}
+
 bool PasswordFormManager::IsBlocklisted() const {
   return form_fetcher_->IsBlocklisted() || newly_blocklisted_;
 }
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index c10e91a..7d35f56 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -188,7 +188,9 @@
   base::WeakPtr<PasswordManagerDriver> GetDriver() const;
   const PasswordForm* GetSubmittedForm() const;
 
-  int driver_id() { return driver_id_; }
+  // Returns the frame id of the corresponding PasswordManagerDriver. See
+  // `GetFrameId()` in PasswordManagerDriver for more details.
+  int GetFrameId();
 
 #if BUILDFLAG(IS_IOS)
   // Sets a value of the field with |field_identifier| of |observed_form()|
@@ -357,8 +359,13 @@
 
   base::WeakPtr<PasswordManagerDriver> driver_;
 
-  // Id of |driver_|. Cached since |driver_| might become null when frame is
-  // close.
+  // The frame id of |driver_|.  See `GetFrameId()` in PasswordManagerDriver for
+  // more details. This is cached since |driver_| might become null when the
+  // frame is deleted.
+  int cached_driver_frame_id_ = 0;
+
+  // The id of |driver_|. Cached since |driver_| might become null when the
+  // frame frame is deleted.
   int driver_id_ = 0;
 
   // The observed form or digest. These are mutually exclusive, hence the usage
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 7fca49b..98dface 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -1016,7 +1016,7 @@
       driver &&
 #endif
       !driver->IsInPrimaryMainFrame() &&
-      submitted_manager->driver_id() != driver->GetId()) {
+      submitted_manager->GetFrameId() != driver->GetFrameId()) {
     // Frames different from the main frame and the frame of the submitted form
     // are unlikely relevant to success of submission.
     return;
diff --git a/components/password_manager/core/browser/password_manager_driver.h b/components/password_manager/core/browser/password_manager_driver.h
index efc40c1..79b2e2d8 100644
--- a/components/password_manager/core/browser/password_manager_driver.h
+++ b/components/password_manager/core/browser/password_manager_driver.h
@@ -141,6 +141,9 @@
   // Returns the ax tree id associated with this driver.
   virtual ::ui::AXTreeID GetAxTreeId() const = 0;
 
+  // Returns the frame ID of the frame associated with this driver.
+  virtual int GetFrameId() const = 0;
+
   // Returns the last committed URL of the frame.
   virtual const GURL& GetLastCommittedURL() const = 0;
 
diff --git a/components/password_manager/core/browser/stub_password_manager_driver.cc b/components/password_manager/core/browser/stub_password_manager_driver.cc
index 10d3237..8458233 100644
--- a/components/password_manager/core/browser/stub_password_manager_driver.cc
+++ b/components/password_manager/core/browser/stub_password_manager_driver.cc
@@ -69,6 +69,10 @@
   return {};
 }
 
+int StubPasswordManagerDriver::GetFrameId() const {
+  return GetId();
+}
+
 const GURL& StubPasswordManagerDriver::GetLastCommittedURL() const {
   return GURL::EmptyGURL();
 }
diff --git a/components/password_manager/core/browser/stub_password_manager_driver.h b/components/password_manager/core/browser/stub_password_manager_driver.h
index 3f922bc..5388de86 100644
--- a/components/password_manager/core/browser/stub_password_manager_driver.h
+++ b/components/password_manager/core/browser/stub_password_manager_driver.h
@@ -45,6 +45,7 @@
   bool IsInPrimaryMainFrame() const override;
   bool CanShowAutofillUi() const override;
   ::ui::AXTreeID GetAxTreeId() const override;
+  int GetFrameId() const override;
   const GURL& GetLastCommittedURL() const override;
 };
 
diff --git a/components/password_manager/ios/ios_password_manager_driver.h b/components/password_manager/ios/ios_password_manager_driver.h
index ff660452..bfb14204 100644
--- a/components/password_manager/ios/ios_password_manager_driver.h
+++ b/components/password_manager/ios/ios_password_manager_driver.h
@@ -56,6 +56,7 @@
   password_manager::PasswordAutofillManager* GetPasswordAutofillManager()
       override;
   ::ui::AXTreeID GetAxTreeId() const override;
+  int GetFrameId() const override;
   bool IsInPrimaryMainFrame() const override;
   bool CanShowAutofillUi() const override;
   const GURL& GetLastCommittedURL() const override;
@@ -90,6 +91,11 @@
       password_generation_helper_;
   web::WebFrame* web_frame_;
   int id_;
+
+  // The hash of the cached frame ID of `web_frame_`. This is cached because
+  // `web_frame` might be set to null when the frame is deleted.
+  int cached_frame_id_;
+
   bool is_in_main_frame_;
   // The security origin associated with |web_frame_|.
   GURL security_origin_;
diff --git a/components/password_manager/ios/ios_password_manager_driver.mm b/components/password_manager/ios/ios_password_manager_driver.mm
index 92d2a3d..0f89294 100644
--- a/components/password_manager/ios/ios_password_manager_driver.mm
+++ b/components/password_manager/ios/ios_password_manager_driver.mm
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/hash/hash.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/password_manager/core/browser/password_generation_frame_helper.h"
 #include "components/password_manager/core/browser/password_manager.h"
@@ -26,7 +27,8 @@
     : bridge_(bridge),
       password_manager_(password_manager),
       web_frame_(web_frame),
-      id_(driver_id) {
+      id_(driver_id),
+      cached_frame_id_(base::FastHash(web_frame->GetFrameId())) {
   password_generation_helper_ =
       std::make_unique<password_manager::PasswordGenerationFrameHelper>(
           password_manager_->GetClient(), this);
@@ -129,6 +131,10 @@
   return {};
 }
 
+int IOSPasswordManagerDriver::GetFrameId() const {
+  return cached_frame_id_;
+}
+
 const GURL& IOSPasswordManagerDriver::GetLastCommittedURL() const {
   return bridge_.lastCommittedURL;
 }
diff --git a/components/supervised_user/core/common/features.cc b/components/supervised_user/core/common/features.cc
index 0c24bb7..b6f7577 100644
--- a/components/supervised_user/core/common/features.cc
+++ b/components/supervised_user/core/common/features.cc
@@ -14,17 +14,17 @@
 namespace supervised_user {
 
 // Enables refreshed version of the website filter interstitial that is shown to
-// Family Link users when the navigate to the blocked website.
+// Family Link users when they navigate to the blocked website.
 // This feature is a prerequisite for `kLocalWebApproval` feature.
-#if BUILDFLAG(IS_CHROMEOS)
-BASE_FEATURE(kWebFilterInterstitialRefresh,
-             "WebFilterInterstitialRefresh",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-#else
+#if BUILDFLAG(IS_ANDROID)
 BASE_FEATURE(kWebFilterInterstitialRefresh,
              "WebFilterInterstitialRefresh",
              base::FEATURE_DISABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_CHROMEOS)
+#else
+BASE_FEATURE(kWebFilterInterstitialRefresh,
+             "WebFilterInterstitialRefresh",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+#endif
 
 // Enables local parent approvals for the blocked website on the Family Link
 // user's device.
@@ -42,7 +42,7 @@
 BASE_FEATURE(kLocalWebApprovals,
              "LocalWebApprovals",
              base::FEATURE_DISABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_CHROMEOS)
+#endif
 
 const char kLocalWebApprovalsPreferredButtonLocal[] = "local";
 const char kLocalWebApprovalsPreferredButtonRemote[] = "remote";
@@ -61,7 +61,7 @@
 BASE_FEATURE(kAllowHistoryDeletionForChildAccounts,
              "AllowHistoryDeletionForChildAccounts",
              base::FEATURE_ENABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_CHROMEOS)
+#endif
 
 // Enables the new Kids Management Api.
 BASE_FEATURE(kEnableKidsManagementService,
@@ -84,7 +84,7 @@
   return true;
 #else
   return false;
-#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#endif
 }
 
 bool IsLocalWebApprovalsEnabled() {
@@ -99,7 +99,7 @@
 #else
   return IsWebFilterInterstitialRefreshEnabled() &&
          base::FeatureList::IsEnabled(kLocalWebApprovals);
-#endif  // BUILDFLAG(IS_ANDROID)
+#endif
 }
 
 bool IsLocalWebApprovalThePreferredButton() {
diff --git a/components/viz/common/quads/texture_draw_quad.h b/components/viz/common/quads/texture_draw_quad.h
index 694d33e..efce38d 100644
--- a/components/viz/common/quads/texture_draw_quad.h
+++ b/components/viz/common/quads/texture_draw_quad.h
@@ -93,7 +93,7 @@
   // This optional damage is in target render pass coordinate space.
   absl::optional<gfx::Rect> damage_rect;
 
-  struct VIZ_COMMON_EXPORT RoundedDisplayMasksInfo {
+  struct RoundedDisplayMasksInfo {
     static constexpr size_t kMaxRoundedDisplayMasksCount = 2;
     static constexpr size_t kOriginRoundedDisplayMaskIndex = 0;
     static constexpr size_t kOtherRoundedDisplayMaskIndex = 1;
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index 3e0ad05..f6f147a0 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -12,6 +12,7 @@
 #include "content/browser/generic_sensor/sensor_provider_proxy_impl.h"
 #include "content/browser/presentation/presentation_test_utils.h"
 #include "content/browser/renderer_host/back_forward_cache_disable.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/worker_host/dedicated_worker_hosts_for_document.h"
 #include "content/public/browser/disallow_activation_reason.h"
@@ -1148,7 +1149,8 @@
 
   // 3) No-op feature update on a subframe while in cache, should be no-op.
   ASSERT_FALSE(delete_observer_rfh_b.deleted());
-  rfh_b->DidChangeBackForwardCacheDisablingFeatures(0);
+  RenderFrameHostImpl::BackForwardCacheBlockingDetails empty_vector;
+  rfh_b->DidChangeBackForwardCacheDisablingFeatures(std::move(empty_vector));
 
   // 4) Go back.
   ASSERT_TRUE(HistoryGoBack(web_contents()));
diff --git a/content/browser/blob_storage/blob_registry_wrapper.cc b/content/browser/blob_storage/blob_registry_wrapper.cc
index bc02f77..ee9f7ae 100644
--- a/content/browser/blob_storage/blob_registry_wrapper.cc
+++ b/content/browser/blob_storage/blob_registry_wrapper.cc
@@ -6,6 +6,7 @@
 
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -31,6 +32,11 @@
   bool CanAccessDataForOrigin(const url::Origin& origin) override {
     return security_policy_handle_.CanAccessDataForOrigin(origin);
   }
+  file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+  GetAccessCallback() override {
+    // TODO (b/262203074) create actual callback
+    return base::NullCallback();
+  }
 
  private:
   ChildProcessSecurityPolicyImpl::Handle security_policy_handle_;
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.cc b/content/browser/blob_storage/chrome_blob_storage_context.cc
index 3bfd99c..0e1d0b8 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.cc
+++ b/content/browser/blob_storage/chrome_blob_storage_context.cc
@@ -12,11 +12,13 @@
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
 #include "base/guid.h"
 #include "base/supports_user_data.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/task_runner.h"
 #include "base/task/thread_pool.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/blob_handle.h"
 #include "content/public/browser/browser_context.h"
@@ -214,14 +216,16 @@
   return blob_handle;
 }
 
-void ChromeBlobStorageContext::CreateFileSystemBlob(
+void ChromeBlobStorageContext::CreateFileSystemBlobWithFileAccess(
     scoped_refptr<storage::FileSystemContext> file_system_context,
     mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
     const storage::FileSystemURL& url,
     const std::string& blob_uuid,
     const std::string& content_type,
     const uint64_t file_size,
-    const base::Time& file_modification_time) {
+    const base::Time& file_modification_time,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   auto blob_builder = std::make_unique<storage::BlobDataBuilder>(blob_uuid);
@@ -229,9 +233,9 @@
     // Use AppendFileSystemFile here, since we're streaming the file directly
     // from the file system backend, and the file thus might not actually be
     // backed by a file on disk.
-    blob_builder->AppendFileSystemFile(url, 0, file_size,
-                                       file_modification_time,
-                                       std::move(file_system_context));
+    blob_builder->AppendFileSystemFile(
+        url, 0, file_size, file_modification_time,
+        std::move(file_system_context), std::move(file_access));
   }
   blob_builder->set_content_type(content_type);
 
@@ -245,6 +249,19 @@
   storage::BlobImpl::Create(std::move(blob_handle), std::move(blob_receiver));
 }
 
+void ChromeBlobStorageContext::CreateFileSystemBlob(
+    scoped_refptr<storage::FileSystemContext> file_system_context,
+    mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
+    const storage::FileSystemURL& url,
+    const std::string& blob_uuid,
+    const std::string& content_type,
+    const uint64_t file_size,
+    const base::Time& file_modification_time) {
+  CreateFileSystemBlobWithFileAccess(
+      file_system_context, std::move(blob_receiver), url, blob_uuid,
+      content_type, file_size, file_modification_time, base::NullCallback());
+}
+
 // static
 scoped_refptr<network::SharedURLLoaderFactory>
 ChromeBlobStorageContext::URLLoaderFactoryForToken(
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.h b/content/browser/blob_storage/chrome_blob_storage_context.h
index 8192a15a8..98fa01ae 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.h
+++ b/content/browser/blob_storage/chrome_blob_storage_context.h
@@ -12,10 +12,12 @@
 #include <vector>
 
 #include "base/files/file_path.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner_helpers.h"
 #include "base/time/time.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -79,6 +81,23 @@
       const std::string& content_type);
 
   // Creates a FileSystem File blob accessible by the renderer via the blob
+  // remote corresponding to `blob_receiver`. The callback can grant or deny the
+  // read access to the file. The callback `file_access` is used to grant or
+  // deny access to files under dlp restrictions. Leaving it at NullCallback
+  // will lead to default behaviour, which currently is granting it (until
+  // b/265908846 is done).
+  void CreateFileSystemBlobWithFileAccess(
+      scoped_refptr<storage::FileSystemContext> file_system_context,
+      mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
+      const storage::FileSystemURL& url,
+      const std::string& blob_uuid,
+      const std::string& content_type,
+      const uint64_t file_size,
+      const base::Time& file_modification_time,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access);
+
+  // Creates a FileSystem File blob accessible by the renderer via the blob
   // remote corresponding to `blob_receiver`.
   void CreateFileSystemBlob(
       scoped_refptr<storage::FileSystemContext> file_system_context,
diff --git a/content/browser/devtools/protocol/preload_handler.cc b/content/browser/devtools/protocol/preload_handler.cc
index 0dd2f70..0539c19 100644
--- a/content/browser/devtools/protocol/preload_handler.cc
+++ b/content/browser/devtools/protocol/preload_handler.cc
@@ -101,8 +101,6 @@
       return Preload::PrerenderFinalStatusEnum::CrossSiteNavigation;
     case PrerenderFinalStatus::kSameSiteCrossOriginRedirect:
       return Preload::PrerenderFinalStatusEnum::SameSiteCrossOriginRedirect;
-    case PrerenderFinalStatus::kSameSiteCrossOriginNavigation:
-      return Preload::PrerenderFinalStatusEnum::SameSiteCrossOriginNavigation;
     case PrerenderFinalStatus::kSameSiteCrossOriginRedirectNotOptIn:
       return Preload::PrerenderFinalStatusEnum::
           SameSiteCrossOriginRedirectNotOptIn;
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index 37a66a9e..39a78d8 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -2186,15 +2186,46 @@
   EXPECT_EQ(web_contents()->GetLastCommittedURL(), kNavigatedUrl);
 }
 
-// TODO(crbug.com/1239281): Support the same-site cross-origin navigation.
 // Tests that the same-site cross-origin main frame navigation in a prerendering
-// page cancels the prerendering.
-IN_PROC_BROWSER_TEST_F(
-    PrerenderBrowserTest,
-    SameSiteCrossOriginMainFrameNavigationCancelsPrerendering) {
+// page succeeds.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
+                       SameSiteCrossOriginMainFrameNavigation) {
   const GURL kInitialUrl = GetUrl("/empty.html");
   const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender");
-  const GURL kNavigatedUrl = GetSameSiteCrossOriginUrl("/empty.html?navigated");
+  const GURL kNavigatedUrl =
+      GetSameSiteCrossOriginUrl("/prerender/prerender_with_opt_in_header.html");
+
+  // Navigate to an initial page.
+  ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
+
+  // Start a prerender.
+  int host_id = AddPrerender(kPrerenderingUrl);
+
+  // Start a same-site cross-origin navigation in the prerender frame tree that
+  // will not cancel the initiator's prerendering.
+  test::PrerenderHostObserver observer(*web_contents_impl(), host_id);
+
+  NavigatePrerenderedPage(host_id, kNavigatedUrl);
+
+  // Activate a prerender and it should succeed.
+  NavigatePrimaryPage(kPrerenderingUrl);
+
+  observer.WaitForActivation();
+  EXPECT_TRUE(observer.was_activated());
+  EXPECT_EQ(web_contents()->GetLastCommittedURL(), kNavigatedUrl);
+}
+
+// Tests that the same-site cross-origin main frame navigation in a prerendering
+// page cancels the prerendering without opt-in.
+IN_PROC_BROWSER_TEST_F(
+    PrerenderBrowserTest,
+    SameSiteCrossOriginMainFrameNavigationCancelsPrerenderingWithoutOptInHeader) {
+  const GURL kInitialUrl = GetUrl("/empty.html");
+  const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender");
+  // The cross-origin navigation should fail prerendering without an opt-in
+  // header.
+  const GURL kNavigatedUrl =
+      GetSameSiteCrossOriginUrl("/prerender/empty.html?navigated");
 
   // Navigate to an initial page.
   ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
@@ -2211,7 +2242,7 @@
   observer.WaitForDestroyed();
   EXPECT_FALSE(HasHostForUrl(kPrerenderingUrl));
   ExpectFinalStatusForSpeculationRule(
-      PrerenderFinalStatus::kSameSiteCrossOriginNavigation);
+      PrerenderFinalStatus::kSameSiteCrossOriginNavigationNotOptIn);
 }
 
 // Tests that the cross-site main frame navigation in a prerendering page
@@ -2220,7 +2251,8 @@
                        CrossSiteMainFrameNavigationCancelsPrerendering) {
   const GURL kInitialUrl = GetUrl("/empty.html");
   const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender");
-  const GURL kNavigatedUrl = GetCrossSiteUrl("/empty.html?navigated");
+  const GURL kNavigatedUrl =
+      GetCrossSiteUrl("/prerender/prerender_with_opt_in_header.html");
 
   // Navigate to an initial page.
   ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
diff --git a/content/browser/preloading/prerender/prerender_final_status.h b/content/browser/preloading/prerender/prerender_final_status.h
index e9c58917f98..8f0ae82 100644
--- a/content/browser/preloading/prerender/prerender_final_status.h
+++ b/content/browser/preloading/prerender/prerender_final_status.h
@@ -73,7 +73,9 @@
   kCrossSiteRedirect = 44,
   kCrossSiteNavigation = 45,
   kSameSiteCrossOriginRedirect = 46,
-  kSameSiteCrossOriginNavigation = 47,
+  // Deprecated. Same-site cross-origin navigation in a prerendered page is
+  // allowed in crbug.com/1239281.
+  // kSameSiteCrossOriginNavigation = 47,
   kSameSiteCrossOriginRedirectNotOptIn = 48,
   kSameSiteCrossOriginNavigationNotOptIn = 49,
   // The prediction is correct, and we are almost ready to activate this
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc
index aa8a5da5..e5900d30 100644
--- a/content/browser/preloading/prerender/prerender_host.cc
+++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -978,7 +978,6 @@
     case PrerenderFinalStatus::kCrossSiteNavigation:
     case PrerenderFinalStatus::kCrossSiteRedirect:
     case PrerenderFinalStatus::kSameSiteCrossOriginRedirect:
-    case PrerenderFinalStatus::kSameSiteCrossOriginNavigation:
     case PrerenderFinalStatus::kSameSiteCrossOriginRedirectNotOptIn:
     case PrerenderFinalStatus::kSameSiteCrossOriginNavigationNotOptIn:
     case PrerenderFinalStatus::kActivationNavigationParameterMismatch:
diff --git a/content/browser/preloading/prerender/prerender_navigation_throttle.cc b/content/browser/preloading/prerender/prerender_navigation_throttle.cc
index bee7c990..14a791f 100644
--- a/content/browser/preloading/prerender/prerender_navigation_throttle.cc
+++ b/content/browser/preloading/prerender/prerender_navigation_throttle.cc
@@ -158,7 +158,7 @@
 
   GURL navigation_url = navigation_handle()->GetURL();
   url::Origin navigation_origin = url::Origin::Create(navigation_url);
-  url::Origin prerendering_origin =
+  url::Origin initial_prerendering_origin =
       url::Origin::Create(prerender_host->GetInitialUrl());
 
   // Check if the main frame navigation happens after the initial prerendering
@@ -175,17 +175,13 @@
       return CANCEL;
     }
 
-    // Cross-origin navigations after the initial prerendering navigation are
+    // Cross-site navigations after the initial prerendering navigation are
     // disallowed.
-    // TODO(crbug.com/1239281): Support same-site cross-origin main frame
-    // navigations.
-    if (navigation_origin != prerendering_origin) {
+    if (!prerender_navigation_utils::IsSameSite(navigation_url,
+                                                initial_prerendering_origin)) {
       prerender_host_registry->CancelHost(
           frame_tree_node->frame_tree_node_id(),
-          prerender_navigation_utils::IsSameSite(navigation_url,
-                                                 prerendering_origin)
-              ? PrerenderFinalStatus::kSameSiteCrossOriginNavigation
-              : PrerenderFinalStatus::kCrossSiteNavigation);
+          PrerenderFinalStatus::kCrossSiteNavigation);
       return CANCEL;
     }
   }
@@ -217,19 +213,16 @@
   if (prerender_host->IsBrowserInitiated()) {
     // Cancel an embedder triggered prerendering if it is redirected to a URL
     // cross-site to the initial prerendering URL.
-    if (is_redirection) {
-      bool is_same_site = prerender_navigation_utils::IsSameSite(
-          navigation_url, prerendering_origin);
-      if (!is_same_site) {
-        AnalyzeCrossOriginRedirection(
-            navigation_origin, prerendering_origin,
-            prerender_host->trigger_type(),
-            prerender_host->embedder_histogram_suffix());
-        prerender_host_registry->CancelHost(
-            frame_tree_node->frame_tree_node_id(),
-            PrerenderFinalStatus::kCrossSiteRedirect);
-        return CANCEL;
-      }
+    if (is_redirection && !prerender_navigation_utils::IsSameSite(
+                              navigation_url, initial_prerendering_origin)) {
+      AnalyzeCrossOriginRedirection(
+          navigation_origin, initial_prerendering_origin,
+          prerender_host->trigger_type(),
+          prerender_host->embedder_histogram_suffix());
+      prerender_host_registry->CancelHost(
+          frame_tree_node->frame_tree_node_id(),
+          PrerenderFinalStatus::kCrossSiteRedirect);
+      return CANCEL;
     }
 
     // Skip the same-site check for non-redirected cases as the initiator
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 6a1fb2b..9c8ad94 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -4425,9 +4425,13 @@
 }
 
 void RenderFrameHostImpl::DidChangeBackForwardCacheDisablingFeatures(
-    uint64_t features_mask) {
-  renderer_reported_bfcache_disabling_features_ =
-      BackForwardCacheDisablingFeatures::FromEnumBitmask(features_mask);
+    BackForwardCacheBlockingDetails details) {
+  renderer_reported_bfcache_disabling_features_.Clear();
+  for (auto& feature_details : details) {
+    renderer_reported_bfcache_disabling_features_.Put(
+        static_cast<blink::scheduler::WebSchedulerTrackedFeature>(
+            feature_details->feature));
+  }
 
   MaybeEvictFromBackForwardCache();
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 3003081..cb10ec3a 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2394,8 +2394,10 @@
 
   // blink::mojom::BackForwardCacheControllerHost:
   void EvictFromBackForwardCache(blink::mojom::RendererEvictionReason) override;
+  using BackForwardCacheBlockingDetails =
+      std::vector<blink::mojom::BlockingDetailsPtr>;
   void DidChangeBackForwardCacheDisablingFeatures(
-      uint64_t features_mask) override;
+      BackForwardCacheBlockingDetails details) override;
 
   // blink::LocalMainFrameHost overrides:
   void ScaleFactorChanged(float scale) override;
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 2e83844..6bb4360 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -308,6 +308,24 @@
          window->web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL();
 }
 
+using blink::mojom::BlockingDetails;
+using BackForwardCacheBlockingDetails =
+    RenderFrameHostImpl::BackForwardCacheBlockingDetails;
+using BlocklistedFeature = blink::scheduler::WebSchedulerTrackedFeature;
+using BlocklistedFeatures = blink::scheduler::WebSchedulerTrackedFeatures;
+// Helper function to create a vector which contains the mojom feature
+// information.
+BackForwardCacheBlockingDetails CreateBlockingDetails(
+    BlocklistedFeatures features) {
+  BackForwardCacheBlockingDetails feature_vector;
+  for (auto feature : features) {
+    auto feature_info = BlockingDetails::New();
+    feature_info->feature = static_cast<uint32_t>(feature);
+    feature_vector.push_back(std::move(feature_info));
+  }
+  return feature_vector;
+}
+
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
                        ExecuteJavaScriptMethodWorksWithArguments) {
   EXPECT_TRUE(NavigateToURL(
@@ -3569,20 +3587,30 @@
   EXPECT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
   RenderFrameHostImpl* main_frame = web_contents()->GetPrimaryMainFrame();
-  // Simulate getting 0b1 as a feature vector from the renderer.
-  main_frame->DidChangeBackForwardCacheDisablingFeatures(0b1u);
-  DCHECK_EQ(main_frame->GetBackForwardCacheDisablingFeatures().ToEnumBitmask(),
-            0b1u);
-  // Simulate the browser side reporting a feature usage.
+  // Simulate getting WebSocket in a feature vector from the renderer.
+  main_frame->DidChangeBackForwardCacheDisablingFeatures(
+      CreateBlockingDetails(BlocklistedFeature::kWebSocket));
+  ASSERT_EQ(main_frame->GetBackForwardCacheDisablingFeatures(),
+            BlocklistedFeatures(BlocklistedFeature::kWebSocket));
+
+  // Simulate the browser side reporting WebRTC usage.
   main_frame->OnBackForwardCacheDisablingStickyFeatureUsed(
-      static_cast<blink::scheduler::WebSchedulerTrackedFeature>(1));
-  DCHECK_EQ(main_frame->GetBackForwardCacheDisablingFeatures().ToEnumBitmask(),
-            0b11u);
+      static_cast<BlocklistedFeature>(BlocklistedFeature::kWebRTC));
+  ASSERT_EQ(main_frame->GetBackForwardCacheDisablingFeatures(),
+            BlocklistedFeatures(BlocklistedFeature::kWebSocket,
+                                BlocklistedFeature::kWebRTC));
+
   // Simulate a feature vector being updated from the renderer with some
   // features being activated and some being deactivated.
-  main_frame->DidChangeBackForwardCacheDisablingFeatures(0b100u);
-  DCHECK_EQ(main_frame->GetBackForwardCacheDisablingFeatures().ToEnumBitmask(),
-            0b110u);
+  // [kWebSocket(0), kWebRTC(1)] -> [kWebRTC(1),
+  // kMainResourceHasCacheControlNoCache(2)]
+  main_frame->DidChangeBackForwardCacheDisablingFeatures(CreateBlockingDetails(
+      {BlocklistedFeature::kWebRTC,
+       BlocklistedFeature::kMainResourceHasCacheControlNoCache}));
+  ASSERT_EQ(main_frame->GetBackForwardCacheDisablingFeatures(),
+            BlocklistedFeatures(
+                BlocklistedFeature::kWebRTC,
+                BlocklistedFeature::kMainResourceHasCacheControlNoCache));
 
   // Navigate away and expect that no values persist the navigation.
   // Note that we are still simulating the renderer call, otherwise features
@@ -3590,7 +3618,9 @@
   EXPECT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")));
   main_frame = web_contents()->GetPrimaryMainFrame();
-  main_frame->DidChangeBackForwardCacheDisablingFeatures(0b0u);
+  BackForwardCacheBlockingDetails empty_vector;
+  main_frame->DidChangeBackForwardCacheDisablingFeatures(
+      CreateBlockingDetails({}));
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index a197dd6..678647d 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -952,16 +952,19 @@
 }
 
 void DedicatedWorkerHost::DidChangeBackForwardCacheDisablingFeatures(
-    uint64_t features_mask) {
+    BackForwardCacheBlockingDetails details) {
   RenderFrameHostImpl* ancestor_render_frame_host =
       RenderFrameHostImpl::FromID(ancestor_render_frame_host_id_);
   if (!ancestor_render_frame_host) {
     // The frame may have already been closed.
     return;
   }
-  bfcache_disabling_features_ =
-      blink::scheduler::WebSchedulerTrackedFeatures::FromEnumBitmask(
-          features_mask);
+  bfcache_disabling_features_.Clear();
+  for (auto& feature_details : details) {
+    bfcache_disabling_features_.Put(
+        static_cast<blink::scheduler::WebSchedulerTrackedFeature>(
+            feature_details->feature));
+  }
   ancestor_render_frame_host->MaybeEvictFromBackForwardCache();
 }
 
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index 277037a..1b50807 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -206,8 +206,10 @@
   // blink::mojom::BackForwardCacheControllerHost:
   void EvictFromBackForwardCache(
       blink::mojom::RendererEvictionReason reason) override;
+  using BackForwardCacheBlockingDetails =
+      std::vector<blink::mojom::BlockingDetailsPtr>;
   void DidChangeBackForwardCacheDisablingFeatures(
-      uint64_t features_mask) override;
+      BackForwardCacheBlockingDetails details) override;
 
   // BucketContext:
   blink::StorageKey GetBucketStorageKey() override;
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index bd44dcc..193cec4 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -834,6 +834,11 @@
   // script will call domAutomationController.send() with the completion
   // value. Setting this bit will disable that, requiring |script| to provide
   // its own call to domAutomationController.send() instead.
+  //
+  // Beware that if your script calls domAutomationController.send more than
+  // once, it can interfere with the results obtained by future calls to EvalJs.
+  // It is safer to use Promise resolution rather than
+  // domAutomationController.send.
   EXECUTE_SCRIPT_USE_MANUAL_REPLY = (1 << 1),
 
   // By default, when the script passed to EvalJs evaluates to a Promise, the
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index f5b8b0f..ead135c 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -53,6 +53,10 @@
 #include "ui/accessibility/null_ax_action_target.h"
 #include "ui/native_theme/native_theme_features.h"
 
+#if defined(LEAK_SANITIZER)
+#include <sanitizer/lsan_interface.h>
+#endif
+
 namespace content {
 
 using blink::WebAXObject;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 9a0c954f..ed66d4e 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -44,7 +44,6 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/simple_thread.h"
-#include "base/threading/thread_local.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/memory_pressure_level_proto.h"
@@ -137,6 +136,7 @@
 #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/viz/public/cpp/gpu/gpu.h"
 #include "skia/ext/skia_memory_dump_provider.h"
+#include "third_party/abseil-cpp/absl/base/attributes.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/page/launching_process_state.h"
 #include "third_party/blink/public/common/privacy_budget/active_sampling.h"
@@ -242,8 +242,7 @@
 
 // Keep the global RenderThreadImpl in a TLS slot so it is impossible to access
 // incorrectly from the wrong thread.
-base::LazyInstance<base::ThreadLocalPointer<RenderThreadImpl>>::DestructorAtExit
-    lazy_tls = LAZY_INSTANCE_INITIALIZER;
+ABSL_CONST_INIT thread_local RenderThreadImpl* render_thread = nullptr;
 
 base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner>>::
     DestructorAtExit g_main_task_runner = LAZY_INSTANCE_INITIALIZER;
@@ -477,7 +476,7 @@
 
 // static
 RenderThreadImpl* RenderThreadImpl::current() {
-  return lazy_tls.Pointer()->Get();
+  return render_thread;
 }
 
 // static
@@ -585,7 +584,7 @@
     blink::WebView::SetUseExternalPopupMenus(true);
 #endif
 
-  lazy_tls.Pointer()->Set(this);
+  render_thread = this;
   g_main_task_runner.Get() = base::SingleThreadTaskRunner::GetCurrentDefault();
 
   // Register this object as the main thread.
diff --git a/content/renderer/worker/worker_thread_registry.cc b/content/renderer/worker/worker_thread_registry.cc
index 5161092..63497d9 100644
--- a/content/renderer/worker/worker_thread_registry.cc
+++ b/content/renderer/worker/worker_thread_registry.cc
@@ -5,18 +5,16 @@
 #include "content/renderer/worker/worker_thread_registry.h"
 
 #include <atomic>
-#include <memory>
 #include <utility>
 
 #include "base/check.h"
 #include "base/containers/contains.h"
-#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
 #include "base/observer_list.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
-#include "base/threading/thread_local.h"
 #include "content/public/renderer/worker_thread.h"
 
 namespace content {
@@ -37,10 +35,7 @@
   WorkerThreadObservers observers;
 };
 
-using ThreadLocalWorkerThreadData = base::ThreadLocalPointer<WorkerThreadData>;
-
-base::LazyInstance<ThreadLocalWorkerThreadData>::DestructorAtExit
-    g_worker_data_tls = LAZY_INSTANCE_INITIALIZER;
+ABSL_CONST_INIT thread_local WorkerThreadData* worker_data = nullptr;
 
 // A task-runner that refuses to run any tasks.
 class DoNothingTaskRunner : public base::SequencedTaskRunner {
@@ -70,9 +65,7 @@
 // WorkerThread implementation:
 
 int WorkerThread::GetCurrentId() {
-  if (auto* worker_data = g_worker_data_tls.Pointer()->Get())
-    return worker_data->thread_id;
-  return 0;
+  return worker_data ? worker_data->thread_id : 0;
 }
 
 void WorkerThread::PostTask(int id, base::OnceClosure task) {
@@ -80,13 +73,11 @@
 }
 
 void WorkerThread::AddObserver(Observer* observer) {
-  auto* worker_data = g_worker_data_tls.Pointer()->Get();
   DCHECK(worker_data);
   worker_data->observers.AddObserver(observer);
 }
 
 void WorkerThread::RemoveObserver(Observer* observer) {
-  auto* worker_data = g_worker_data_tls.Pointer()->Get();
   DCHECK(worker_data);
   worker_data->observers.RemoveObserver(observer);
 }
@@ -105,38 +96,32 @@
 }
 
 WorkerThreadRegistry* WorkerThreadRegistry::Instance() {
-  static base::LazyInstance<WorkerThreadRegistry>::Leaky worker_task_runner =
-      LAZY_INSTANCE_INITIALIZER;
-  return worker_task_runner.Pointer();
+  static base::NoDestructor<WorkerThreadRegistry> worker_thread_registry;
+  return worker_thread_registry.get();
 }
 
-WorkerThreadRegistry::~WorkerThreadRegistry() {}
+WorkerThreadRegistry::~WorkerThreadRegistry() = default;
 
 void WorkerThreadRegistry::DidStartCurrentWorkerThread() {
-  DCHECK(!g_worker_data_tls.Pointer()->Get());
+  DCHECK(!worker_data);
   DCHECK(!base::PlatformThread::CurrentRef().is_null());
-  auto worker_thread_data = std::make_unique<WorkerThreadData>();
-  int id = worker_thread_data->thread_id;
-  g_worker_data_tls.Pointer()->Set(worker_thread_data.release());
-
+  worker_data = new WorkerThreadData();
   base::AutoLock locker_(task_runner_map_lock_);
-  task_runner_map_[id] =
+  task_runner_map_[worker_data->thread_id] =
       base::SingleThreadTaskRunner::GetCurrentDefault().get();
-  CHECK(task_runner_map_[id]);
+  CHECK(task_runner_map_[worker_data->thread_id]);
 }
 
 void WorkerThreadRegistry::WillStopCurrentWorkerThread() {
-  auto worker_data =
-      base::WrapUnique<WorkerThreadData>(g_worker_data_tls.Pointer()->Get());
   DCHECK(worker_data);
   for (auto& observer : worker_data->observers)
     observer.WillStopCurrentWorkerThread();
-
   {
     base::AutoLock locker(task_runner_map_lock_);
     task_runner_map_.erase(worker_data->thread_id);
   }
-  g_worker_data_tls.Pointer()->Set(nullptr);
+  delete worker_data;
+  worker_data = nullptr;
 }
 
 base::SequencedTaskRunner* WorkerThreadRegistry::GetTaskRunnerFor(
diff --git a/content/test/data/prerender/prerender_with_opt_in_header.html.mock-http-headers b/content/test/data/prerender/prerender_with_opt_in_header.html.mock-http-headers
index 5875b2a..17d81e41 100644
--- a/content/test/data/prerender/prerender_with_opt_in_header.html.mock-http-headers
+++ b/content/test/data/prerender/prerender_with_opt_in_header.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Supports-Loading-Mode: credentialed-prerender
\ No newline at end of file
+Supports-Loading-Mode: credentialed-prerender
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 4226a45..0d30b23a 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -208,6 +208,7 @@
 # Test is too slow for old phones and laptops, and in Debug mode.
 crbug.com/1356246 [ android android-nexus-5x ] Pixel_SVGHuge [ Skip ]
 crbug.com/1356246 [ mac amd debug ] Pixel_SVGHuge [ Skip ]
+crbug.com/1356246 [ mac intel debug ] Pixel_SVGHuge [ Failure ]
 # Mac Nvidia laptops are quite old and the test times out on release builds too.
 crbug.com/1356246 [ mac nvidia ] Pixel_SVGHuge [ Skip ]
 
@@ -360,8 +361,6 @@
 crbug.com/1395227 [ asan intel mac-x86_64 monterey release ] Pixel_WebGPUImportVideoFrameOffscreenCanvas [ Failure ]
 crbug.com/1395227 [ win ] Pixel_WebGPUImportVideoFrameUnacceleratedOffscreenCanvas [ Failure ]
 
-# Pixel_VideoStreamFromWebGL* tests sporadically fail on Linux FYI Release (NVIDIA) bot
-crbug.com/1348002 [ angle-opengl linux nvidia release ] Pixel_VideoStreamFromWebGLAlphaCanvas [ Failure ]
 
 crbug.com/1372144 [ android android-sm-a135m ] Pixel_CanvasDisplaySRGBAccelerated2D [ Failure ]
 crbug.com/1372144 [ android android-sm-a135m ] Pixel_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 9dd50a9..0762968 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -313,7 +313,6 @@
 ## Metal Common ##
 # Common to all GPU types (both AMD and Intel, at least)
 
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] WebglExtension_EXT_disjoint_timer_query_webgl2 [ Failure ]
 crbug.com/angleproject/7079 [ angle-metal mac mac-x86_64 passthrough ] deqp/functional/gles3/fboinvalidate/sub.html [ Failure ]
 crbug.com/angleproject/7079 [ angle-metal mac mac-x86_64 passthrough ] deqp/functional/gles3/fboinvalidate/whole.html [ Failure ]
 crbug.com/angleproject/7079 [ angle-metal mac mac-x86_64 passthrough ] deqp/functional/gles3/fbomultisample.2_samples.html [ Failure ]
@@ -323,7 +322,7 @@
 
 ## Metal AMD ##
 
-crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] conformance2/textures/misc/tex-srgb-mipmap.html [ Failure ]
+crbug.com/angleproject/6430 [ amd-0x6821 angle-metal monterey passthrough ] conformance2/textures/misc/tex-srgb-mipmap.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/pixelbufferobject.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/shaderpackingfunction.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/textureshadow/2d_array_nearest_mipmap_nearest_greater_or_equal.html [ Failure ]
@@ -341,6 +340,7 @@
 crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/texturespecification/texstorage2d_format_depth_stencil.html [ Failure ]
 crbug.com/angleproject/7425 [ mac passthrough angle-metal amd ] deqp/functional/gles3/texturespecification/texstorage3d_format_depth_stencil.html [ Failure ]
 
+crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/canvas/rapid-resizing.html [ RetryOnFailure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/ogles/GL/lessThan/lessThan_001_to_008.html [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/ogles/GL/vec/vec_017_to_018.html [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/textures/misc/texture-size-cube-maps.html [ Failure ]
@@ -357,7 +357,10 @@
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/data/gles3/shaders/conversions_vector_to_scalar.html [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/data/gles3/shaders/swizzles_vec4.html [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/functional/gles3/draw/draw_elements_instanced.html [ Failure ]
+crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/functional/gles3/fbocolorbuffer/tex3d_04.html [ RetryOnFailure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/functional/gles3/fborender/recreate_color_02.html [ Failure ]
+crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/functional/gles3/texturefiltering/3d_formats_03.html [ RetryOnFailure ]
+crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/functional/gles3/texturefiltering/cube_combinations_05.html [ RetryOnFailure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/functional/gles3/texturespecification/basic_teximage3d_2d_array_02.html [ Failure ]
 
 ## Metal Intel ##
@@ -385,7 +388,7 @@
 crbug.com/angleproject/8003 [ mac passthrough angle-metal intel ] deqp/functional/gles3/uniformbuffers/single_nested_struct.html [ Failure ]
 crbug.com/angleproject/8003 [ mac passthrough angle-metal intel ] deqp/functional/gles3/uniformbuffers/single_nested_struct_array.html [ Failure ]
 # finder:group-end
-crbug.com/angleproject/8026 [ mac passthrough angle-metal intel ] conformance2/uniforms/dependent-buffer-change.html [ Failure ]
+crbug.com/angleproject/8026 [ angle-metal intel monterey passthrough ] conformance2/uniforms/dependent-buffer-change.html [ Failure ]
 
 ## Metal M1 ##
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 0b24a0f..8bbe1ec3 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -450,6 +450,7 @@
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/ogles/GL/lessThan/lessThan_001_to_008.html [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/ogles/GL/vec/vec_017_to_018.html [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/textures/misc/texture-size-cube-maps.html [ Failure ]
+crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/textures/misc/tex-video-using-tex-unit-non-zero.html [ RetryOnFailure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] conformance/textures/webgl_canvas/* [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/data/gles2/shaders/swizzles_bvec4.html [ Failure ]
 crbug.com/1421053 [ mac amd-0x67ef angle-metal ] deqp/data/gles2/shaders/swizzles_vec2.html [ Failure ]
@@ -565,7 +566,7 @@
 crbug.com/1383180 [ android android-pixel-6 ] conformance/extensions/oes-texture-float-with-video.html [ RetryOnFailure ]
 crbug.com/1383180 [ android android-pixel-6 target-cpu-64 ] conformance/textures/misc/texture-video-transparent.html [ RetryOnFailure ]
 crbug.com/1383180 [ android android-pixel-6 ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-crbug.com/1383180 [ android android-pixel-6 ] conformance/textures/video/* [ RetryOnFailure ]
+crbug.com/1383180 [ android android-pixel-6 angle-opengles passthrough ] conformance/textures/video/* [ RetryOnFailure ]
 # finder:group-end
 
 ## Misc failures ##
diff --git a/extensions/browser/extension_service_worker_message_filter.cc b/extensions/browser/extension_service_worker_message_filter.cc
index cc7df0e..2e3fd684 100644
--- a/extensions/browser/extension_service_worker_message_filter.cc
+++ b/extensions/browser/extension_service_worker_message_filter.cc
@@ -19,8 +19,6 @@
 #include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_factory.h"
-#include "extensions/browser/process_map.h"
-#include "extensions/browser/service_worker_task_queue.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/mojom/frame.mojom.h"
 
@@ -91,8 +89,6 @@
     content::BrowserThread::ID* thread) {
   if (message.type() == ExtensionHostMsg_RequestWorker::ID ||
       message.type() == ExtensionHostMsg_EventAckWorker::ID ||
-      message.type() == ExtensionHostMsg_DidStartServiceWorkerContext::ID ||
-      message.type() == ExtensionHostMsg_DidStopServiceWorkerContext::ID ||
       message.type() == ExtensionHostMsg_WorkerResponseAck::ID) {
     *thread = content::BrowserThread::UI;
   }
@@ -113,10 +109,6 @@
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementServiceWorkerActivity,
                         OnDecrementServiceWorkerActivity)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_EventAckWorker, OnEventAckWorker)
-    IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidStartServiceWorkerContext,
-                        OnDidStartServiceWorkerContext)
-    IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidStopServiceWorkerContext,
-                        OnDidStopServiceWorkerContext)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_WorkerResponseAck, OnResponseWorker)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
@@ -200,54 +192,6 @@
                          this));
 }
 
-void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext(
-    const ExtensionId& extension_id,
-    const base::UnguessableToken& activation_sequence,
-    const GURL& service_worker_scope,
-    int64_t service_worker_version_id,
-    int thread_id) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!browser_context_)
-    return;
-  DCHECK_NE(kMainThreadId, thread_id);
-  if (!ProcessMap::Get(browser_context_)
-           ->Contains(extension_id, render_process_id_)) {
-    // We can legitimately get here if the extension was already unloaded.
-    return;
-  }
-  CHECK(service_worker_scope.SchemeIs(kExtensionScheme) &&
-        extension_id == service_worker_scope.host_piece());
-
-  ServiceWorkerTaskQueue::Get(browser_context_)
-      ->DidStartServiceWorkerContext(render_process_id_, extension_id,
-                                     activation_sequence, service_worker_scope,
-                                     service_worker_version_id, thread_id);
-}
-
-void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext(
-    const ExtensionId& extension_id,
-    const base::UnguessableToken& activation_sequence,
-    const GURL& service_worker_scope,
-    int64_t service_worker_version_id,
-    int thread_id) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!browser_context_)
-    return;
-  DCHECK_NE(kMainThreadId, thread_id);
-  if (!ProcessMap::Get(browser_context_)
-           ->Contains(extension_id, render_process_id_)) {
-    // We can legitimately get here if the extension was already unloaded.
-    return;
-  }
-  CHECK(service_worker_scope.SchemeIs(kExtensionScheme) &&
-        extension_id == service_worker_scope.host_piece());
-
-  ServiceWorkerTaskQueue::Get(browser_context_)
-      ->DidStopServiceWorkerContext(render_process_id_, extension_id,
-                                    activation_sequence, service_worker_scope,
-                                    service_worker_version_id, thread_id);
-}
-
 void ExtensionServiceWorkerMessageFilter::DidFailDecrementInflightEvent() {
   bad_message::ReceivedBadMessage(this, bad_message::ESWMF_BAD_EVENT_ACK);
 }
diff --git a/extensions/browser/extension_service_worker_message_filter.h b/extensions/browser/extension_service_worker_message_filter.h
index fd06919..b478fb8 100644
--- a/extensions/browser/extension_service_worker_message_filter.h
+++ b/extensions/browser/extension_service_worker_message_filter.h
@@ -16,12 +16,6 @@
 #include "extensions/common/extension_id.h"
 #include "extensions/common/mojom/frame.mojom-forward.h"
 
-class GURL;
-
-namespace base {
-class UnguessableToken;
-}
-
 namespace content {
 class BrowserContext;
 class ServiceWorkerContext;
@@ -73,18 +67,6 @@
                         int64_t service_worker_version_id,
                         int thread_id,
                         int event_id);
-  void OnDidStartServiceWorkerContext(
-      const ExtensionId& extension_id,
-      const base::UnguessableToken& activation_sequence,
-      const GURL& service_worker_scope,
-      int64_t service_worker_version_id,
-      int thread_id);
-  void OnDidStopServiceWorkerContext(
-      const ExtensionId& extension_id,
-      const base::UnguessableToken& activation_sequence,
-      const GURL& service_worker_scope,
-      int64_t service_worker_version_id,
-      int thread_id);
 
   void DidFailDecrementInflightEvent();
 
diff --git a/extensions/browser/service_worker/service_worker_host.cc b/extensions/browser/service_worker/service_worker_host.cc
index a612e35..f99b177 100644
--- a/extensions/browser/service_worker/service_worker_host.cc
+++ b/extensions/browser/service_worker/service_worker_host.cc
@@ -9,6 +9,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/process_map.h"
 #include "extensions/browser/service_worker_task_queue.h"
+#include "extensions/common/constants.h"
 
 namespace extensions {
 
@@ -77,6 +78,56 @@
                                           worker_thread_id);
 }
 
+void ServiceWorkerHost::DidStartServiceWorkerContext(
+    const ExtensionId& extension_id,
+    const base::UnguessableToken& activation_token,
+    const GURL& service_worker_scope,
+    int64_t service_worker_version_id,
+    int worker_thread_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK_NE(kMainThreadId, worker_thread_id);
+
+  content::BrowserContext* browser_context = GetBrowserContext();
+  int render_process_id = render_process_host_->GetID();
+  if (!ProcessMap::Get(browser_context)
+           ->Contains(extension_id, render_process_id)) {
+    // We can legitimately get here if the extension was already unloaded.
+    return;
+  }
+  CHECK(service_worker_scope.SchemeIs(kExtensionScheme) &&
+        extension_id == service_worker_scope.host_piece());
+
+  ServiceWorkerTaskQueue::Get(browser_context)
+      ->DidStartServiceWorkerContext(
+          render_process_id, extension_id, activation_token,
+          service_worker_scope, service_worker_version_id, worker_thread_id);
+}
+
+void ServiceWorkerHost::DidStopServiceWorkerContext(
+    const ExtensionId& extension_id,
+    const base::UnguessableToken& activation_token,
+    const GURL& service_worker_scope,
+    int64_t service_worker_version_id,
+    int worker_thread_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK_NE(kMainThreadId, worker_thread_id);
+
+  content::BrowserContext* browser_context = GetBrowserContext();
+  int render_process_id = render_process_host_->GetID();
+  if (!ProcessMap::Get(browser_context)
+           ->Contains(extension_id, render_process_id)) {
+    // We can legitimately get here if the extension was already unloaded.
+    return;
+  }
+  CHECK(service_worker_scope.SchemeIs(kExtensionScheme) &&
+        extension_id == service_worker_scope.host_piece());
+
+  ServiceWorkerTaskQueue::Get(browser_context)
+      ->DidStopServiceWorkerContext(
+          render_process_id, extension_id, activation_token,
+          service_worker_scope, service_worker_version_id, worker_thread_id);
+}
+
 content::BrowserContext* ServiceWorkerHost::GetBrowserContext() {
   return render_process_host_->GetBrowserContext();
 }
diff --git a/extensions/browser/service_worker/service_worker_host.h b/extensions/browser/service_worker/service_worker_host.h
index 1107ccb2..659f250 100644
--- a/extensions/browser/service_worker/service_worker_host.h
+++ b/extensions/browser/service_worker/service_worker_host.h
@@ -11,6 +11,12 @@
 #include "extensions/common/mojom/service_worker_host.mojom.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 
+class GURL;
+
+namespace base {
+class UnguessableToken;
+}
+
 namespace content {
 class BrowserContext;
 class RenderProcessHost;
@@ -36,6 +42,18 @@
   void DidInitializeServiceWorkerContext(const ExtensionId& extension_id,
                                          int64_t service_worker_version_id,
                                          int worker_thread_id) override;
+  void DidStartServiceWorkerContext(
+      const ExtensionId& extension_id,
+      const base::UnguessableToken& activation_token,
+      const GURL& service_worker_scope,
+      int64_t service_worker_version_id,
+      int worker_thread_id) override;
+  void DidStopServiceWorkerContext(
+      const ExtensionId& extension_id,
+      const base::UnguessableToken& activation_token,
+      const GURL& service_worker_scope,
+      int64_t service_worker_version_id,
+      int worker_thread_id) override;
 
  private:
   // Returns the browser context associated with the render process this
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 995a8d0..69f4fbb6 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -455,41 +455,6 @@
                      int /* worker_thread_id */,
                      int /* event_id */)
 
-// Tells the browser that an extension service worker context has started and
-// finished executing its top-level JavaScript.
-// Start corresponds to EmbeddedWorkerInstance::OnStarted notification.
-//
-// TODO(lazyboy): This is a workaround: ideally this IPC should be redundant
-// because it directly corresponds to EmbeddedWorkerInstance::OnStarted message.
-// However, because OnStarted message is on different mojo IPC pipe, and most
-// extension IPCs are on legacy IPC pipe, this IPC is necessary to ensure FIFO
-// ordering of this message with rest of the extension IPCs.
-// Two possible solutions to this:
-//   - Associate extension IPCs with Service Worker IPCs. This can be done (and
-//     will be a requirement) when extension IPCs are moved to mojo, but
-//     requires resolving or defining ordering dependencies amongst the
-//     extension messages, and any additional messages in Chrome.
-//   - Make Service Worker IPCs channel-associated so that there's FIFO
-//     guarantee between extension IPCs and Service Worker IPCs. This isn't
-//     straightforward as it changes SW IPC ordering with respect of rest of
-//     Chrome.
-// See https://crbug.com/879015#c4 for details.
-IPC_MESSAGE_CONTROL5(ExtensionHostMsg_DidStartServiceWorkerContext,
-                     std::string /* extension_id */,
-                     base::UnguessableToken /* activation_sequence */,
-                     GURL /* service_worker_scope */,
-                     int64_t /* service_worker_version_id */,
-                     int /* worker_thread_id */)
-
-// Tells the browser that an extension service worker context has been
-// destroyed.
-IPC_MESSAGE_CONTROL5(ExtensionHostMsg_DidStopServiceWorkerContext,
-                     std::string /* extension_id */,
-                     base::UnguessableToken /* activation_sequence */,
-                     GURL /* service_worker_scope */,
-                     int64_t /* service_worker_version_id */,
-                     int /* worker_thread_id */)
-
 // Optional Ack message sent to the browser to notify that the response to a
 // function has been processed.
 IPC_MESSAGE_CONTROL2(ExtensionHostMsg_WorkerResponseAck,
diff --git a/extensions/common/mojom/service_worker_host.mojom b/extensions/common/mojom/service_worker_host.mojom
index caf48a3..503918bc 100644
--- a/extensions/common/mojom/service_worker_host.mojom
+++ b/extensions/common/mojom/service_worker_host.mojom
@@ -4,6 +4,9 @@
 
 module extensions.mojom;
 
+import "mojo/public/mojom/base/unguessable_token.mojom";
+import "url/mojom/url.mojom";
+
 // An interface for an extension service worker context. Implemented in the
 // browser process.
 interface ServiceWorkerHost {
@@ -12,4 +15,40 @@
   DidInitializeServiceWorkerContext(string extension_id,
                                     int64 service_worker_version_id,
                                     int32 worker_thread_id);
+
+  // Tells the browser that an extension service worker context has started and
+  // finished executing its top-level JavaScript.
+  // Start corresponds to EmbeddedWorkerInstance::OnStarted notification.
+  //
+  // TODO(crbug.com/1422440): This is a workaround: ideally this IPC should be
+  // redundant because it directly corresponds to
+  // EmbeddedWorkerInstance::OnStarted message. However, because OnStarted
+  // message is on different mojo IPC pipe, and most extension IPCs are on
+  // legacy IPC pipe, this IPC is necessary to ensure FIFO ordering of this
+  // message with rest of the extension IPCs.
+  // Two possible solutions to this:
+  //   - Associate extension IPCs with Service Worker IPCs. This can be done
+  //     (and will be a requirement) when extension IPCs are moved to mojo, but
+  //     requires resolving or defining ordering dependencies amongst the
+  //     extension messages, and any additional messages in Chrome.
+  //   - Make Service Worker IPCs channel-associated so that there's FIFO
+  //     guarantee between extension IPCs and Service Worker IPCs. This isn't
+  //     straightforward as it changes SW IPC ordering with respect of rest of
+  //     Chrome.
+  // See https://crbug.com/879015#c4 for details.
+  DidStartServiceWorkerContext(
+      string extension_id,
+      mojo_base.mojom.UnguessableToken activation_token,
+      url.mojom.Url service_worker_scope,
+      int64 service_worker_version_id,
+      int32 worker_thread_id);
+
+  // Tells the browser that an extension service worker context has been
+  // destroyed.
+  DidStopServiceWorkerContext(
+      string extension_id,
+      mojo_base.mojom.UnguessableToken activation_token,
+      url.mojom.Url service_worker_scope,
+      int64 service_worker_version_id,
+      int32 worker_thread_id);
 };
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc
index a6c5a7d..24dcb13c 100644
--- a/extensions/renderer/worker_thread_dispatcher.cc
+++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -516,7 +516,17 @@
             service_worker_data->service_worker_version_id());
   const int thread_id = content::WorkerThread::GetCurrentId();
   DCHECK_NE(thread_id, kMainThreadId);
-  Send(new ExtensionHostMsg_DidStartServiceWorkerContext(
+  PostTaskToIOThread(base::BindOnce(
+      [](const ExtensionId& extension_id,
+         const base::UnguessableToken& activation_token,
+         const GURL& service_worker_scope, int64_t service_worker_version_id,
+         int thread_id) {
+        WorkerThreadDispatcher::Get()
+            ->GetServiceWorkerHostOnIO()
+            ->DidStartServiceWorkerContext(
+                extension_id, activation_token, service_worker_scope,
+                service_worker_version_id, thread_id);
+      },
       service_worker_data->context()->GetExtensionID(),
       service_worker_data->activation_sequence(), service_worker_scope,
       service_worker_version_id, thread_id));
@@ -528,7 +538,17 @@
   DCHECK_NE(thread_id, kMainThreadId);
   DCHECK_EQ(service_worker_version_id,
             service_worker_data->service_worker_version_id());
-  Send(new ExtensionHostMsg_DidStopServiceWorkerContext(
+  PostTaskToIOThread(base::BindOnce(
+      [](const ExtensionId& extension_id,
+         const base::UnguessableToken& activation_token,
+         const GURL& service_worker_scope, int64_t service_worker_version_id,
+         int thread_id) {
+        WorkerThreadDispatcher::Get()
+            ->GetServiceWorkerHostOnIO()
+            ->DidStopServiceWorkerContext(extension_id, activation_token,
+                                          service_worker_scope,
+                                          service_worker_version_id, thread_id);
+      },
       service_worker_data->context()->GetExtensionID(),
       service_worker_data->activation_sequence(), service_worker_scope,
       service_worker_version_id, thread_id));
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index f69780f..b485e6f 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1421,6 +1421,11 @@
      flag_descriptions::kEnableFollowManagementInstantReloadDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kEnableFollowManagementInstantReload)},
+    {"mixed-content-autoupgrade-ios",
+     flag_descriptions::kMixedContentAutoupgradeName,
+     flag_descriptions::kMixedContentAutoupgradeDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(
+         security_interstitials::features::kMixedContentAutoupgrade)},
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index c48aba3f..742515d 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -561,6 +561,10 @@
     "Enables sending Metrickit reports for non crash type (hang, "
     "cpu-exception, diskwrite-exception)";
 
+const char kMixedContentAutoupgradeName[] = "Auto-upgrade mixed content";
+const char kMixedContentAutoupgradeDescription[] =
+    "Enables auto-upgrading of mixed content images, audio and video";
+
 const char kModernTabStripName[] = "Modern TabStrip";
 const char kModernTabStripDescription[] =
     "When enabled, the newly implemented tabstrip can be tested.";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 99c0bef8..9f5c061 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -492,6 +492,10 @@
 extern const char kMetrickitNonCrashReportName[];
 extern const char kMetrickitNonCrashReportDescription[];
 
+// Title and description for the flag to enable Mixed Content auto-upgrading.
+extern const char kMixedContentAutoupgradeName[];
+extern const char kMixedContentAutoupgradeDescription[];
+
 // TODO(crbug.com/1128242): Remove this flag after the refactoring work is
 // finished.
 // Title and description for the flag used to test the newly
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
index 7b2d403..eb8d1a6 100644
--- a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
+++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
@@ -48,8 +48,7 @@
   // Returns true if an `url` is either chrome://newtab or about://newtab.
   bool IsNTPURL(const GURL& url);
 
-  // Returns the initially selected feed for the next NTP and then resets it to
-  // default.
+  // Returns the selected feed for the next NTP.
   FeedType GetNextNTPFeedType();
 
   // Sets the default feed for the next NTP.
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm b/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm
index a940938e..7aeee9d 100644
--- a/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm
+++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm
@@ -88,11 +88,7 @@
 }
 
 FeedType NewTabPageTabHelper::GetNextNTPFeedType() {
-  FeedType feed_type = next_ntp_feed_type_;
-  // Resets feed type to default in case next_ntp_feed_type_ was overriden by
-  // SetNextNTPFeedType.
-  next_ntp_feed_type_ = DefaultFeedType();
-  return feed_type;
+  return next_ntp_feed_type_;
 }
 
 void NewTabPageTabHelper::SetNextNTPFeedType(FeedType feed_type) {
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 8e975d2..f58ecb0 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -2553,8 +2553,13 @@
   NewTabPageCoordinator* NTPCoordinator = self.NTPCoordinator;
   DCHECK(NTPCoordinator);
   if (NTPHelper->IsActive()) {
-    [NTPCoordinator start];
-    [NTPCoordinator didNavigateToNTP];
+    // Starting the NTPCoordinator triggers its visibility, so we only
+    // explicitly call `didNavigateToNTP` if the NTP was already started.
+    if (NTPCoordinator.started) {
+      [NTPCoordinator didNavigateToNTP];
+    } else {
+      [NTPCoordinator start];
+    }
   } else {
     [NTPCoordinator didNavigateAwayFromNTP];
   }
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm
index 4eede526..ebea68c 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm
@@ -315,8 +315,7 @@
   // Insert the web_state into the Browser.
   InsertWebState();
 
-  // Open a NTP and expect a call to the NTP coordinator.
-  [[mockNTPCoordinator expect] didNavigateToNTP];
+  // Open an NTP to start the coordinator.
   OpenURL(GURL("chrome://newtab/"));
   EXPECT_OCMOCK_VERIFY(mockNTPCoordinator);
 
@@ -325,5 +324,10 @@
   OpenURL(GURL("chrome://version/"));
   EXPECT_OCMOCK_VERIFY(mockNTPCoordinator);
 
+  // Open another NTP and expect a navigation call.
+  [[mockNTPCoordinator expect] didNavigateToNTP];
+  OpenURL(GURL("chrome://newtab/"));
+  EXPECT_OCMOCK_VERIFY(mockNTPCoordinator);
+
   [browser_coordinator stop];
 }
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index da2efbe..c764ab9 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -301,6 +301,12 @@
     return;
   }
 
+  NewTabPageTabHelper* NTPHelper =
+      NewTabPageTabHelper::FromWebState(self.webState);
+  if (NTPHelper) {
+    self.selectedFeed = NTPHelper->GetNextNTPFeedType();
+  }
+
   // NOTE: anything that executes below WILL NOT execute for OffTheRecord
   // browsers!
 
@@ -1608,6 +1614,12 @@
 
     if (visible) {
       if ([self isFollowingFeedAvailable]) {
+        NewTabPageTabHelper* helper =
+            NewTabPageTabHelper::FromWebState(self.webState);
+        self.shouldScrollIntoFeed = helper->GetNextNTPScrolledToFeed();
+        [self selectFeedType:helper->GetNextNTPFeedType()];
+        helper->SetNextNTPFeedType(NewTabPageTabHelper::DefaultFeedType());
+
         self.NTPViewController.shouldScrollIntoFeed = self.shouldScrollIntoFeed;
         // Reassign the sort type in case it changed in another tab.
         self.feedHeaderViewController.followingFeedSortType =
@@ -1619,10 +1631,6 @@
         self.feedMetricsRecorder.feedControlDelegate = self;
         self.feedMetricsRecorder.followDelegate = self;
       }
-      NewTabPageTabHelper* helper =
-          NewTabPageTabHelper::FromWebState(self.webState);
-      self.shouldScrollIntoFeed = helper->GetNextNTPScrolledToFeed();
-      [self selectFeedType:helper->GetNextNTPFeedType()];
     } else {
       // Unfocus omnibox, to prevent it from lingering when it should be
       // dismissed (for example, when navigating away or when changing feed
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
index 5e29baf..26b21cb 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/overflow_menu_mediator.mm
@@ -1723,6 +1723,7 @@
 
 // Dismisses the menu and pins the tab.
 - (void)pinTab {
+  RecordAction(UserMetricsAction("MobileMenuPinTab"));
   [[self class] setTabPinned:YES
                     webState:self.webState
                 webStateList:self.webStateList];
@@ -1733,6 +1734,7 @@
 
 // Dismisses the menu and unpins the tab.
 - (void)unpinTab {
+  RecordAction(UserMetricsAction("MobileMenuUnpinTab"));
   [[self class] setTabPinned:NO
                     webState:self.webState
                 webStateList:self.webStateList];
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
index 37cfe6c..41b9a2f 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.mm
@@ -67,6 +67,9 @@
 #error "This file requires ARC support."
 #endif
 
+using base::RecordAction;
+using base::UserMetricsAction;
+
 namespace {
 // Returns the corresponding command type for a Popup menu `type`.
 PopupMenuCommandType CommandTypeFromPopupType(PopupMenuType type) {
@@ -180,15 +183,13 @@
 #pragma mark - PopupMenuCommands
 
 - (void)showNavigationHistoryBackPopupMenu {
-  base::RecordAction(
-      base::UserMetricsAction("MobileToolbarShowTabHistoryMenu"));
+  RecordAction(UserMetricsAction("MobileToolbarShowTabHistoryMenu"));
   [self presentPopupOfType:PopupMenuTypeNavigationBackward
       fromLayoutGuideNamed:kBackButtonGuide];
 }
 
 - (void)showNavigationHistoryForwardPopupMenu {
-  base::RecordAction(
-      base::UserMetricsAction("MobileToolbarShowTabHistoryMenu"));
+  RecordAction(UserMetricsAction("MobileToolbarShowTabHistoryMenu"));
   [self presentPopupOfType:PopupMenuTypeNavigationForward
       fromLayoutGuideNamed:kForwardButtonGuide];
 }
@@ -200,13 +201,13 @@
 }
 
 - (void)showTabGridButtonPopup {
-  base::RecordAction(base::UserMetricsAction("MobileToolbarShowTabGridMenu"));
+  RecordAction(UserMetricsAction("MobileToolbarShowTabGridMenu"));
   [self presentPopupOfType:PopupMenuTypeTabGrid
       fromLayoutGuideNamed:kTabSwitcherGuide];
 }
 
 - (void)showNewTabButtonPopup {
-  base::RecordAction(base::UserMetricsAction("MobileToolbarShowNewTabMenu"));
+  RecordAction(UserMetricsAction("MobileToolbarShowNewTabMenu"));
   [self presentPopupOfType:PopupMenuTypeNewTab
       fromLayoutGuideNamed:kNewTabButtonGuide];
 }
@@ -263,7 +264,6 @@
 - (void)showSnackbarForPinnedState:(BOOL)pinnedState
                           webState:(web::WebState*)webState {
   DCHECK(IsPinnedTabsOverflowEnabled());
-
   int messageId = pinnedState ? IDS_IOS_SNACKBAR_MESSAGE_PINNED_TAB
                               : IDS_IOS_SNACKBAR_MESSAGE_UNPINNED_TAB;
 
@@ -271,6 +271,12 @@
   base::WeakPtr<Browser> weakBrowser = self.browser->AsWeakPtr();
 
   void (^undoAction)() = ^{
+    if (pinnedState) {
+      RecordAction(UserMetricsAction("MobileSnackbarUndoPinAction"));
+    } else {
+      RecordAction(UserMetricsAction("MobileSnackbarUndoUnpinAction"));
+    }
+
     Browser* browser = weakBrowser.get();
     if (!browser) {
       return;
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 8c092d3..8ae8e5c 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-9e7419ddb70dca4ea5cb46bc6758535802aeb76d
\ No newline at end of file
+31703878e0c49a0c5bb12360115a30cb1d4c3867
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 5bd0f3d..37ef4e28 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-52598abe454583d9f008cfe2c9c8640919a5b70c
\ No newline at end of file
+eac9702002f2de4ef473c123abd5a6d47c2e9eae
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index df7cbd0..c20aa92c3 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-8aaffc053002390fa80e93b5b27e1fd1d56249e9
\ No newline at end of file
+0ff175cbe3f43b7fb16fd5ddfdfb5a71539d0d4a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index ae05a42f..16909af 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9afcb5db6cecfa45d4ba93a0927305e015e37138
\ No newline at end of file
+357f458911ce4ce9337aca672ce160103d860107
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index a33d9868c..cf38433 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-42139585f3c64c518fb18b163c05b3569d441780
\ No newline at end of file
+8f5a00a6f81432ed7c997c0715ce525c685c9b97
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 159e38a..5bbe48f 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b3954b81373d108e6cac3849af4be4925ee2ef0d
\ No newline at end of file
+3ee2ad022f9d249588a6fee7f51682e301b35e76
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 75704fea3..2e0e3362 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b3609b58af24c9e45ed23a6f26f744b1cf0f88df
\ No newline at end of file
+7fd00011e08175125cdcd016f2951fb9c0168654
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index c59f6f3..5b87d97d 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-6acdf4c88aec99eca7cda292d7c7cbd7a0547d35
\ No newline at end of file
+601560381cc6a595aa348f913d9f32e3f7d0a451
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index bbc3169..91b4ae5f 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e1db7a7da5d8d8d5de3e134028154d0496a89ce9
\ No newline at end of file
+84f6fc35741a415508ea6db647a8d8d59fbaae95
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 056134a7..23a60d5 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-234bc216b2b179d95bd7c55efddb2e1b74b0b95f
\ No newline at end of file
+44d40ad74e727f86410d339b499cff5d51863192
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index e85db3c..bc79b5b5 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-da738168a48d5caedc6a5f6bae6aae5c2e6107e1
\ No newline at end of file
+605377a6816d837b379a44533cfdaf677b65cd28
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 81f9c99..5e980e46 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b84ab689ddb98f7fb96bb49a844a44fafed4cb74
\ No newline at end of file
+e42fe498427cc421a323c762894221f121bf454d
\ No newline at end of file
diff --git a/services/device/geolocation/network_location_request.cc b/services/device/geolocation/network_location_request.cc
index 14c47ad..75795bc 100644
--- a/services/device/geolocation/network_location_request.cc
+++ b/services/device/geolocation/network_location_request.cc
@@ -71,11 +71,6 @@
                            code);
 }
 
-void RecordUmaNetError(int net_error) {
-  base::UmaHistogramSparse("Geolocation.NetworkLocationRequest.NetError",
-                           -net_error);
-}
-
 void RecordUmaAccessPoints(int count) {
   const int min = 1;
   const int max = 20;
@@ -329,7 +324,6 @@
                         "DevTools console for more information.",
                         net::ErrorToShortString(net_error), position);
     RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
-    RecordUmaNetError(net_error);
     return;
   }
 
diff --git a/services/tracing/perfetto/privacy_filtered_fields-inl.h b/services/tracing/perfetto/privacy_filtered_fields-inl.h
index 1a1754b..a3ab5be 100644
--- a/services/tracing/perfetto/privacy_filtered_fields-inl.h
+++ b/services/tracing/perfetto/privacy_filtered_fields-inl.h
@@ -178,7 +178,7 @@
 constexpr MessageInfo kChromeMessagePump = {kChromeMessagePumpIndices, nullptr};
 
 // Proto Message: ChromeMojoEventInfo
-constexpr int kChromeMojoEventInfoIndices[] = {1, 2, 3, 4, 6, 7, -1};
+constexpr int kChromeMojoEventInfoIndices[] = {1, 2, 3, 4, 5, 6, 7, -1};
 constexpr MessageInfo kChromeMojoEventInfo = {kChromeMojoEventInfoIndices,
                                               nullptr};
 
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index cefb615..1451f4585 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -14,7 +14,6 @@
 #include "components/viz/common/quads/compositor_render_pass.h"
 #include "components/viz/common/quads/debug_border_draw_quad.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
-#include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/resources/resource_format.h"
 #include "components/viz/common/resources/resource_settings.h"
 #include "components/viz/common/resources/returned_resource.h"
@@ -1047,40 +1046,16 @@
   const gfx::Rect rect6(321, 765, 11109, 151413);
   const bool needs_blending6 = false;
   const ResourceId resource_id6(1234);
-  const gfx::Size resource_size_in_pixels6(1234, 5678);
+  const gfx::Size resource_size_in_pixels(1234, 5678);
   const float stream_draw_quad_opacity[] = {1, 1, 1, 1};
   TextureDrawQuad* stream_video_draw_quad =
       render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   stream_video_draw_quad->SetAll(
-      sqs, rect6, rect6, needs_blending6, resource_id6,
-      resource_size_in_pixels6, false, uv_top_left, uv_bottom_right,
-      SkColors::kTransparent, stream_draw_quad_opacity, false, false, false,
-      protected_video_type);
+      sqs, rect6, rect6, needs_blending6, resource_id6, resource_size_in_pixels,
+      false, uv_top_left, uv_bottom_right, SkColors::kTransparent,
+      stream_draw_quad_opacity, false, false, false, protected_video_type);
   stream_video_draw_quad->is_stream_video = true;
 
-  // Create a TextureDrawQuad with rounded-display masks.
-  const gfx::Rect rect7(421, 865, 11109, 151413);
-  const bool needs_blending7 = false;
-  const ResourceId resource_id7(4834);
-  const gfx::Size resource_size_in_pixels7(12894, 8878);
-  const float rounded_display_mask_quad_opacity[] = {1.0, 1.5, 1.8, 1.1};
-  const int origin_rounded_display_mask_radius = 10;
-  const int other_rounded_display_mask_radius = 15;
-  const bool is_horizontally_positioned = false;
-
-  TextureDrawQuad* rounded_display_mask_quad =
-      render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
-  rounded_display_mask_quad->SetAll(
-      sqs, rect7, rect7, needs_blending7, resource_id7,
-      resource_size_in_pixels7, false, uv_top_left, uv_bottom_right,
-      SkColors::kTransparent, rounded_display_mask_quad_opacity, false, false,
-      false, protected_video_type);
-
-  rounded_display_mask_quad->rounded_display_masks_info =
-      TextureDrawQuad::RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(
-          origin_rounded_display_mask_radius, other_rounded_display_mask_radius,
-          is_horizontally_positioned);
-
   std::unique_ptr<CompositorRenderPass> output;
   mojo::test::SerializeAndDeserialize<mojom::CompositorRenderPass>(render_pass,
                                                                    output);
@@ -1162,33 +1137,10 @@
   EXPECT_EQ(rect6, out_stream_video_draw_quad->visible_rect);
   EXPECT_EQ(needs_blending6, out_stream_video_draw_quad->needs_blending);
   EXPECT_EQ(resource_id6, out_stream_video_draw_quad->resource_id());
-  EXPECT_EQ(resource_size_in_pixels6,
+  EXPECT_EQ(resource_size_in_pixels,
             out_stream_video_draw_quad->resource_size_in_pixels());
   EXPECT_EQ(uv_top_left, out_stream_video_draw_quad->uv_top_left);
   EXPECT_EQ(uv_bottom_right, out_stream_video_draw_quad->uv_bottom_right);
-
-  const TextureDrawQuad* out_rounded_display_mask_quad =
-      TextureDrawQuad::MaterialCast(output->quad_list.ElementAt(6));
-  EXPECT_FALSE(out_rounded_display_mask_quad->is_stream_video);
-  EXPECT_EQ(rect7, out_rounded_display_mask_quad->rect);
-  EXPECT_EQ(rect7, out_rounded_display_mask_quad->visible_rect);
-  EXPECT_EQ(needs_blending7, out_rounded_display_mask_quad->needs_blending);
-  EXPECT_EQ(resource_id7, out_rounded_display_mask_quad->resource_id());
-  EXPECT_EQ(resource_size_in_pixels7,
-            out_rounded_display_mask_quad->resource_size_in_pixels());
-  EXPECT_EQ(uv_top_left, out_rounded_display_mask_quad->uv_top_left);
-  EXPECT_EQ(uv_bottom_right, out_rounded_display_mask_quad->uv_bottom_right);
-  EXPECT_EQ(origin_rounded_display_mask_radius,
-            out_rounded_display_mask_quad->rounded_display_masks_info
-                .radii[TextureDrawQuad::RoundedDisplayMasksInfo::
-                           kOriginRoundedDisplayMaskIndex]);
-  EXPECT_EQ(other_rounded_display_mask_radius,
-            out_rounded_display_mask_quad->rounded_display_masks_info
-                .radii[TextureDrawQuad::RoundedDisplayMasksInfo::
-                           kOtherRoundedDisplayMaskIndex]);
-  EXPECT_EQ(is_horizontally_positioned,
-            out_rounded_display_mask_quad->rounded_display_masks_info
-                .is_horizontally_positioned);
 }
 
 TEST_F(StructTraitsTest, SurfaceId) {
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.cc b/services/viz/public/cpp/compositing/quads_mojom_traits.cc
index b0e2b973..e2667795 100644
--- a/services/viz/public/cpp/compositing/quads_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/quads_mojom_traits.cc
@@ -64,22 +64,6 @@
 }
 
 // static
-bool StructTraits<viz::mojom::RoundedDisplayMasksInfoDataView,
-                  viz::TextureDrawQuad::RoundedDisplayMasksInfo>::
-    Read(viz::mojom::RoundedDisplayMasksInfoDataView data,
-         viz::TextureDrawQuad::RoundedDisplayMasksInfo* out) {
-  viz::TextureDrawQuad::RoundedDisplayMasksInfo* info =
-      static_cast<viz::TextureDrawQuad::RoundedDisplayMasksInfo*>(out);
-  base::span<uint8_t> radii_array(info->radii);
-  if (!data.ReadRadii(&radii_array)) {
-    return false;
-  }
-
-  info->is_horizontally_positioned = data.is_horizontally_positioned();
-  return true;
-}
-
-// static
 bool StructTraits<viz::mojom::DebugBorderQuadStateDataView, viz::DrawQuad>::
     Read(viz::mojom::DebugBorderQuadStateDataView data, viz::DrawQuad* out) {
   viz::DebugBorderDrawQuad* quad = static_cast<viz::DebugBorderDrawQuad*>(out);
@@ -166,8 +150,7 @@
       !data.ReadProtectedVideoType(&protected_video_type) ||
       !data.ReadHdrMode(&quad->hdr_mode) ||
       !data.ReadHdrMetadata(&quad->hdr_metadata) ||
-      !data.ReadOverlayPriorityHint(&overlay_priority_hint) ||
-      !data.ReadRoundedDisplayMasksInfo(&quad->rounded_display_masks_info)) {
+      !data.ReadOverlayPriorityHint(&overlay_priority_hint)) {
     return false;
   }
   quad->protected_video_type = protected_video_type;
diff --git a/services/viz/public/cpp/compositing/quads_mojom_traits.h b/services/viz/public/cpp/compositing/quads_mojom_traits.h
index f7bd112e..39794cd 100644
--- a/services/viz/public/cpp/compositing/quads_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/quads_mojom_traits.h
@@ -111,23 +111,6 @@
 };
 
 template <>
-struct StructTraits<viz::mojom::RoundedDisplayMasksInfoDataView,
-                    viz::TextureDrawQuad::RoundedDisplayMasksInfo> {
-  static bool is_horizontally_positioned(
-      const viz::TextureDrawQuad::RoundedDisplayMasksInfo& input) {
-    return input.is_horizontally_positioned;
-  }
-
-  static base::span<const uint8_t> radii(
-      const viz::TextureDrawQuad::RoundedDisplayMasksInfo& input) {
-    return input.radii;
-  }
-
-  static bool Read(viz::mojom::RoundedDisplayMasksInfoDataView data,
-                   viz::TextureDrawQuad::RoundedDisplayMasksInfo* out);
-};
-
-template <>
 struct UnionTraits<viz::mojom::DrawQuadStateDataView, viz::DrawQuad> {
   static viz::mojom::DrawQuadStateDataView::Tag GetTag(
       const viz::DrawQuad& quad) {
@@ -413,13 +396,6 @@
     return quad->resource_size_in_pixels();
   }
 
-  static viz::TextureDrawQuad::RoundedDisplayMasksInfo
-  rounded_display_masks_info(const viz::DrawQuad& input) {
-    const viz::TextureDrawQuad* quad =
-        viz::TextureDrawQuad::MaterialCast(&input);
-    return quad->rounded_display_masks_info;
-  }
-
   static bool premultiplied_alpha(const viz::DrawQuad& input) {
     const viz::TextureDrawQuad* quad =
         viz::TextureDrawQuad::MaterialCast(&input);
diff --git a/services/viz/public/mojom/compositing/quads.mojom b/services/viz/public/mojom/compositing/quads.mojom
index abbccd5..8d4b472 100644
--- a/services/viz/public/mojom/compositing/quads.mojom
+++ b/services/viz/public/mojom/compositing/quads.mojom
@@ -28,11 +28,6 @@
   kRequired
 };
 
-struct RoundedDisplayMasksInfo {
-  bool is_horizontally_positioned;
-  array<uint8, 2> radii;
-};
-
 struct DebugBorderQuadState {
   // Debug border color.
   skia.mojom.SkColor4f color;
@@ -101,7 +96,6 @@
   gfx.mojom.HDRMetadata? hdr_metadata;
   gfx.mojom.Rect? damage_rect;
   OverlayPriority overlay_priority_hint;
-  RoundedDisplayMasksInfo rounded_display_masks_info;
 };
 
 struct TileQuadState {
diff --git a/storage/BUILD.gn b/storage/BUILD.gn
index e0aa65d..16efc11e 100644
--- a/storage/BUILD.gn
+++ b/storage/BUILD.gn
@@ -5,6 +5,7 @@
 
 test("storage_unittests") {
   deps = [
+    "//components/file_access:test_support",
     "//storage/browser:unittests",
     "//storage/common:unittests",
   ]
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index 5addf56..87ee276 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -351,6 +351,7 @@
     "//base/test:test_support",
     "//build:chromeos_buildflags",
     "//components/file_access:file_access",
+    "//components/file_access:test_support",
     "//components/services/filesystem/public/mojom",
     "//components/services/storage/public/cpp",
     "//mojo/public/cpp/system",
@@ -420,6 +421,7 @@
     ":browser",
     "//base/test:test_support",
     "//build:chromeos_buildflags",
+    "//components/file_access:file_access",
     "//components/services/storage/public/cpp",
     "//components/services/storage/public/mojom",
     "//net:test_support",
diff --git a/storage/browser/blob/blob_data_builder.cc b/storage/browser/blob/blob_data_builder.cc
index f79833f..2d18d91 100644
--- a/storage/browser/blob/blob_data_builder.cc
+++ b/storage/browser/blob/blob_data_builder.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/blob/blob_entry.h"
 #include "storage/browser/blob/blob_storage_registry.h"
 #include "storage/browser/blob/shareable_blob_data_item.h"
@@ -146,13 +147,16 @@
   return FutureFile(std::move(item));
 }
 
-void BlobDataBuilder::AppendFile(const FilePath& file_path,
-                                 uint64_t offset,
-                                 uint64_t length,
-                                 const base::Time& expected_modification_time) {
-  auto item = BlobDataItem::CreateFile(file_path, offset, length,
-                                       expected_modification_time,
-                                       ShareableFileReference::Get(file_path));
+void BlobDataBuilder::AppendFile(
+    const FilePath& file_path,
+    uint64_t offset,
+    uint64_t length,
+    const base::Time& expected_modification_time,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
+  auto item = BlobDataItem::CreateFile(
+      file_path, offset, length, expected_modification_time,
+      ShareableFileReference::Get(file_path), std::move(file_access));
   DCHECK(!item->IsFutureFileItem()) << file_path.value();
 
   auto shareable_item = base::MakeRefCounted<ShareableBlobDataItem>(
@@ -277,7 +281,8 @@
       case BlobDataItem::Type::kFile: {
         data_item = BlobDataItem::CreateFile(
             source_item->path(), source_item->offset() + item_offset, read_size,
-            source_item->expected_modification_time(), source_item->file_ref_);
+            source_item->expected_modification_time(), source_item->file_ref_,
+            source_item->file_access_);
 
         if (source_item->IsFutureFileItem()) {
           // The source file isn't a real file yet (path is fake), so store the
@@ -290,7 +295,7 @@
         data_item = BlobDataItem::CreateFileFilesystem(
             source_item->filesystem_url(), source_item->offset() + item_offset,
             read_size, source_item->expected_modification_time(),
-            source_item->file_system_context());
+            source_item->file_system_context(), source_item->file_access_);
         break;
       }
       case BlobDataItem::Type::kReadableDataHandle: {
@@ -320,11 +325,13 @@
     uint64_t offset,
     uint64_t length,
     const base::Time& expected_modification_time,
-    scoped_refptr<FileSystemContext> file_system_context) {
+    scoped_refptr<FileSystemContext> file_system_context,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
   DCHECK_GT(length, 0ul);
   auto item = BlobDataItem::CreateFileFilesystem(
       url, offset, length, expected_modification_time,
-      std::move(file_system_context));
+      std::move(file_system_context), std::move(file_access));
 
   auto shareable_item = base::MakeRefCounted<ShareableBlobDataItem>(
       std::move(item), ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA);
diff --git a/storage/browser/blob/blob_data_builder.h b/storage/browser/blob/blob_data_builder.h
index 1a2f9db..c439457 100644
--- a/storage/browser/blob/blob_data_builder.h
+++ b/storage/browser/blob/blob_data_builder.h
@@ -13,8 +13,10 @@
 
 #include "base/component_export.h"
 #include "base/files/file_path.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/numerics/checked_math.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
 #include "storage/browser/blob/blob_data_item.h"
 #include "storage/browser/blob/blob_data_snapshot.h"
@@ -132,11 +134,17 @@
 
   // You must know the length of the file, you cannot use kuint64max to specify
   // the whole file.  This method creates a ShareableFileReference to the given
-  // file, which is stored in this builder.
-  void AppendFile(const base::FilePath& file_path,
-                  uint64_t offset,
-                  uint64_t length,
-                  const base::Time& expected_modification_time);
+  // file, which is stored in this builder. The callback `file_access` is used
+  // to grant or deny access to files under dlp restrictions. Leaving it at
+  // NullCallback will lead to default behaviour, which currently is granting it
+  // (until b/265908846 is done).
+  void AppendFile(
+      const base::FilePath& file_path,
+      uint64_t offset,
+      uint64_t length,
+      const base::Time& expected_modification_time,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access = base::NullCallback());
 
   void AppendBlob(const std::string& uuid,
                   uint64_t offset,
@@ -150,7 +158,9 @@
       uint64_t offset,
       uint64_t length,
       const base::Time& expected_modification_time,
-      scoped_refptr<FileSystemContext> file_system_context);
+      scoped_refptr<FileSystemContext> file_system_context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access = base::NullCallback());
 
   void AppendReadableDataHandle(scoped_refptr<DataHandle> data_handle) {
     auto length = data_handle->GetSize();
diff --git a/storage/browser/blob/blob_data_item.cc b/storage/browser/blob/blob_data_item.cc
index 99061577..0bc997d 100644
--- a/storage/browser/blob/blob_data_item.cc
+++ b/storage/browser/blob/blob_data_item.cc
@@ -10,6 +10,8 @@
 
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/net_errors.h"
 #include "services/network/public/cpp/data_pipe_to_source_stream.h"
@@ -80,8 +82,12 @@
 }
 
 // static
-scoped_refptr<BlobDataItem> BlobDataItem::CreateFile(base::FilePath path) {
-  return CreateFile(path, 0, blink::BlobUtils::kUnknownSize);
+scoped_refptr<BlobDataItem> BlobDataItem::CreateFile(
+    base::FilePath path,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
+  return CreateFile(path, 0, blink::BlobUtils::kUnknownSize, base::Time(),
+                    nullptr, std::move(file_access));
 }
 
 // static
@@ -90,12 +96,15 @@
     uint64_t offset,
     uint64_t length,
     base::Time expected_modification_time,
-    scoped_refptr<ShareableFileReference> file_ref) {
+    scoped_refptr<ShareableFileReference> file_ref,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
   auto item =
       base::WrapRefCounted(new BlobDataItem(Type::kFile, offset, length));
   item->path_ = std::move(path);
   item->expected_modification_time_ = std::move(expected_modification_time);
   item->file_ref_ = std::move(file_ref);
+  item->file_access_ = std::move(file_access);
   // TODO(mek): DCHECK(!item->IsFutureFileItem()) when BlobDataBuilder has some
   // other way of slicing a future file.
   return item;
@@ -120,12 +129,15 @@
     uint64_t offset,
     uint64_t length,
     base::Time expected_modification_time,
-    scoped_refptr<FileSystemContext> file_system_context) {
+    scoped_refptr<FileSystemContext> file_system_context,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
   auto item = base::WrapRefCounted(
       new BlobDataItem(Type::kFileFilesystem, offset, length));
   item->filesystem_url_ = url;
   item->expected_modification_time_ = std::move(expected_modification_time);
   item->file_system_context_ = std::move(file_system_context);
+  item->file_access_ = std::move(file_access);
   return item;
 }
 
diff --git a/storage/browser/blob/blob_data_item.h b/storage/browser/blob/blob_data_item.h
index 261510a..6aa24b23 100644
--- a/storage/browser/blob/blob_data_item.h
+++ b/storage/browser/blob/blob_data_item.h
@@ -14,8 +14,11 @@
 #include "base/component_export.h"
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
+#include "components/file_access/scoped_file_access.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
 #include "net/base/io_buffer.h"
 #include "storage/browser/blob/shareable_file_reference.h"
@@ -83,13 +86,18 @@
   static scoped_refptr<BlobDataItem> CreateBytes(
       base::span<const uint8_t> bytes);
   static scoped_refptr<BlobDataItem> CreateBytesDescription(size_t length);
-  static scoped_refptr<BlobDataItem> CreateFile(base::FilePath path);
+  static scoped_refptr<BlobDataItem> CreateFile(
+      base::FilePath path,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access = base::NullCallback());
   static scoped_refptr<BlobDataItem> CreateFile(
       base::FilePath path,
       uint64_t offset,
       uint64_t length,
       base::Time expected_modification_time = base::Time(),
-      scoped_refptr<ShareableFileReference> file_ref = nullptr);
+      scoped_refptr<ShareableFileReference> file_ref = nullptr,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access = base::NullCallback());
   static scoped_refptr<BlobDataItem> CreateFutureFile(uint64_t offset,
                                                       uint64_t length,
                                                       uint64_t file_id);
@@ -98,7 +106,9 @@
       uint64_t offset,
       uint64_t length,
       base::Time expected_modification_time,
-      scoped_refptr<FileSystemContext> file_system_context);
+      scoped_refptr<FileSystemContext> file_system_context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access = base::NullCallback());
   static scoped_refptr<BlobDataItem> CreateReadableDataHandle(
       scoped_refptr<DataHandle> data_handle,
       uint64_t offset,
@@ -146,6 +156,12 @@
   // Returns |file_id| given to CreateFutureFile.
   uint64_t GetFutureFileID() const;
 
+  file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+  file_access() const {
+    DCHECK(type_ == Type::kFile || type_ == Type::kFileFilesystem);
+    return file_access_;
+  }
+
  private:
   friend class BlobBuilderFromStream;
   friend class BlobDataBuilder;
@@ -195,6 +211,9 @@
 
   scoped_refptr<FileSystemContext>
       file_system_context_;  // For Type::kFileFilesystem.
+
+  file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+      file_access_;  // For Type::kFile and kFileFilesystem.
 };
 
 COMPONENT_EXPORT(STORAGE_BROWSER)
diff --git a/storage/browser/blob/blob_reader.cc b/storage/browser/blob/blob_reader.cc
index f82c0cff..e9b8275 100644
--- a/storage/browser/blob/blob_reader.cc
+++ b/storage/browser/blob/blob_reader.cc
@@ -17,6 +17,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/task/thread_pool.h"
 #include "base/trace_event/trace_event.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "storage/browser/blob/blob_data_handle.h"
@@ -515,8 +516,7 @@
       GetOrCreateFileReaderAtIndex(current_item_index_);
   if (!reader)
     return ReportError(net::ERR_FILE_NOT_FOUND);
-
-  return ReadFileItem(reader, bytes_to_read);
+  return ReadFileItem(reader, bytes_to_read, item.file_access());
 }
 
 void BlobReader::AdvanceItem() {
@@ -560,8 +560,11 @@
   AdvanceBytesRead(bytes_to_read);
 }
 
-BlobReader::Status BlobReader::ReadFileItem(FileStreamReader* reader,
-                                            int bytes_to_read) {
+BlobReader::Status BlobReader::ReadFileItem(
+    FileStreamReader* reader,
+    int bytes_to_read,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!io_pending_)
       << "Can't begin IO while another IO operation is pending.";
@@ -725,7 +728,8 @@
       }
       return FileStreamReader::CreateForLocalFile(
           file_task_runner_.get(), item.path(),
-          item.offset() + additional_offset, item.expected_modification_time());
+          item.offset() + additional_offset, item.expected_modification_time(),
+          item.file_access());
     case BlobDataItem::Type::kFileFilesystem: {
       int64_t max_bytes_to_read =
           item.length() == std::numeric_limits<uint64_t>::max()
@@ -738,7 +742,8 @@
       }
       return item.file_system_context()->CreateFileStreamReader(
           item.filesystem_url(), item.offset() + additional_offset,
-          max_bytes_to_read, item.expected_modification_time());
+          max_bytes_to_read, item.expected_modification_time(),
+          item.file_access());
     }
     case BlobDataItem::Type::kBytes:
     case BlobDataItem::Type::kBytesDescription:
diff --git a/storage/browser/blob/blob_reader.h b/storage/browser/blob/blob_reader.h
index e5aba6f..af034fea 100644
--- a/storage/browser/blob/blob_reader.h
+++ b/storage/browser/blob/blob_reader.h
@@ -13,9 +13,13 @@
 #include <vector>
 
 #include "base/component_export.h"
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "components/file_access/scoped_file_access.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/completion_once_callback.h"
@@ -168,7 +172,7 @@
   FRIEND_TEST_ALL_PREFIXES(BlobReaderTest, HandleBeforeAsyncCancel);
   FRIEND_TEST_ALL_PREFIXES(BlobReaderTest, ReadFromIncompleteBlob);
 
-  BlobReader(const BlobDataHandle* blob_handle);
+  explicit BlobReader(const BlobDataHandle* blob_handle);
 
   bool total_size_calculated() const {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -208,7 +212,11 @@
   void AdvanceItem();
   void AdvanceBytesRead(int result);
   void ReadBytesItem(const BlobDataItem& item, int bytes_to_read);
-  BlobReader::Status ReadFileItem(FileStreamReader* reader, int bytes_to_read);
+  BlobReader::Status ReadFileItem(
+      FileStreamReader* reader,
+      int bytes_to_read,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access);
   void DidReadFile(int result);
   void DeleteItemReaders();
   Status ReadReadableDataHandle(const BlobDataItem& item, int bytes_to_read);
diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc
index 0bc74f9..b4a344568 100644
--- a/storage/browser/blob/blob_registry_impl.cc
+++ b/storage/browser/blob/blob_registry_impl.cc
@@ -8,9 +8,12 @@
 
 #include "base/barrier_closure.h"
 #include "base/functional/bind.h"
+#include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "base/task/sequenced_task_runner.h"
+#include "components/file_access/scoped_file_access.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "net/base/features.h"
 #include "storage/browser/blob/blob_builder_from_stream.h"
 #include "storage/browser/blob/blob_data_builder.h"
@@ -57,17 +60,21 @@
     mojo::Remote<blink::mojom::Blob> blob;
   };
 
-  BlobUnderConstruction(BlobRegistryImpl* blob_registry,
-                        const std::string& uuid,
-                        const std::string& content_type,
-                        const std::string& content_disposition,
-                        std::vector<ElementEntry> elements,
-                        mojo::ReportBadMessageCallback bad_message_callback)
+  BlobUnderConstruction(
+      BlobRegistryImpl* blob_registry,
+      const std::string& uuid,
+      const std::string& content_type,
+      const std::string& content_disposition,
+      std::vector<ElementEntry> elements,
+      mojo::ReportBadMessageCallback bad_message_callback,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access)
       : blob_registry_(blob_registry),
         uuid_(uuid),
         builder_(std::make_unique<BlobDataBuilder>(uuid)),
         elements_(std::move(elements)),
-        bad_message_callback_(std::move(bad_message_callback)) {
+        bad_message_callback_(std::move(bad_message_callback)),
+        file_access_(std::move(file_access)) {
     builder_->set_content_type(content_type);
     builder_->set_content_disposition(content_disposition);
   }
@@ -213,6 +220,10 @@
   // Number of dependent blobs that have started constructing.
   size_t ready_dependent_blob_count_ = 0;
 
+  // Callback to gain dlp file access.
+  file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+      file_access_;
+
   base::WeakPtrFactory<BlobUnderConstruction> weak_ptr_factory_{this};
 };
 
@@ -383,9 +394,9 @@
       }
     } else if (element->is_file()) {
       const auto& f = element->get_file();
-      builder_->AppendFile(
-          f->path, f->offset, f->length,
-          f->expected_modification_time.value_or(base::Time()));
+      builder_->AppendFile(f->path, f->offset, f->length,
+                           f->expected_modification_time.value_or(base::Time()),
+                           file_access_);
     } else if (element->is_blob()) {
       DCHECK(blob_uuid_it != referenced_blob_uuids_.end());
       const std::string& blob_uuid = *blob_uuid_it++;
@@ -565,7 +576,7 @@
 
   blobs_under_construction_[uuid] = std::make_unique<BlobUnderConstruction>(
       this, uuid, content_type, content_disposition, std::move(element_entries),
-      receivers_.GetBadMessageCallback());
+      receivers_.GetBadMessageCallback(), delegate->GetAccessCallback());
 
   std::unique_ptr<BlobDataHandle> handle = context_->AddFutureBlob(
       uuid, content_type, content_disposition,
diff --git a/storage/browser/blob/blob_registry_impl.h b/storage/browser/blob/blob_registry_impl.h
index d75ade8..5f17424 100644
--- a/storage/browser/blob/blob_registry_impl.h
+++ b/storage/browser/blob/blob_registry_impl.h
@@ -9,6 +9,9 @@
 #include "base/component_export.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/functional/callback_forward.h"
+#include "components/file_access/scoped_file_access.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
 #include "storage/browser/blob/blob_url_registry.h"
@@ -35,6 +38,8 @@
     virtual ~Delegate() {}
     virtual bool CanReadFile(const base::FilePath& file) = 0;
     virtual bool CanAccessDataForOrigin(const url::Origin& origin) = 0;
+    virtual file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+    GetAccessCallback() = 0;
   };
 
   BlobRegistryImpl(base::WeakPtr<BlobStorageContext> context,
diff --git a/storage/browser/blob/blob_storage_context.cc b/storage/browser/blob/blob_storage_context.cc
index 831601e0..db6ff07 100644
--- a/storage/browser/blob/blob_storage_context.cc
+++ b/storage/browser/blob/blob_storage_context.cc
@@ -530,8 +530,8 @@
           scoped_refptr<BlobDataItem> new_item = BlobDataItem::CreateFile(
               source_item->path(),
               source_item->offset() + copy.source_item_offset, dest_size,
-              source_item->expected_modification_time(),
-              source_item->file_ref_);
+              source_item->expected_modification_time(), source_item->file_ref_,
+              source_item->file_access_);
           copy.dest_item->set_item(std::move(new_item));
           break;
         }
diff --git a/storage/browser/file_system/file_stream_reader.h b/storage/browser/file_system/file_stream_reader.h
index fd23950..cb250e0 100644
--- a/storage/browser/file_system/file_stream_reader.h
+++ b/storage/browser/file_system/file_stream_reader.h
@@ -12,7 +12,11 @@
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
 #include "base/files/file.h"
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/weak_ptr.h"
+#include "components/file_access/scoped_file_access.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "components/services/storage/public/cpp/filesystem/filesystem_proxy.h"
 #include "net/base/completion_once_callback.h"
 
@@ -45,7 +49,9 @@
       scoped_refptr<base::TaskRunner> task_runner,
       const base::FilePath& file_path,
       int64_t initial_offset,
-      const base::Time& expected_modification_time);
+      const base::Time& expected_modification_time,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access = base::NullCallback());
 
   // Creates a new FileReader for a local file |file_path|, which is a
   // relative path into |filesystem_proxy|.  This function's behavior
diff --git a/storage/browser/file_system/file_system_backend.h b/storage/browser/file_system/file_system_backend.h
index 71b6494..7f391fa 100644
--- a/storage/browser/file_system/file_system_backend.h
+++ b/storage/browser/file_system/file_system_backend.h
@@ -16,7 +16,9 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/file_permission_policy.h"
+#include "storage/browser/file_system/file_stream_reader.h"
 #include "storage/browser/file_system/open_file_system_mode.h"
 #include "storage/browser/file_system/task_runner_bound_observer_list.h"
 #include "storage/common/file_system/file_system_types.h"
@@ -121,13 +123,17 @@
   // ERR_UPLOAD_FILE_CHANGED error.
   // This method itself does *not* check if the given path exists and is a
   // regular file. At most |max_bytes_to_read| can be fetched from the file
-  // stream reader.
+  // stream reader. The callback `file_access` grants access to dlp restricted
+  // files. If it is a NullCallback currently the access will be granted. This
+  // will change to being denied after b/265908846
   virtual std::unique_ptr<FileStreamReader> CreateFileStreamReader(
       const FileSystemURL& url,
       int64_t offset,
       int64_t max_bytes_to_read,
       const base::Time& expected_modification_time,
-      FileSystemContext* context) const = 0;
+      FileSystemContext* context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) const = 0;
 
   // Creates a new file stream writer for a given filesystem URL |url| with an
   // offset |offset|.
diff --git a/storage/browser/file_system/file_system_context.cc b/storage/browser/file_system/file_system_context.cc
index 560c400..a4b111b 100644
--- a/storage/browser/file_system/file_system_context.cc
+++ b/storage/browser/file_system/file_system_context.cc
@@ -19,6 +19,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/types/pass_key.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
 #include "components/services/storage/public/cpp/buckets/constants.h"
 #include "components/services/storage/public/cpp/quota_client_callback_wrapper.h"
@@ -595,14 +596,17 @@
     const FileSystemURL& url,
     int64_t offset,
     int64_t max_bytes_to_read,
-    const base::Time& expected_modification_time) {
+    const base::Time& expected_modification_time,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
   if (!url.is_valid())
     return nullptr;
   FileSystemBackend* backend = GetFileSystemBackend(url.type());
   if (!backend)
     return nullptr;
   return backend->CreateFileStreamReader(url, offset, max_bytes_to_read,
-                                         expected_modification_time, this);
+                                         expected_modification_time, this,
+                                         std::move(file_access));
 }
 
 std::unique_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter(
diff --git a/storage/browser/file_system/file_system_context.h b/storage/browser/file_system/file_system_context.h
index fe704dd1a..98f2c6b 100644
--- a/storage/browser/file_system/file_system_context.h
+++ b/storage/browser/file_system/file_system_context.h
@@ -15,6 +15,7 @@
 #include "base/component_export.h"
 #include "base/files/file.h"
 #include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -22,6 +23,7 @@
 #include "base/threading/sequence_bound.h"
 #include "base/types/pass_key.h"
 #include "build/build_config.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "components/services/storage/public/cpp/quota_error_or.h"
 #include "components/services/storage/public/mojom/quota_client.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -281,7 +283,9 @@
       const FileSystemURL& url,
       int64_t offset,
       int64_t max_bytes_to_read,
-      const base::Time& expected_modification_time);
+      const base::Time& expected_modification_time,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access = base::NullCallback());
 
   // Creates new FileStreamWriter instance to write into a file pointed by
   // `url` from `offset`.
diff --git a/storage/browser/file_system/isolated_file_system_backend.cc b/storage/browser/file_system/isolated_file_system_backend.cc
index e7676e6..60525668 100644
--- a/storage/browser/file_system/isolated_file_system_backend.cc
+++ b/storage/browser/file_system/isolated_file_system_backend.cc
@@ -129,10 +129,12 @@
     int64_t offset,
     int64_t max_bytes_to_read,
     const base::Time& expected_modification_time,
-    FileSystemContext* context) const {
+    FileSystemContext* context,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) const {
   return FileStreamReader::CreateForLocalFile(
       context->default_file_task_runner(), url.path(), offset,
-      expected_modification_time);
+      expected_modification_time, std::move(file_access));
 }
 
 std::unique_ptr<FileStreamWriter>
diff --git a/storage/browser/file_system/isolated_file_system_backend.h b/storage/browser/file_system/isolated_file_system_backend.h
index 13ea79e..043fa66 100644
--- a/storage/browser/file_system/isolated_file_system_backend.h
+++ b/storage/browser/file_system/isolated_file_system_backend.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/task_runner_bound_observer_list.h"
 
@@ -44,7 +45,9 @@
       int64_t offset,
       int64_t max_bytes_to_read,
       const base::Time& expected_modification_time,
-      FileSystemContext* context) const override;
+      FileSystemContext* context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) const override;
   std::unique_ptr<FileStreamWriter> CreateFileStreamWriter(
       const FileSystemURL& url,
       int64_t offset,
diff --git a/storage/browser/file_system/local_file_stream_reader.cc b/storage/browser/file_system/local_file_stream_reader.cc
index 4f13360e..482b370 100644
--- a/storage/browser/file_system/local_file_stream_reader.cc
+++ b/storage/browser/file_system/local_file_stream_reader.cc
@@ -12,11 +12,14 @@
 #include "base/check_op.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
 #include "base/location.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_runner.h"
 #include "base/types/pass_key.h"
+#include "components/file_access/scoped_file_access.h"
 #include "components/file_access/scoped_file_access_delegate.h"
 #include "net/base/file_stream.h"
 #include "net/base/io_buffer.h"
@@ -46,10 +49,13 @@
     scoped_refptr<base::TaskRunner> task_runner,
     const base::FilePath& file_path,
     int64_t initial_offset,
-    const base::Time& expected_modification_time) {
+    const base::Time& expected_modification_time,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access) {
   return std::make_unique<LocalFileStreamReader>(
       std::move(task_runner), file_path, initial_offset,
-      expected_modification_time, base::PassKey<FileStreamReader>());
+      expected_modification_time, base::PassKey<FileStreamReader>(),
+      std::move(file_access));
 }
 
 LocalFileStreamReader::~LocalFileStreamReader() = default;
@@ -84,11 +90,14 @@
     const base::FilePath& file_path,
     int64_t initial_offset,
     const base::Time& expected_modification_time,
-    base::PassKey<FileStreamReader> /*pass_key*/)
+    base::PassKey<FileStreamReader> /*pass_key*/,
+    file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+        file_access)
     : task_runner_(std::move(task_runner)),
       file_path_(file_path),
       initial_offset_(initial_offset),
-      expected_modification_time_(expected_modification_time) {}
+      expected_modification_time_(expected_modification_time),
+      file_access_(std::move(file_access)) {}
 
 void LocalFileStreamReader::Open(net::CompletionOnceCallback callback) {
   DCHECK(!has_pending_open_);
@@ -98,8 +107,12 @@
   base::OnceCallback<void(file_access::ScopedFileAccess)> open_cb =
       base::BindOnce(&LocalFileStreamReader::OnScopedFileAccessRequested,
                      weak_factory_.GetWeakPtr(), std::move(callback));
+  if (file_access_) {
+    file_access_.Run({file_path_}, std::move(open_cb));
+    return;
+  }
 
-  // TODO(b/262199707 b/265908846): Replace with getting access through a
+  // TODO(b/265908846): Replace with getting access through a
   // callback.
   file_access::ScopedFileAccessDelegate::RequestFilesAccessForSystemIO(
       {file_path_}, std::move(open_cb));
diff --git a/storage/browser/file_system/local_file_stream_reader.h b/storage/browser/file_system/local_file_stream_reader.h
index 468f0ed..849392e 100644
--- a/storage/browser/file_system/local_file_stream_reader.h
+++ b/storage/browser/file_system/local_file_stream_reader.h
@@ -12,10 +12,13 @@
 #include "base/component_export.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "base/types/pass_key.h"
 #include "components/file_access/scoped_file_access.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "net/base/completion_once_callback.h"
 #include "storage/browser/file_system/file_stream_reader.h"
 
@@ -34,11 +37,14 @@
 class COMPONENT_EXPORT(STORAGE_BROWSER) LocalFileStreamReader
     : public FileStreamReader {
  public:
-  LocalFileStreamReader(scoped_refptr<base::TaskRunner> task_runner,
-                        const base::FilePath& file_path,
-                        int64_t initial_offset,
-                        const base::Time& expected_modification_time,
-                        base::PassKey<FileStreamReader> pass_key);
+  LocalFileStreamReader(
+      scoped_refptr<base::TaskRunner> task_runner,
+      const base::FilePath& file_path,
+      int64_t initial_offset,
+      const base::Time& expected_modification_time,
+      base::PassKey<FileStreamReader> pass_key,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access);
   ~LocalFileStreamReader() override;
 
   // FileStreamReader overrides.
@@ -76,6 +82,8 @@
   const int64_t initial_offset_;
   const base::Time expected_modification_time_;
   bool has_pending_open_ = false;
+  file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+      file_access_;
   base::WeakPtrFactory<LocalFileStreamReader> weak_factory_{this};
 };
 
diff --git a/storage/browser/file_system/local_file_stream_reader_unittest.cc b/storage/browser/file_system/local_file_stream_reader_unittest.cc
index a81f8385..39920f6 100644
--- a/storage/browser/file_system/local_file_stream_reader_unittest.cc
+++ b/storage/browser/file_system/local_file_stream_reader_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
 #include "base/functional/callback_helpers.h"
 #include "base/location.h"
 #include "base/run_loop.h"
@@ -70,14 +71,23 @@
     file_thread_.Stop();
     base::RunLoop().RunUntilIdle();
   }
-
   std::unique_ptr<FileStreamReader> CreateFileReader(
       const std::string& file_name,
       int64_t initial_offset,
       const base::Time& expected_modification_time) override {
+    return CreateFileReader(file_name, initial_offset,
+                            expected_modification_time, base::NullCallback());
+  }
+
+  std::unique_ptr<FileStreamReader> CreateFileReader(
+      const std::string& file_name,
+      int64_t initial_offset,
+      const base::Time& expected_modification_time,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) {
     return FileStreamReader::CreateForLocalFile(
         file_task_runner(), test_dir().AppendASCII(file_name), initial_offset,
-        expected_modification_time);
+        expected_modification_time, std::move(file_access));
   }
 
   void WriteFile(const std::string& file_name,
@@ -124,7 +134,7 @@
                                FileStreamReaderTypedTest,
                                LocalFileStreamReaderTest);
 
-// TODO(b/262199707 b/265908846): Replace direct call to
+// TODO(b/265908846): Replace direct call to
 // file_access::ScopedFileAccessDelegate with getting access through a callback.
 TEST_F(LocalFileStreamReaderTest, ReadAllowedByDataLeakPrevention) {
   this->WriteTestFile();
@@ -151,7 +161,7 @@
   ASSERT_EQ(this->kTestData, data);
 }
 
-// TODO(b/262199707 b/265908846): Replace direct call to
+// TODO(b/265908846): Replace direct call to
 // file_access::ScopedFileAccessDelegate with getting access through a callback.
 TEST_F(LocalFileStreamReaderTest, ReadBlockedByDataLeakPrevention) {
   this->WriteTestFile();
@@ -178,4 +188,50 @@
   ASSERT_EQ("", data);
 }
 
+TEST_F(LocalFileStreamReaderTest, ReadAllowedByDataLeakPreventionCallback) {
+  this->WriteTestFile();
+  base::MockRepeatingCallback<void(
+      const std::vector<base::FilePath>&,
+      base::OnceCallback<void(file_access::ScopedFileAccess)>)>
+      callback;
+  EXPECT_CALL(
+      callback,
+      Run(testing::ElementsAre(test_dir().AppendASCII(kTestFileName)), _))
+      .WillOnce(base::test::RunOnceCallback<1>(CreateScopedFileAccess(true)));
+
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      std::string(this->kTestFileName), 0, this->test_file_modification_time(),
+      callback.Get()));
+
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::OK, result);
+  ASSERT_EQ(this->kTestData, data);
+}
+
+TEST_F(LocalFileStreamReaderTest, ReadBlockedByDataLeakPreventionCallback) {
+  this->WriteTestFile();
+  base::MockRepeatingCallback<void(
+      const std::vector<base::FilePath>&,
+      base::OnceCallback<void(file_access::ScopedFileAccess)>)>
+      callback;
+  file_access::ScopedFileAccessDelegate::
+      ScopedRequestFilesAccessCallbackForTesting file_access_callback(
+          callback.Get());
+  EXPECT_CALL(
+      callback,
+      Run(testing::ElementsAre(test_dir().AppendASCII(kTestFileName)), _))
+      .WillOnce(base::test::RunOnceCallback<1>(CreateScopedFileAccess(false)));
+  std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
+      std::string(this->kTestFileName), 0, this->test_file_modification_time(),
+      callback.Get()));
+
+  int result = 0;
+  std::string data;
+  ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
+  ASSERT_EQ(net::ERR_ACCESS_DENIED, result);
+  ASSERT_EQ("", data);
+}
+
 }  // namespace storage
diff --git a/storage/browser/file_system/sandbox_file_system_backend.cc b/storage/browser/file_system/sandbox_file_system_backend.cc
index 21857e4..0907f4a4 100644
--- a/storage/browser/file_system/sandbox_file_system_backend.cc
+++ b/storage/browser/file_system/sandbox_file_system_backend.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/async_file_util_adapter.h"
 #include "storage/browser/file_system/copy_or_move_file_validator.h"
 #include "storage/browser/file_system/file_stream_reader.h"
@@ -137,7 +138,9 @@
     int64_t offset,
     int64_t max_bytes_to_read,
     const base::Time& expected_modification_time,
-    FileSystemContext* context) const {
+    FileSystemContext* context,
+    file_access::ScopedFileAccessDelegate::
+        RequestFilesAccessIOCallback /*file_access*/) const {
   DCHECK(CanHandleType(url.type()));
   DCHECK(delegate_);
   return delegate_->CreateFileStreamReader(url, offset,
diff --git a/storage/browser/file_system/sandbox_file_system_backend.h b/storage/browser/file_system/sandbox_file_system_backend.h
index c9b8f32..1bf1253 100644
--- a/storage/browser/file_system/sandbox_file_system_backend.h
+++ b/storage/browser/file_system/sandbox_file_system_backend.h
@@ -14,6 +14,7 @@
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/file_system_quota_util.h"
 #include "storage/browser/file_system/sandbox_file_system_backend_delegate.h"
@@ -58,7 +59,9 @@
       int64_t offset,
       int64_t max_bytes_to_read,
       const base::Time& expected_modification_time,
-      FileSystemContext* context) const override;
+      FileSystemContext* context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) const override;
   std::unique_ptr<FileStreamWriter> CreateFileStreamWriter(
       const FileSystemURL& url,
       int64_t offset,
diff --git a/storage/browser/test/mock_blob_registry_delegate.cc b/storage/browser/test/mock_blob_registry_delegate.cc
index 1cc840c..91e5f44c 100644
--- a/storage/browser/test/mock_blob_registry_delegate.cc
+++ b/storage/browser/test/mock_blob_registry_delegate.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "storage/browser/test/mock_blob_registry_delegate.h"
+#include "base/functional/callback_helpers.h"
 
 namespace storage {
 
@@ -15,4 +16,9 @@
   return can_access_data_for_origin;
 }
 
+file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+MockBlobRegistryDelegate::GetAccessCallback() {
+  return base::DoNothing();
+}
+
 }  // namespace storage
diff --git a/storage/browser/test/mock_blob_registry_delegate.h b/storage/browser/test/mock_blob_registry_delegate.h
index 87d0ed33..7261fe8 100644
--- a/storage/browser/test/mock_blob_registry_delegate.h
+++ b/storage/browser/test/mock_blob_registry_delegate.h
@@ -19,6 +19,8 @@
 
   bool CanReadFile(const base::FilePath& file) override;
   bool CanAccessDataForOrigin(const url::Origin& origin) override;
+  file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+  GetAccessCallback() override;
 
   bool can_read_file_result = true;
   bool can_access_data_for_origin = true;
diff --git a/storage/browser/test/test_file_system_backend.cc b/storage/browser/test/test_file_system_backend.cc
index fedf2cf8..24053f47 100644
--- a/storage/browser/test/test_file_system_backend.cc
+++ b/storage/browser/test/test_file_system_backend.cc
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/task/sequenced_task_runner.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/copy_or_move_file_validator.h"
 #include "storage/browser/file_system/file_observers.h"
 #include "storage/browser/file_system/file_system_operation.h"
@@ -216,7 +217,9 @@
     int64_t offset,
     int64_t max_bytes_to_read,
     const base::Time& expected_modification_time,
-    FileSystemContext* context) const {
+    FileSystemContext* context,
+    file_access::ScopedFileAccessDelegate::
+        RequestFilesAccessIOCallback /*file_access*/) const {
   return std::make_unique<SandboxFileStreamReader>(context, url, offset,
                                                    expected_modification_time);
 }
diff --git a/storage/browser/test/test_file_system_backend.h b/storage/browser/test/test_file_system_backend.h
index 16ddfd6..4a70ff4 100644
--- a/storage/browser/test/test_file_system_backend.h
+++ b/storage/browser/test/test_file_system_backend.h
@@ -11,6 +11,7 @@
 
 #include "base/files/file_path.h"
 #include "base/memory/scoped_refptr.h"
+#include "components/file_access/scoped_file_access_delegate.h"
 #include "storage/browser/file_system/async_file_util_adapter.h"
 #include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/task_runner_bound_observer_list.h"
@@ -61,7 +62,9 @@
       int64_t offset,
       int64_t max_bytes_to_read,
       const base::Time& expected_modification_time,
-      FileSystemContext* context) const override;
+      FileSystemContext* context,
+      file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
+          file_access) const override;
   std::unique_ptr<FileStreamWriter> CreateFileStreamWriter(
       const FileSystemURL& url,
       int64_t offset,
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index a8ee4bd..9d0c2ce2 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5826,9 +5826,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5840,8 +5840,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -5997,9 +5997,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6011,8 +6011,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -6149,9 +6149,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6163,8 +6163,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index dfa82ca..9b15bbd 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -25689,9 +25689,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -25703,8 +25703,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -25860,9 +25860,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -25874,8 +25874,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -26012,9 +26012,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -26026,8 +26026,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index f0a020c..f70d004 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -48615,9 +48615,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48628,8 +48628,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -48786,9 +48786,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48799,8 +48799,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -48938,9 +48938,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48951,8 +48951,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -50476,9 +50476,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -50489,8 +50489,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -50647,9 +50647,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -50660,8 +50660,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -50799,9 +50799,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -50812,8 +50812,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -51585,9 +51585,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -51598,8 +51598,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 6b1444086..78c7fde 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -18533,12 +18533,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18550,8 +18550,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -18724,12 +18724,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18741,8 +18741,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
@@ -18891,12 +18891,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 113.0.5639.0",
+        "description": "Run with ash-chrome version 113.0.5640.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18908,8 +18908,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v113.0.5639.0",
-              "revision": "version:113.0.5639.0"
+              "location": "lacros_version_skew_tests_v113.0.5640.0",
+              "revision": "version:113.0.5640.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 5ebf85c..7ac6fcd 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5639.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v113.0.5640.0/test_ash_chrome',
     ],
-    'description': 'Run with ash-chrome version 113.0.5639.0',
+    'description': 'Run with ash-chrome version 113.0.5640.0',
     'identifier': 'Lacros version skew testing ash canary',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v113.0.5639.0',
-          'revision': 'version:113.0.5639.0',
+          'location': 'lacros_version_skew_tests_v113.0.5640.0',
+          'revision': 'version:113.0.5640.0',
         },
       ],
     },
diff --git a/testing/scripts/run_finch_smoke_tests_android.py b/testing/scripts/run_finch_smoke_tests_android.py
index 24293cc1..31b6fcb 100755
--- a/testing/scripts/run_finch_smoke_tests_android.py
+++ b/testing/scripts/run_finch_smoke_tests_android.py
@@ -496,6 +496,7 @@
                         type=lambda processes: max(0, int(processes)),
                         default=1,
                         help='Number of emulator to run.')
+    common.add_emulator_args(parser)
     # Add arguments used by Skia Gold.
     FinchSkiaGoldProperties.AddCommandLineArguments(parser)
 
@@ -1189,7 +1190,6 @@
         required=False,
         help='path to write test results JSON object to')
 
-  common.add_emulator_args(parser)
   script_common.AddDeviceArguments(parser)
   script_common.AddEnvironmentArguments(parser)
   logging_common.AddLoggingArguments(parser)
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 9e98e26..f254e070 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -10838,7 +10838,6 @@
       CrossSiteRedirect
       CrossSiteNavigation
       SameSiteCrossOriginRedirect
-      SameSiteCrossOriginNavigation
       SameSiteCrossOriginRedirectNotOptIn
       SameSiteCrossOriginNavigationNotOptIn
       ActivationNavigationParameterMismatch
diff --git a/third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom b/third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom
index e5f6e34..dedc87d 100644
--- a/third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom
+++ b/third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom
@@ -6,6 +6,22 @@
 
 import "third_party/blink/public/mojom/navigation/renderer_eviction_reason.mojom";
 
+// This struct contains information about a blocking feature's usage, i.e. what feature
+// is used, in what JavaScript file, in what function, at what line and column number.
+struct BlockingDetails {
+  // The index of the blocking feature.
+  // See blink::SchedulingPolicy::Feature for the meaning of the individual bits.
+  uint32 feature;
+  // The URL of the JavaScript file where the feature is used.
+  string url;
+  // The function in the JavaScript file where the feature is used.
+  string function_name;
+  // The line number in JavaScript file where the feature is used.
+  uint64 line_number;
+  // The column number in JavaScript file where the feature is used.
+  uint64 column_number;
+};
+
 // This interface defines back-forward cache related methods that will be
 // invoked from the renderer process.
 //
@@ -20,7 +36,8 @@
 
   // Sent by the blink's FrameScheduler when a list of active features
   // the scheduler tracks changes.
-  // See blink::scheduler::SchedulingPolicy::Feature for the meaning
-  // of the individual bits.
-  DidChangeBackForwardCacheDisablingFeatures(uint64 features_mask);
+  // `details` is the list of blocking features currently being used and their
+  // information about where they were used in JavaScript.
+  DidChangeBackForwardCacheDisablingFeatures(
+    array<BlockingDetails> details);
 };
diff --git a/third_party/blink/renderer/core/dom/mutation_observer.cc b/third_party/blink/renderer/core/dom/mutation_observer.cc
index e38e3f7..9e1e464d 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer.cc
+++ b/third_party/blink/renderer/core/dom/mutation_observer.cc
@@ -103,6 +103,10 @@
     active_mutation_observers_.insert(observer);
   }
 
+  void ClearActiveObserver(MutationObserver* observer) {
+    active_mutation_observers_.erase(observer);
+  }
+
  private:
   void EnsureEnqueueMicrotask() {
     if (active_mutation_observers_.empty() &&
@@ -346,6 +350,16 @@
     ActivateObserver(this);
 }
 
+void MutationObserver::ContextDestroyed() {
+  // The 'DeliverMutations' micro task is *not* guaranteed to run.
+  // It's necessary to clear out this observer from the list of active observers
+  // in case the MutationObserverAgentData is reused across navigations.
+  // Otherwise no MutationObserver for the agent can fire again.
+  DCHECK(GetExecutionContext());
+  MutationObserverAgentData::From(*GetExecutionContext()->GetAgent())
+      .ClearActiveObserver(this);
+}
+
 void MutationObserver::CancelInspectorAsyncTasks() {
   for (auto& record : records_) {
     record->async_task_context()->Cancel();
diff --git a/third_party/blink/renderer/core/dom/mutation_observer.h b/third_party/blink/renderer/core/dom/mutation_observer.h
index a747e76e..8a42dab 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer.h
+++ b/third_party/blink/renderer/core/dom/mutation_observer.h
@@ -117,7 +117,7 @@
   bool HasPendingActivity() const override { return !records_.empty(); }
 
   void ContextLifecycleStateChanged(mojom::FrameLifecycleState) final;
-  void ContextDestroyed() final {}
+  void ContextDestroyed() final;
 
   void Trace(Visitor*) const override;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 0e5c9c7b..86cf8bdf 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -293,6 +293,18 @@
                    StyleEnvironmentVariables::FormatPx(segment_rect.height()));
 }
 
+mojom::blink::BlockingDetailsPtr CreateBlockingDetailsMojom(
+    const FeatureAndJSLocationBlockingBFCache& blocking_details) {
+  auto feature_location_to_report = mojom::blink::BlockingDetails::New();
+  feature_location_to_report->feature =
+      static_cast<uint32_t>(blocking_details.Feature());
+  feature_location_to_report->line_number = blocking_details.LineNumber();
+  feature_location_to_report->column_number = blocking_details.ColumnNumber();
+  feature_location_to_report->url = blocking_details.Url();
+  feature_location_to_report->function_name = blocking_details.Function();
+  return feature_location_to_report;
+}
+
 }  // namespace
 
 template class CORE_TEMPLATE_EXPORT Supplement<LocalFrame>;
@@ -2366,10 +2378,27 @@
 
 void LocalFrame::UpdateBackForwardCacheDisablingFeatures(
     BlockingDetails details) {
-  // TODO(crbug.com/1366675): Add two Vectors to argument of
-  // DidChangeBackForwardCacheDisablingFeatures
+  auto mojom_details = ConvertFeatureAndLocationToMojomStruct(
+      details.non_sticky_features_and_js_locations,
+      details.sticky_features_and_js_locations);
   GetBackForwardCacheControllerHostRemote()
-      .DidChangeBackForwardCacheDisablingFeatures(details.feature_mask);
+      .DidChangeBackForwardCacheDisablingFeatures(std::move(mojom_details));
+}
+
+using BlockingDetailsList = Vector<mojom::blink::BlockingDetailsPtr>;
+BlockingDetailsList LocalFrame::ConvertFeatureAndLocationToMojomStruct(
+    const BFCacheBlockingFeatureAndLocations& non_sticky,
+    const BFCacheBlockingFeatureAndLocations& sticky) {
+  BlockingDetailsList blocking_details_list;
+  for (auto feature : non_sticky) {
+    auto blocking_details = CreateBlockingDetailsMojom(feature);
+    blocking_details_list.push_back(std::move(blocking_details));
+  }
+  for (auto feature : sticky) {
+    auto blocking_details = CreateBlockingDetailsMojom(feature);
+    blocking_details_list.push_back(std::move(blocking_details));
+  }
+  return blocking_details_list;
 }
 
 const base::UnguessableToken& LocalFrame::GetAgentClusterId() const {
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 51d97c27..bebd8c9 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -855,6 +855,11 @@
 
   void ScheduleNextServiceForScrollSnapshotClients();
 
+  using BlockingDetailsList = Vector<mojom::blink::BlockingDetailsPtr>;
+  static BlockingDetailsList ConvertFeatureAndLocationToMojomStruct(
+      const BFCacheBlockingFeatureAndLocations&,
+      const BFCacheBlockingFeatureAndLocations&);
+
  private:
   friend class FrameNavigationDisabler;
   // LocalFrameMojoHandler is a part of LocalFrame.
diff --git a/third_party/blink/renderer/core/frame/local_frame_back_forward_cache_test.cc b/third_party/blink/renderer/core/frame/local_frame_back_forward_cache_test.cc
index 49b9de0..2c616daf 100644
--- a/third_party/blink/renderer/core/frame/local_frame_back_forward_cache_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_back_forward_cache_test.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/execution_context/agent.h"
@@ -51,7 +52,7 @@
   }
 
   void DidChangeBackForwardCacheDisablingFeatures(
-      uint64_t features_mask) override {}
+      Vector<mojom::blink::BlockingDetailsPtr> details) override {}
 
   void WaitUntilEvictedFromBackForwardCache() {
     base::RunLoop run_loop;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h
index d23cd9a2..4c8de83 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h
@@ -50,6 +50,13 @@
     return text_offset_;
   }
 
+  // True if the offset of `this` is equal to or after `other`.
+  bool IsAtEqualOrAfter(const NGInlineBreakToken& other) const {
+    return text_offset_ > other.text_offset_ ||
+           (text_offset_ == other.text_offset_ &&
+            item_index_ >= other.item_index_);
+  }
+
   bool UseFirstLineStyle() const {
     return flags_ & kUseFirstLineStyle;
   }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_paragraph_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_paragraph_line_breaker.cc
index 7f1d1f4..f7175d6f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_paragraph_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_paragraph_line_breaker.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_paragraph_line_breaker.h"
 
 #include <numeric>
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_info.h"
@@ -28,19 +29,30 @@
   LineBreakResults(const NGInlineNode& node, const NGConstraintSpace& space)
       : node_(node), space_(space) {}
 
-  wtf_size_t size() const { return lines_.size(); }
+  wtf_size_t Size() const { return lines_.size(); }
   LayoutUnit LineWidthSum() const {
     return std::accumulate(lines_.begin(), lines_.end(), LayoutUnit(),
                            [](LayoutUnit acc, const LineBreakResult& item) {
                              return acc + item.width;
                            });
   }
+  const NGInlineBreakToken* BreakToken() const { return break_token_; }
 
-  void clear() { lines_.clear(); }
+  void Clear() {
+    break_token_ = nullptr;
+    lines_.clear();
+  }
 
-  bool BreakLines(const LayoutUnit available_width, wtf_size_t max_lines) {
+  enum class Status {
+    kFinished,          // Finished to the end or `stop_at`.
+    kNotApplicable,     // This block is not applicable.
+    kMaxLinesExceeded,  // # of lines exceeded `max_lines`.
+  };
+
+  Status BreakLines(const LayoutUnit available_width,
+                    wtf_size_t max_lines,
+                    const NGInlineBreakToken* stop_at = nullptr) {
     DCHECK(lines_.empty());
-    const NGInlineBreakToken* break_token = nullptr;
     const NGLineLayoutOpportunity line_opportunity(available_width);
     NGPositionedFloatVector leading_floats;
     NGExclusionSpace exclusion_space;
@@ -49,23 +61,24 @@
       NGLineBreaker line_breaker(
           node_, NGLineBreakerMode::kContent, space_, line_opportunity,
           leading_floats,
-          /* handled_leading_floats_index */ 0, break_token,
+          /* handled_leading_floats_index */ 0, break_token_,
           /* column_spanner_path_ */ nullptr, &exclusion_space);
       line_breaker.NextLine(&line_info);
       // Bisecting can't find the desired value if the paragraph has forced line
       // breaks.
       DCHECK(!line_info.HasForcedBreak());
       if (line_info.HasOverflow()) {
-        return false;  // Don't balance if there are overflowing lines.
+        return Status::kNotApplicable;
       }
-      break_token = line_breaker.CreateBreakToken(line_info);
+      break_token_ = line_breaker.CreateBreakToken(line_info);
       lines_.push_back(LineBreakResult{line_info.Width()});
       DCHECK_LE(lines_.size(), kMaxLinesToBalance);
-      if (!break_token) {
-        return true;
+      if (!break_token_ ||
+          (stop_at && break_token_->IsAtEqualOrAfter(*stop_at))) {
+        return Status::kFinished;
       }
       if (!--max_lines) {
-        return false;  // Didn't fit into `max_lines`.
+        return Status::kMaxLinesExceeded;
       }
     }
   }
@@ -73,21 +86,24 @@
   LayoutUnit BisectAvailableWidth(const LayoutUnit max_available_width,
                                   const LayoutUnit min_available_width,
                                   const LayoutUnit epsilon,
-                                  const wtf_size_t num_lines) {
+                                  const wtf_size_t num_lines,
+                                  const NGInlineBreakToken* stop_at = nullptr) {
     DCHECK_GT(epsilon, LayoutUnit());  // 0 may cause an infinite loop
     DCHECK_GT(num_lines, 0u);
-    DCHECK_EQ(size(), 0u);
+    DCHECK_EQ(Size(), 0u);
     LayoutUnit upper = max_available_width;
     LayoutUnit lower = min_available_width;
     while (lower + epsilon < upper) {
       const LayoutUnit middle = (upper + lower) / 2;
-      if (!BreakLines(middle, num_lines)) {
+      const Status status = BreakLines(middle, num_lines, stop_at);
+      DCHECK_NE(status, Status::kNotApplicable);
+      if (status != Status::kFinished) {
         lower = middle;
       } else {
-        DCHECK_LE(size(), num_lines);
+        DCHECK_LE(Size(), num_lines);
         upper = middle;
       }
-      clear();
+      Clear();
     }
     DCHECK_GE(upper, min_available_width);
     DCHECK_LE(upper, max_available_width);
@@ -98,6 +114,7 @@
   const NGInlineNode node_;
   const NGConstraintSpace& space_;
   Vector<LineBreakResult, kMaxLinesToBalance> lines_;
+  const NGInlineBreakToken* break_token_ = nullptr;
 };
 
 // Estimate the number of lines using the `ch` unit (the space width) without
@@ -142,22 +159,40 @@
     }
   }
 
-  // Estimate the number of lines to see if the text is too long to balance.
-  // Because this is an estimate, allow it to be `kMaxLinesToBalance * 2`.
   const ComputedStyle& block_style = node.Style();
-  const wtf_size_t estimated_num_lines = EstimateNumLines(
-      items_data.text_content, block_style.GetFont().PrimaryFont(),
-      line_opportunity.AvailableInlineSize());
-  if (estimated_num_lines > kMaxLinesToBalance * 2) {
-    return absl::nullopt;
-  }
-
   const LayoutUnit available_width = line_opportunity.AvailableInlineSize();
   LineBreakResults normal_lines(node, space);
-  if (!normal_lines.BreakLines(available_width, kMaxLinesToBalance)) {
-    return absl::nullopt;
+  const int lines_until_clamp = space.LinesUntilClamp().value_or(0);
+  if (lines_until_clamp > 0 &&
+      static_cast<unsigned>(lines_until_clamp) <= kMaxLinesToBalance) {
+    if (lines_until_clamp == 1) {
+      return absl::nullopt;  // Balancing not needed for single line paragraphs.
+    }
+
+    const LineBreakResults::Status status =
+        normal_lines.BreakLines(available_width, lines_until_clamp);
+    if (status == LineBreakResults::Status::kNotApplicable) {
+      return absl::nullopt;
+    }
+  } else {
+    // Estimate the number of lines to see if the text is too long to balance.
+    // Because this is an estimate, allow it to be `kMaxLinesToBalance * 2`.
+    const wtf_size_t estimated_num_lines = EstimateNumLines(
+        items_data.text_content, block_style.GetFont().PrimaryFont(),
+        line_opportunity.AvailableInlineSize());
+    if (estimated_num_lines > kMaxLinesToBalance * 2) {
+      return absl::nullopt;
+    }
+
+    const LineBreakResults::Status status =
+        normal_lines.BreakLines(available_width, kMaxLinesToBalance);
+    if (status != LineBreakResults::Status::kFinished) {
+      // Abort if not applicable or `kMaxLinesToBalance` exceeded.
+      return absl::nullopt;
+    }
+    DCHECK(!normal_lines.BreakToken());
   }
-  const wtf_size_t num_lines = normal_lines.size();
+  const wtf_size_t num_lines = normal_lines.Size();
   DCHECK_LE(num_lines, kMaxLinesToBalance);
   if (num_lines <= 1) {
     return absl::nullopt;  // Balancing not needed for single line paragraphs.
@@ -178,7 +213,8 @@
   const LayoutUnit min_available_width =
       LayoutUnit::FromFloatRound(avg_line_width * .8f);
   return balanced_lines.BisectAvailableWidth(
-      available_width, min_available_width, epsilon, num_lines);
+      available_width, min_available_width, epsilon, num_lines,
+      normal_lines.BreakToken());
 }
 
 // static
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index 7f1a403..2a5f46ff 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -690,8 +690,10 @@
   MinMaxSizes min_max = ComputeMinMaxBlockSizes(space, style, border_padding,
                                                 override_available_size);
 
-  if (space.MinBlockSizeShouldEncompassIntrinsicSize())
-    min_max.Encompass(intrinsic_size);
+  if (space.MinBlockSizeShouldEncompassIntrinsicSize()) {
+    // Encompass intrinsic block-size, but not beyond computed max-block-size.
+    min_max.Encompass(std::min(intrinsic_size, min_max.max_size));
+  }
 
   // Scrollable percentage-sized children of table cells (sometimes) are sized
   // to their min-size.
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index f01a983c..90076fb2 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
 #include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
@@ -488,9 +489,11 @@
   if (!back_forward_cache_controller_host_.is_bound()) {
     return;
   }
-
+  auto mojom_details = LocalFrame::ConvertFeatureAndLocationToMojomStruct(
+      details.non_sticky_features_and_js_locations,
+      details.sticky_features_and_js_locations);
   back_forward_cache_controller_host_
-      ->DidChangeBackForwardCacheDisablingFeatures(details.feature_mask);
+      ->DidChangeBackForwardCacheDisablingFeatures(std::move(mojom_details));
 }
 
 void DedicatedWorkerGlobalScope::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 1d458099..cc4629b 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -2525,6 +2525,14 @@
   dict = "//testing/libfuzzer/fuzzers/dicts/math_transform.dict"
 }
 
+fuzzer_test("layout_locale_fuzzer") {
+  sources = [ "text/layout_locale_fuzzer.cc" ]
+  deps = [
+    ":blink_fuzzer_test_support",
+    ":platform",
+  ]
+}
+
 # Fuzzer for blink::FindCategory.
 fuzzer_test("mathml_operator_dictionary_fuzzer") {
   sources = [ "text/mathml_operator_dictionary_fuzzer.cc" ]
diff --git a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
index cbb9a07b..0d728194 100644
--- a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.cc
@@ -53,16 +53,10 @@
     : thread_type_(thread_type),
       thread_metrics_(ConvertBlinkThreadType(thread_type),
                       has_cpu_timing_for_each_task),
-      thread_task_duration_reporter_(
-          "RendererScheduler.TaskDurationPerThreadType2"),
       thread_task_cpu_duration_reporter_(
           "RendererScheduler.TaskCPUDurationPerThreadType2"),
-      foreground_thread_task_duration_reporter_(
-          "RendererScheduler.TaskDurationPerThreadType2.Foreground"),
       foreground_thread_task_cpu_duration_reporter_(
           "RendererScheduler.TaskCPUDurationPerThreadType2.Foreground"),
-      background_thread_task_duration_reporter_(
-          "RendererScheduler.TaskDurationPerThreadType2.Background"),
       background_thread_task_cpu_duration_reporter_(
           "RendererScheduler.TaskCPUDurationPerThreadType2.Background") {}
 
@@ -83,19 +77,8 @@
     const base::sequence_manager::TaskQueue::TaskTiming& task_timing) {
   thread_metrics_.RecordTaskMetrics(task, task_timing);
 
-  thread_task_duration_reporter_.RecordTask(thread_type_,
-                                            task_timing.wall_duration());
-
   bool backgrounded = internal::ProcessState::Get()->is_process_backgrounded;
 
-  if (backgrounded) {
-    background_thread_task_duration_reporter_.RecordTask(
-        thread_type_, task_timing.wall_duration());
-  } else {
-    foreground_thread_task_duration_reporter_.RecordTask(
-        thread_type_, task_timing.wall_duration());
-  }
-
   if (!task_timing.has_thread_time())
     return;
   thread_task_cpu_duration_reporter_.RecordTask(thread_type_,
diff --git a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h
index 477394c3..efd059c 100644
--- a/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/metrics_helper.h
@@ -59,16 +59,10 @@
   scheduling_metrics::ThreadMetrics thread_metrics_;
 
   scheduling_metrics::TaskDurationMetricReporter<ThreadType>
-      thread_task_duration_reporter_;
-  scheduling_metrics::TaskDurationMetricReporter<ThreadType>
       thread_task_cpu_duration_reporter_;
   scheduling_metrics::TaskDurationMetricReporter<ThreadType>
-      foreground_thread_task_duration_reporter_;
-  scheduling_metrics::TaskDurationMetricReporter<ThreadType>
       foreground_thread_task_cpu_duration_reporter_;
   scheduling_metrics::TaskDurationMetricReporter<ThreadType>
-      background_thread_task_duration_reporter_;
-  scheduling_metrics::TaskDurationMetricReporter<ThreadType>
       background_thread_task_cpu_duration_reporter_;
 };
 
diff --git a/third_party/blink/renderer/platform/scheduler/common/metrics_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/metrics_helper_unittest.cc
index f22e0bc..6a596e1 100644
--- a/third_party/blink/renderer/platform/scheduler/common/metrics_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/metrics_helper_unittest.cc
@@ -64,15 +64,6 @@
 
   EXPECT_THAT(
       histogram_tester.GetAllSamples(
-          "RendererScheduler.TaskDurationPerThreadType2"),
-      testing::UnorderedElementsAre(
-          base::Bucket(static_cast<int>(ThreadType::kMainThread), 40),
-          base::Bucket(static_cast<int>(ThreadType::kCompositorThread), 170),
-          base::Bucket(static_cast<int>(ThreadType::kUnspecifiedWorkerThread),
-                       115)));
-
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples(
           "RendererScheduler.TaskCPUDurationPerThreadType2"),
       testing::UnorderedElementsAre(
           base::Bucket(static_cast<int>(ThreadType::kMainThread), 15),
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 66a8b15..dd88c1b 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -203,12 +203,6 @@
 };
 
 MATCHER(BlockingDetailsHasCCNS, "Blocking details has CCNS.") {
-  bool mask_has_ccns =
-      (arg.feature_mask == (1 << static_cast<uint64_t>(
-                                SchedulingPolicy::Feature::
-                                    kMainResourceHasCacheControlNoStore)) |
-       (1 << static_cast<uint64_t>(
-            SchedulingPolicy::Feature::kMainResourceHasCacheControlNoCache)));
   bool vector_empty = arg.non_sticky_features_and_js_locations.empty();
   bool vector_has_ccns =
       arg.sticky_features_and_js_locations.Contains(
@@ -219,21 +213,18 @@
           FeatureAndJSLocationBlockingBFCache(
               SchedulingPolicy::Feature::kMainResourceHasCacheControlNoCache,
               nullptr));
-  return mask_has_ccns && vector_empty && vector_has_ccns;
+  return vector_empty && vector_has_ccns;
 }
 
 MATCHER_P(BlockingDetailsHasWebSocket,
           handle,
           "BlockingDetails has WebSocket.") {
-  bool mask_has_web_socket =
-      (arg.feature_mask ==
-       (1 << static_cast<size_t>(SchedulingPolicy::Feature::kWebSocket)));
   bool handle_has_web_socket =
       (handle->GetFeatureAndJSLocationBlockingBFCache() ==
        FeatureAndJSLocationBlockingBFCache(
            SchedulingPolicy::Feature::kWebSocket, nullptr));
   bool vector_empty = arg.sticky_features_and_js_locations.empty();
-  return mask_has_web_socket && handle_has_web_socket && vector_empty;
+  return handle_has_web_socket && vector_empty;
 }
 
 MATCHER(BlockingDetailsIsEmpty, "BlockingDetails is empty.") {
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
index 048b0fd..ba1c5f0 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
@@ -366,12 +366,6 @@
 };
 
 MATCHER(BlockingDetailsHasCCNS, "Compares two blocking details.") {
-  bool mask_has_ccns =
-      (arg.feature_mask == (1 << static_cast<uint64_t>(
-                                SchedulingPolicy::Feature::
-                                    kMainResourceHasCacheControlNoStore)) |
-       (1 << static_cast<uint64_t>(
-            SchedulingPolicy::Feature::kMainResourceHasCacheControlNoCache)));
   bool vector_empty = arg.non_sticky_features_and_js_locations.empty();
   bool vector_has_ccns =
       arg.sticky_features_and_js_locations.Contains(
@@ -382,7 +376,7 @@
           FeatureAndJSLocationBlockingBFCache(
               SchedulingPolicy::Feature::kMainResourceHasCacheControlNoCache,
               nullptr));
-  return mask_has_ccns && vector_empty && vector_has_ccns;
+  return vector_empty && vector_has_ccns;
 }
 
 // Confirms that the feature usage in a dedicated worker is uploaded to
diff --git a/third_party/blink/renderer/platform/text/layout_locale_fuzzer.cc b/third_party/blink/renderer/platform/text/layout_locale_fuzzer.cc
new file mode 100644
index 0000000..6e5a9bc5
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/layout_locale_fuzzer.cc
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <tuple>
+
+#include "third_party/blink/renderer/platform/text/layout_locale.h"
+
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/testing/fuzzed_data_provider.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static blink::BlinkFuzzerTestSupport test_support;
+
+  blink::FuzzedDataProvider fuzzed_data(data, size);
+
+  bool use_default = fuzzed_data.ConsumeBool();
+
+  // Keep fuzz data layout the same.
+  auto maybe_locale = fuzzed_data.ConsumeRandomLengthString(10u);
+
+  const blink::LayoutLocale* locale;
+  if (use_default) {
+    locale = &blink::LayoutLocale::GetDefault();
+  } else {
+    locale = blink::LayoutLocale::Get(AtomicString(maybe_locale));
+  }
+
+  if (!locale) {
+    return 0;
+  }
+
+  auto* hyphen = locale->GetHyphenation();
+
+  if (!hyphen) {
+    return 0;
+  }
+
+  auto string_data = AtomicString(
+      fuzzed_data.ConsumeRandomLengthString(fuzzed_data.RemainingBytes()));
+  std::ignore = hyphen->HyphenLocations(string_data);
+
+  return 0;
+}
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 0a31819..28edead 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -56,13 +56,6 @@
        {}
       ]
      ],
-     "aria-owns-presentation.html": [
-      "138625a1534faca65cb00ff3d615dc4fb3fc080b",
-      [
-       null,
-       {}
-      ]
-     ],
      "aria-owns-reparent.html": [
       "2eaceb66f6957b0240a4f02b0c23b74e8ca4921a",
       [
@@ -3842,6 +3835,13 @@
      }
     },
     "css-view-transitions": {
+     "document-element-detatched-crash.html": [
+      "3c5419a8102c2160ac82e69bf046408b2c542e41",
+      [
+       null,
+       {}
+      ]
+     ],
      "get-computed-style-crash.html": [
       "38cd5af7f83d60976f9712e446a55d11fe07160e",
       [
@@ -187108,6 +187108,19 @@
         {}
        ]
       ],
+      "text-wrap-balance-line-clamp-001.html": [
+       "292e655464b750a3c0426fb6c63df8ebdbac5ffe",
+       [
+        null,
+        [
+         [
+          "/css/css-text/white-space/reference/text-wrap-balance-line-clamp-001-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "textarea-break-spaces-001.html": [
        "ad81e99768f9816c6e8a659c13aed81c4f22d940",
        [
@@ -265663,11 +265676,11 @@
   "support": {
    ".cache": {
     "gitignore2.json": [
-     "0fcc592c012d275cccf4ff6821f6bc3f3d5179e8",
+     "ef463bfdaca2e777b7467ff80a04f43567a13939",
      []
     ],
     "mtime.json": [
-     "17c2e6c03bfd81433d941bd6e02b8304856c89db",
+     "3a98e41764a18f6c53e9d9f327b967938734c853",
      []
     ]
    },
@@ -307455,15 +307468,15 @@
        []
       ],
       "custom-property-transition-custom-ident.html.ini": [
-       "d2683822855a10fdf2254faa7d4c33392e257b3b",
+       "90567525de988272ac3b18a70474030a4d650d75",
        []
       ],
       "custom-property-transition-image.html.ini": [
-       "7e24e9fbc204c46d8ef9415f66263f60be5ca7d5",
+       "68e0c05ef99188ed34983c3531720ec04d1bdffd",
        []
       ],
       "custom-property-transition-mismatched-list.html.ini": [
-       "4c2b38046189e03a6699c8129d7a3ec51b5f99ac",
+       "398da8fb54dc4624f2146c60051550f959ec6e71",
        []
       ],
       "custom-property-transition-property-all-expected.txt": [
@@ -307491,7 +307504,7 @@
        []
       ],
       "custom-property-transition-url.html.ini": [
-       "bd8ba68e95b5016d1e35ea4ff22aeec0e3c71407",
+       "e59f4ae17cc5d90fda8c6109172591dc935adcb3",
        []
       ]
      },
@@ -316093,6 +316106,10 @@
         "43202e4d97f03c1694176f5c6a95b4a48e938a96",
         []
        ],
+       "text-wrap-balance-line-clamp-001-ref.html": [
+        "4e465be7ccf97dfe4e8523f92aa6aec4470acbe2",
+        []
+       ],
        "textarea-pre-wrap-001-ref.html": [
         "31070ea92815e4d3a3ece48ed69da03de02f671e",
         []
@@ -321410,6 +321427,10 @@
        "e19e35b9221c3f106b1db29f46c18acbd5e10c3a",
        []
       ],
+      "kind-of-widget-fallback-input-search-text-border-inline-end-style-001.html.ini": [
+       "1f30d2ea6dce2aba534c240922f6d905ddafec36",
+       []
+      ],
       "kind-of-widget-fallback-input-search-text-border-top-color-001.html.ini": [
        "1b6035bf619946ac7293be42e2d5f3c2c0f37091",
        []
@@ -542525,7 +542546,7 @@
        ]
       ],
       "popover-types.html": [
-       "615c5a818c06c1bd75c5bfc80c2fc38e543048a9",
+       "9941f3228285690149c59d03d5dcba480d18b70b",
        [
         null,
         {}
@@ -569023,6 +569044,15 @@
        }
       ]
      ],
+     "pointerevent_mouse-pointer-preventdefault-passive.html": [
+      "57da09686602f96da63cf8bb0b740f0862732c91",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
      "pointerevent_mouse-pointer-preventdefault.html": [
       "549cbf55d8818291f412a1627765df9611ce614c",
       [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/flex-item-content-overflow-003.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/flex-item-content-overflow-003.html
new file mode 100644
index 0000000..d3a5b976
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/flex-item-content-overflow-003.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1417976">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-fill:auto;">
+  <div style="display:flex;">
+    <div style="width:100px; max-height:50px;">
+      <div style="height:50px; background:green;"></div>
+      <div style="height:50px; background:red;"></div>
+    </div>
+  </div>
+  <div style="position:relative; width:100px; height:50px; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-overflow-001.html b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-overflow-001.html
new file mode 100644
index 0000000..30d6f3b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/grid/grid-item-overflow-001.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1417976">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-fill:auto;">
+  <div style="display:grid;">
+    <div style="width:100px; max-height:50px;">
+      <div style="height:50px; background:green;"></div>
+      <div style="height:50px; background:red;"></div>
+    </div>
+  </div>
+  <div style="position:relative; width:100px; height:50px; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-custom-ident.html.ini b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-custom-ident.html.ini
index d268382..9056752 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-custom-ident.html.ini
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-custom-ident.html.ini
@@ -1,5 +1,8 @@
 [custom-property-transition-custom-ident.html]
   expected:
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "highdpi"): OK
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): OK
-    CRASH
+    if (product == "content_shell") and (os == "linux") and (flag_specific == ""): CRASH
+    if (product == "content_shell") and (os == "mac"): CRASH
+    if product == "chrome": CRASH
+  [A custom property of type <custom-ident> cannot yield a CSS Transition]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-image.html.ini b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-image.html.ini
index 7e24e9fb..68e0c05e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-image.html.ini
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-image.html.ini
@@ -1,5 +1,8 @@
 [custom-property-transition-image.html]
   expected:
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): OK
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "highdpi"): OK
-    CRASH
+    if (product == "content_shell") and (os == "linux") and (flag_specific == ""): CRASH
+    if (product == "content_shell") and (os == "mac"): CRASH
+    if product == "chrome": CRASH
+  [A custom property of type <image> cannot yield a CSS Transition]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-mismatched-list.html.ini b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-mismatched-list.html.ini
index 4c2b3804..398da8fb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-mismatched-list.html.ini
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-mismatched-list.html.ini
@@ -1,5 +1,100 @@
 [custom-property-transition-mismatched-list.html]
   expected:
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): OK
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "highdpi"): OK
-    CRASH
+    if (product == "content_shell") and (os == "linux") and (flag_specific == ""): CRASH
+    if (product == "content_shell") and (os == "mac"): CRASH
+    if product == "chrome": CRASH
+  [A custom property of type <angle># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <angle>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <color># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <color>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <custom-ident># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <custom-ident>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <image># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <image>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <integer># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <integer>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <length-percentage># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <length-percentage>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <length># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <length>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <number># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <number>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <percentage># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <percentage>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <resolution># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <resolution>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <time># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <time>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <url># does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
+
+  [A custom property of type <url>+ does not yield a CSS Transition if the lists do not contain the same number of values]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-url.html.ini b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-url.html.ini
index bd8ba68..e59f4ae 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-url.html.ini
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/animation/custom-property-transition-url.html.ini
@@ -1,5 +1,8 @@
 [custom-property-transition-url.html]
   expected:
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "highdpi"): OK
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): OK
-    CRASH
+    if (product == "content_shell") and (os == "linux") and (flag_specific == ""): CRASH
+    if (product == "content_shell") and (os == "mac"): CRASH
+    if product == "chrome": CRASH
+  [A custom property of type <url> cannot yield a CSS Transition]
+    expected:
+      if (product == "content_shell") and (os == "win"): FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-line-clamp-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-line-clamp-001-ref.html
new file mode 100644
index 0000000..4e465be
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-line-clamp-001-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+.container {
+  font-family: monospace;
+  font-size: 20px;
+  width: 20ch;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
+  overflow: hidden;
+}
+</style>
+<div class="container">
+  123 567 901 345 789
+  123 567 901 345 789
+  123
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-line-clamp-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-line-clamp-001.html
new file mode 100644
index 0000000..292e6554
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-line-clamp-001.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="help" href="https://w3c.github.io/csswg-drafts/css-text-4/#valdef-text-wrap-balance">
+<link rel="match" href="reference/text-wrap-balance-line-clamp-001-ref.html">
+<style>
+.container {
+  font-family: monospace;
+  font-size: 20px;
+  width: 20ch;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
+  overflow: hidden;
+  text-wrap: balance;
+}
+</style>
+<div class="container">
+  123 567 901 345 789
+  123 567 901 345 789
+  123
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-search-text-border-inline-end-style-001.html.ini b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-search-text-border-inline-end-style-001.html.ini
new file mode 100644
index 0000000..1f30d2ea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-input-search-text-border-inline-end-style-001.html.ini
@@ -0,0 +1,3 @@
+[kind-of-widget-fallback-input-search-text-border-inline-end-style-001.html]
+  expected:
+    if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): FAIL
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-types.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-types.html
index 615c5a81..9941f32 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-types.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-types.html
@@ -5,35 +5,33 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
-<div>
+<div id="container">
   <div popover>Popover</div>
   <div popover=manual>Async</div>
   <div popover=manual>Async</div>
-  <script>
-  {
-    const auto = document.currentScript.parentElement.querySelector('[popover=""]');
-    const manual = document.currentScript.parentElement.querySelectorAll('[popover=manual]')[0];
-    const manual2 = document.currentScript.parentElement.querySelectorAll('[popover=manual]')[1];
-    function assert_state_1(autoOpen,manualOpen,manual2Open) {
-      assert_equals(auto.matches(':open'),autoOpen,'auto open state is incorrect');
-      assert_equals(manual.matches(':open'),manualOpen,'manual open state is incorrect');
-      assert_equals(manual2.matches(':open'),manual2Open,'manual2 open state is incorrect');
-    }
-    test(() => {
-      assert_state_1(false,false,false);
-      auto.showPopover();
-      assert_state_1(true,false,false);
-      manual.showPopover();
-      assert_state_1(true,true,false);
-      manual2.showPopover();
-      assert_state_1(true,true,true);
-      auto.hidePopover();
-      assert_state_1(false,true,true);
-      manual.hidePopover();
-      assert_state_1(false,false,true);
-      manual2.hidePopover();
-      assert_state_1(false,false,false);
-    },'manuals do not close popovers');
-  }
-  </script>
 </div>
+<script>
+  const auto = container.querySelector('[popover=""]');
+  const manual = container.querySelectorAll('[popover=manual]')[0];
+  const manual2 = container.querySelectorAll('[popover=manual]')[1];
+  function assert_state_1(autoOpen,manualOpen,manual2Open) {
+    assert_equals(auto.matches(':open'),autoOpen,'auto open state is incorrect');
+    assert_equals(manual.matches(':open'),manualOpen,'manual open state is incorrect');
+    assert_equals(manual2.matches(':open'),manual2Open,'manual2 open state is incorrect');
+  }
+  test(() => {
+    assert_state_1(false,false,false);
+    auto.showPopover();
+    assert_state_1(true,false,false);
+    manual.showPopover();
+    assert_state_1(true,true,false);
+    manual2.showPopover();
+    assert_state_1(true,true,true);
+    auto.hidePopover();
+    assert_state_1(false,true,true);
+    manual.hidePopover();
+    assert_state_1(false,false,true);
+    manual2.hidePopover();
+    assert_state_1(false,false,false);
+  }, 'manuals do not close popovers');
+</script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/mutation-observer-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/mutation-observer-expected.txt
new file mode 100644
index 0000000..97c437f1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/mutation-observer-expected.txt
@@ -0,0 +1,4 @@
+MutationObserver should keep firing after reloading from a debugger statement
+Paused on debugger statement.
+Successfully reloaded and paused.
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/mutation-observer.js b/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/mutation-observer.js
new file mode 100644
index 0000000..0f6adf9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/mutation-observer.js
@@ -0,0 +1,16 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `MutationObserver should keep firing after reloading from a debugger statement`);
+
+  await dp.Debugger.enable();
+
+  session.navigate('http://devtools.test:8000/inspector-protocol/debugger/reload-on-pause/resources/simple-script.php?script=observer.js');
+  await dp.Debugger.oncePaused();
+  testRunner.log('Paused on debugger statement.');
+
+  dp.Page.reload();
+  await dp.Debugger.oncePaused(),
+  testRunner.log('Successfully reloaded and paused.');
+
+  testRunner.completeTest();
+});
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/resources/observer.js b/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/resources/observer.js
new file mode 100644
index 0000000..3623b527
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/debugger/reload-on-pause/resources/observer.js
@@ -0,0 +1,12 @@
+
+new MutationObserver(mutations => {
+  console.log('from observer');
+  debugger;
+}).observe(document.body, {
+  childList: true,
+  subtree: true
+});
+
+setTimeout(() => {
+  document.body.appendChild(document.createElement('div'));
+}, 0);
diff --git a/third_party/crc32c/BUILD.gn b/third_party/crc32c/BUILD.gn
index 39c151a..d2a7933 100644
--- a/third_party/crc32c/BUILD.gn
+++ b/third_party/crc32c/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/config/features.gni")
 import("//testing/test.gni")
-import("//third_party/google_benchmark/buildconfig.gni")
 
 # Only applied to CRC32C source and tests. (not exported)
 config("crc32c_config") {
@@ -168,16 +167,14 @@
   ]
 }
 
-if (enable_google_benchmarks) {
-  test("crc32c_benchmark") {
-    sources = [ "src/src/crc32c_benchmark.cc" ]
-    configs += [ ":crc32c_config" ]
-    deps = [
-      ":crc32c",
-      ":crc32c_arm64",
-      ":crc32c_internal_headers",
-      ":crc32c_sse42",
-      "//third_party/google_benchmark",
-    ]
-  }
+test("crc32c_benchmark") {
+  sources = [ "src/src/crc32c_benchmark.cc" ]
+  configs += [ ":crc32c_config" ]
+  deps = [
+    ":crc32c",
+    ":crc32c_arm64",
+    ":crc32c_internal_headers",
+    ":crc32c_sse42",
+    "//third_party/google_benchmark",
+  ]
 }
diff --git a/third_party/google_benchmark/BUILD.gn b/third_party/google_benchmark/BUILD.gn
index 20a84ae3..09e0be4 100644
--- a/third_party/google_benchmark/BUILD.gn
+++ b/third_party/google_benchmark/BUILD.gn
@@ -2,86 +2,82 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("buildconfig.gni")
+config("benchmark_config") {
+  include_dirs = [ "src/include" ]
+}
 
-if (enable_google_benchmarks) {
-  config("benchmark_config") {
-    include_dirs = [ "src/include" ]
-  }
+# TODO(crbug.com/1344570): Remove once third_party/google_benchmark no longer
+# uses std::wstring_convert.
+config("benchmark_suppress_warnings") {
+  cflags = [ "-Wno-deprecated-declarations" ]
+}
 
-  # TODO(crbug.com/1344570): Remove once third_party/google_benchmark no longer
-  # uses std::wstring_convert.
-  config("benchmark_suppress_warnings") {
-    cflags = ["-Wno-deprecated-declarations"]
-  }
+source_set("google_benchmark") {
+  testonly = true
 
-  source_set("google_benchmark") {
-    testonly = true
+  public = [ "src/include/benchmark/benchmark.h" ]
 
-    public = [ "src/include/benchmark/benchmark.h" ]
+  sources = [
+    "src/src/arraysize.h",
+    "src/src/benchmark.cc",
+    "src/src/benchmark_api_internal.cc",
+    "src/src/benchmark_api_internal.h",
+    "src/src/benchmark_name.cc",
+    "src/src/benchmark_register.cc",
+    "src/src/benchmark_register.h",
+    "src/src/benchmark_runner.cc",
+    "src/src/benchmark_runner.h",
+    "src/src/check.h",
+    "src/src/colorprint.cc",
+    "src/src/colorprint.h",
+    "src/src/commandlineflags.cc",
+    "src/src/commandlineflags.h",
+    "src/src/complexity.cc",
+    "src/src/complexity.h",
+    "src/src/console_reporter.cc",
+    "src/src/counter.cc",
+    "src/src/counter.h",
+    "src/src/csv_reporter.cc",
+    "src/src/cycleclock.h",
+    "src/src/internal_macros.h",
+    "src/src/json_reporter.cc",
+    "src/src/log.h",
+    "src/src/mutex.h",
+    "src/src/perf_counters.cc",
+    "src/src/perf_counters.h",
+    "src/src/re.h",
+    "src/src/reporter.cc",
+    "src/src/sleep.cc",
+    "src/src/sleep.h",
+    "src/src/statistics.cc",
+    "src/src/statistics.h",
+    "src/src/string_util.cc",
+    "src/src/string_util.h",
+    "src/src/sysinfo.cc",
+    "src/src/thread_manager.h",
+    "src/src/thread_timer.h",
+    "src/src/timers.cc",
+    "src/src/timers.h",
+  ]
 
-    sources = [
-      "src/src/arraysize.h",
-      "src/src/benchmark.cc",
-      "src/src/benchmark_api_internal.cc",
-      "src/src/benchmark_api_internal.h",
-      "src/src/benchmark_name.cc",
-      "src/src/benchmark_register.cc",
-      "src/src/benchmark_register.h",
-      "src/src/benchmark_runner.cc",
-      "src/src/benchmark_runner.h",
-      "src/src/check.h",
-      "src/src/colorprint.cc",
-      "src/src/colorprint.h",
-      "src/src/commandlineflags.cc",
-      "src/src/commandlineflags.h",
-      "src/src/complexity.cc",
-      "src/src/complexity.h",
-      "src/src/console_reporter.cc",
-      "src/src/counter.cc",
-      "src/src/counter.h",
-      "src/src/csv_reporter.cc",
-      "src/src/cycleclock.h",
-      "src/src/internal_macros.h",
-      "src/src/json_reporter.cc",
-      "src/src/log.h",
-      "src/src/mutex.h",
-      "src/src/perf_counters.cc",
-      "src/src/perf_counters.h",
-      "src/src/re.h",
-      "src/src/reporter.cc",
-      "src/src/sleep.cc",
-      "src/src/sleep.h",
-      "src/src/statistics.cc",
-      "src/src/statistics.h",
-      "src/src/string_util.cc",
-      "src/src/string_util.h",
-      "src/src/sysinfo.cc",
-      "src/src/thread_manager.h",
-      "src/src/thread_timer.h",
-      "src/src/timers.cc",
-      "src/src/timers.h",
-    ]
+  all_dependent_configs = [ ":benchmark_config" ]
 
-    all_dependent_configs = [ ":benchmark_config" ]
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+    ":benchmark_suppress_warnings",
+  ]
 
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [
-      "//build/config/compiler:no_chromium_code",
-      ":benchmark_suppress_warnings",
-    ]
+  defines = [
+    # Tell gtest to always use standard regular expressions.
+    "HAVE_GNU_POSIX_REGEX=0",
+    "HAVE_POSIX_REGEX=0",
+    "HAVE_STD_REGEX=1",
+  ]
+}
 
-    defines = [
-      # Tell gtest to always use standard regular expressions.
-      "HAVE_GNU_POSIX_REGEX=0",
-      "HAVE_POSIX_REGEX=0",
-      "HAVE_STD_REGEX=1",
-    ]
-  }
-
-  source_set("benchmark_main") {
-    testonly = true
-    sources = [ "src/src/benchmark_main.cc" ]
-    deps = [ ":google_benchmark" ]
-  }
+source_set("benchmark_main") {
+  testonly = true
+  sources = [ "src/src/benchmark_main.cc" ]
+  deps = [ ":google_benchmark" ]
 }
diff --git a/third_party/google_benchmark/README.chromium b/third_party/google_benchmark/README.chromium
index fdf4f54..e0a77c96 100644
--- a/third_party/google_benchmark/README.chromium
+++ b/third_party/google_benchmark/README.chromium
@@ -9,17 +9,5 @@
 Description:
 A microbenchmark support library.
 
-Google Benchmark is NOT supported for writing benchmarks in Chromium. It is
-included to run benchmarks in third-party code that have already been written
-against Google Benchmarks.
-
-To include this library in the Chromium checkout, add the following clause to
-your .gclient configuration.
-
-    "custom_vars": {
-        "checkout_google_benchmark": True,
-    }
-
-
 Local Additions:
 * gn file for building in chromium
diff --git a/third_party/leveldatabase/BUILD.gn b/third_party/leveldatabase/BUILD.gn
index 0dfc372b1..22be8b57 100644
--- a/third_party/leveldatabase/BUILD.gn
+++ b/third_party/leveldatabase/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
-import("//third_party/google_benchmark/buildconfig.gni")
 
 defines = [ "LEVELDB_PLATFORM_CHROMIUM=1" ]
 
@@ -274,18 +273,14 @@
     deps = [ ":leveldb_testutil" ]
   }
 
-  # The google/banchmark third_party dependency is optional, and only
-  # available if specified. See third_party/google_benchmark/README.chromium.
-  if (enable_google_benchmarks) {
-    test("leveldb_db_bench_log") {
-      sources = [ "src/benchmarks/db_bench_log.cc" ]
-      configs -= [ "//build/config/compiler:chromium_code" ]
-      configs += [ "//build/config/compiler:no_chromium_code" ]
-      deps = [
-        ":leveldb_testutil",
-        "//third_party/google_benchmark",
-      ]
-    }
+  test("leveldb_db_bench_log") {
+    sources = [ "src/benchmarks/db_bench_log.cc" ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+      "//third_party/google_benchmark",
+    ]
   }
 
   executable("leveldb_leveldbutil") {
@@ -300,11 +295,9 @@
     deps = [
       ":env_chromium_unittests",
       ":leveldb_db_bench",
+      ":leveldb_db_bench_log",
       ":leveldb_unittests",
     ]
-    if (enable_google_benchmarks) {
-      deps += [ ":leveldb_db_bench_log" ]
-    }
   }
 }
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 5308cdf..0b6d0d96 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -90,6 +90,7 @@
     },
 
     'chromium.accessibility': {
+      'fuchsia-x64-accessibility-rel': 'release_bot_fuchsia_reclient',
       'linux-blink-web-tests-force-accessibility-rel': 'release_bot_blink_accessibility_reclient',
     },
 
@@ -1015,6 +1016,7 @@
     },
 
     'tryserver.chromium.accessibility': {
+      'fuchsia-x64-accessibility-rel': 'release_bot_fuchsia_reclient',
       'linux-blink-web-tests-force-accessibility-rel': 'release_bot_blink_accessibility_reclient',
     },
 
diff --git a/tools/mb/mb_config_expectations/chromium.accessibility.json b/tools/mb/mb_config_expectations/chromium.accessibility.json
index d11b1e4..ce2b5bf 100644
--- a/tools/mb/mb_config_expectations/chromium.accessibility.json
+++ b/tools/mb/mb_config_expectations/chromium.accessibility.json
@@ -1,4 +1,13 @@
 {
+  "fuchsia-x64-accessibility-rel": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "is_component_build": false,
+      "is_debug": false,
+      "target_os": "fuchsia",
+      "use_remoteexec": true
+    }
+  },
   "linux-blink-web-tests-force-accessibility-rel": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.accessibility.json b/tools/mb/mb_config_expectations/tryserver.chromium.accessibility.json
index d11b1e4..ce2b5bf 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.accessibility.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.accessibility.json
@@ -1,4 +1,13 @@
 {
+  "fuchsia-x64-accessibility-rel": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "is_component_build": false,
+      "is_debug": false,
+      "target_os": "fuchsia",
+      "use_remoteexec": true
+    }
+  },
   "linux-blink-web-tests-force-accessibility-rel": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 5a5d5648..1b6e17c 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -19623,6 +19623,14 @@
   <description>User pressed 'Paste and Go' in the app menu.</description>
 </action>
 
+<action name="MobileMenuPinTab">
+  <owner>ewannpv@chromium.org</owner>
+  <owner>pakzhygitov@google.com</owner>
+  <description>
+    User tapped on the 'Pin Tab' action in the iOS overflow menu.
+  </description>
+</action>
+
 <action name="MobileMenuPriceNotifications">
   <owner>danieltwhite@google.com</owner>
   <owner>ajuma@chromium.org</owner>
@@ -19763,6 +19771,14 @@
   <description>User pressed the unfollow option in the app menu.</description>
 </action>
 
+<action name="MobileMenuUnpinTab">
+  <owner>ewannpv@chromium.org</owner>
+  <owner>pakzhygitov@google.com</owner>
+  <description>
+    User tapped on the 'Unpin Tab' action in the iOS overflow menu.
+  </description>
+</action>
+
 <action name="MobileMenuVoiceSearch">
   <owner>gambard@chromium.org</owner>
   <description>User pressed 'Voice Search' in the app menu.</description>
@@ -20945,6 +20961,24 @@
   </description>
 </action>
 
+<action name="MobileSnackbarUndoPinAction">
+  <owner>ewannpv@chromium.org</owner>
+  <owner>pakzhygitov@google.com</owner>
+  <description>
+    User tapped on the UNDO button of the snackbar displayed after pinning a tab
+    from the iOS overflow menu.
+  </description>
+</action>
+
+<action name="MobileSnackbarUndoUnpinAction">
+  <owner>ewannpv@chromium.org</owner>
+  <owner>pakzhygitov@google.com</owner>
+  <description>
+    User tapped on the UNDO button of the snackbar displayed after unpinning a
+    tab from the iOS overflow menu.
+  </description>
+</action>
+
 <action name="MobileStackViewCloseTab">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f2c6b964..152f721 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -55325,6 +55325,7 @@
   <int value="1" label="Closed regular browser"/>
   <int value="2" label="Opened regular browser"/>
   <int value="3" label="Opened devtools browser"/>
+  <int value="4" label="Opened normal troubleshooting browser"/>
 </enum>
 
 <enum name="KioskInternetAccessInfo">
@@ -59834,6 +59835,8 @@
   <int value="-1183321008" label="ImeDecoderWithSandbox:disabled"/>
   <int value="-1183009666" label="OfflinePagesLimitlessPrefetching:disabled"/>
   <int value="-1182777927" label="ShareButtonInTopToolbar:enabled"/>
+  <int value="-1182771360"
+      label="AutofillVirtualCardsOnTouchToFillAndroid:disabled"/>
   <int value="-1182087678"
       label="CrossOriginOpenerPolicyAccessReporting:disabled"/>
   <int value="-1179917414" label="DefaultChromeAppUninstallSync:disabled"/>
@@ -60918,6 +60921,7 @@
   <int value="-582300629" label="UserNotes:enabled"/>
   <int value="-581236612" label="ConnectivityDiagnosticsWebUi:disabled"/>
   <int value="-580897686" label="SharedHighlightingAmp:enabled"/>
+  <int value="-579897981" label="EnableEdgeDetection:enabled"/>
   <int value="-579656054" label="CCTResizableSideSheetForThirdParties:enabled"/>
   <int value="-579192400" label="disable-input-view"/>
   <int value="-577982497" label="CupsPrintersUiOverhaul:enabled"/>
@@ -61477,6 +61481,8 @@
   <int value="-262368430" label="PersistShareHubOnAppSwitch:enabled"/>
   <int value="-262122630" label="ArcEnableDocumentsProviderInFilesApp:enabled"/>
   <int value="-261398170" label="ChromeLabs:enabled"/>
+  <int value="-259952983"
+      label="AutofillVirtualCardsOnTouchToFillAndroid:enabled"/>
   <int value="-258081634" label="AutofillAssistantDirectActions:disabled"/>
   <int value="-257600773" label="MessagesPreinstall:disabled"/>
   <int value="-257478609" label="TextfieldFocusOnTapUp:enabled"/>
@@ -64982,6 +64988,7 @@
   <int value="1722748383" label="EnableAppReinstallZeroState:disabled"/>
   <int value="1723601083" label="enable-app-window-controls"/>
   <int value="1724247189" label="StylusHandwriting:enabled"/>
+  <int value="1724706885" label="EnableEdgeDetection:disabled"/>
   <int value="1724800383" label="AsmJsToWebAssembly:disabled"/>
   <int value="1725438666" label="NewStyleNotifications:enabled"/>
   <int value="1729369067" label="StartSurfaceDisabledFeedImprovement:disabled"/>
@@ -84540,7 +84547,7 @@
   <int value="44" label="kCrossSiteRedirect"/>
   <int value="45" label="kCrossSiteNavigation"/>
   <int value="46" label="kSameSiteCrossOriginRedirect"/>
-  <int value="47" label="kSameSiteCrossOriginNavigation"/>
+  <int value="47" label="kSameSiteCrossOriginNavigation (obsoleted)"/>
   <int value="48" label="kSameSiteCrossOriginRedirectNotOptIn"/>
   <int value="49" label="kSameSiteCrossOriginNavigationNotOptIn"/>
   <int value="50" label="kActivationNavigationParameterMismatch"/>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index 624a23b..1653a013 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -819,6 +819,29 @@
   </summary>
 </histogram>
 
+<histogram name="FileBrowser.Search.ResultCount" units="count"
+    expires_after="M120">
+  <owner>simmonsjosh@google.com</owner>
+  <owner>src/ui/file_manager/OWNERS</owner>
+  <summary>
+    ChromeOS File Browser: The number of results returned by a search query.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.Search.{Source}.Latency" units="ms"
+    expires_after="M120">
+  <owner>simmonsjosh@google.com</owner>
+  <owner>src/ui/file_manager/OWNERS</owner>
+  <summary>
+    ChromeOS File Browser: The time it took to complete user search request.
+  </summary>
+  <token key="Source">
+    <variant name="Drive"/>
+    <variant name="Local"/>
+    <variant name="Removable"/>
+  </token>
+</histogram>
+
 <histogram name="FileBrowser.SWA.Create" enum="FileDialogType"
     expires_after="2023-08-08">
   <owner>simmonsjosh@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/geolocation/histograms.xml b/tools/metrics/histograms/metadata/geolocation/histograms.xml
index 7fbfe52b..d4fae47 100644
--- a/tools/metrics/histograms/metadata/geolocation/histograms.xml
+++ b/tools/metrics/histograms/metadata/geolocation/histograms.xml
@@ -109,13 +109,6 @@
   </summary>
 </histogram>
 
-<histogram name="Geolocation.NetworkLocationRequest.NetError"
-    enum="NetErrorCodes" expires_after="2023-04-16">
-  <owner>JamesHollyer@chromium.org</owner>
-  <owner>device-dev@chromium.org</owner>
-  <summary>Network errors in NetworkLocationRequest.</summary>
-</histogram>
-
 <histogram name="Geolocation.NetworkLocationRequest.ResponseCode"
     enum="HttpResponseCode" expires_after="2023-08-20">
   <owner>mattreynolds@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/help_app/OWNERS b/tools/metrics/histograms/metadata/help_app/OWNERS
new file mode 100644
index 0000000..e94d940
--- /dev/null
+++ b/tools/metrics/histograms/metadata/help_app/OWNERS
@@ -0,0 +1,5 @@
+per-file OWNERS=file://tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+
+# Prefer sending CLs to the owners listed below.
+# Use chromium-metrics-reviews@google.com as a backup.
+tby@chromium.org
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 6a14a8c4..270c1a8 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -4989,7 +4989,6 @@
   <suffix name="Background" label=""/>
   <suffix name="Foreground" label=""/>
   <affected-histogram name="RendererScheduler.TaskDurationPerTaskType2"/>
-  <affected-histogram name="RendererScheduler.TaskDurationPerThreadType2"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="RendererScheduler_TaskDurationPerTaskTypeSplit"
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index 057d2e9a..c931003 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -2486,6 +2486,31 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.Total.HibernatedCanvas.OriginalSize" units="MB"
+    expires_after="2023-08-08">
+  <owner>lizeb@chromium.org</owner>
+  <owner>chrome-memory@chromium.org</owner>
+  <summary>
+    Total size of all hibernated canvases across all renderers, prior to
+    compression.
+
+    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
+    Android.
+  </summary>
+</histogram>
+
+<histogram name="Memory.Total.HibernatedCanvas.Size" units="MB"
+    expires_after="2023-08-08">
+  <owner>lizeb@chromium.org</owner>
+  <owner>chrome-memory@chromium.org</owner>
+  <summary>
+    Total size of all hibernated canvases across all renderers.
+
+    Recorded at Poisson sampled time intervals with a mean of 5 minutes on
+    Android.
+  </summary>
+</histogram>
+
 <histogram name="Memory.Total.PrivateMemoryFootprint" units="MB"
     expires_after="never">
 <!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
diff --git a/tools/metrics/histograms/metadata/renderer/histograms.xml b/tools/metrics/histograms/metadata/renderer/histograms.xml
index 352e332c..1f1253b 100644
--- a/tools/metrics/histograms/metadata/renderer/histograms.xml
+++ b/tools/metrics/histograms/metadata/renderer/histograms.xml
@@ -314,7 +314,7 @@
 </histogram>
 
 <histogram name="Renderer.Font.PrimaryFont.DomContentLoaded.Style" units="ms"
-    expires_after="2023-03-19">
+    expires_after="2023-08-20">
   <owner>kojii@chromium.org</owner>
   <owner>tkent@chromium.org</owner>
   <owner>yosin@chromium.org</owner>
@@ -638,24 +638,6 @@
   </summary>
 </histogram>
 
-<histogram name="RendererScheduler.TaskDurationPerThreadType2"
-    enum="RendererSchedulerThreadType" expires_after="M85">
-  <owner>altimin@chromium.org</owner>
-  <owner>lpy@chromium.org</owner>
-  <summary>
-    Total duration (reported in full seconds) of renderer tasks split by per
-    thread type. Note that partial seconds are rounded up/down so that they
-    average to the correct value when many reports are added. For more details,
-    check base::ScaledLinearHistogram.
-
-    Used to compare CPU usage of tasks from different threads. Reported each
-    time a task is completed.
-
-    Note that this metric discards tasks longer than 30 seconds because they are
-    considered to be a result of measurement glitch.
-  </summary>
-</histogram>
-
 <histogram name="RendererScheduler.TaskTime2" units="microseconds"
     expires_after="never">
   <owner>sunyunjia@chromium.org</owner>
diff --git a/ui/base/models/menu_model.cc b/ui/base/models/menu_model.cc
index 69b2f556..cc1478674 100644
--- a/ui/base/models/menu_model.cc
+++ b/ui/base/models/menu_model.cc
@@ -97,4 +97,12 @@
   menu_model_delegate_ = delegate;
 }
 
+absl::optional<ui::ColorId> MenuModel::GetForegroundColor(size_t index) {
+  return absl::nullopt;
+}
+
+absl::optional<ui::ColorId> MenuModel::GetSubmenuBackgroundColor(size_t index) {
+  return absl::nullopt;
+}
+
 }  // namespace ui
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index f1f8f33..50eeaa9 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -10,9 +10,11 @@
 #include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/models/menu_model_delegate.h"
 #include "ui/base/models/menu_separator_types.h"
+#include "ui/color/color_id.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace gfx {
@@ -171,6 +173,9 @@
                                            MenuModel** model,
                                            size_t* index);
 
+  virtual absl::optional<ui::ColorId> GetForegroundColor(size_t index);
+  virtual absl::optional<ui::ColorId> GetSubmenuBackgroundColor(size_t index);
+
  private:
   // MenuModelDelegate. Weak. Could be null.
   raw_ptr<MenuModelDelegate> menu_model_delegate_;
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 7b3304a..b22203e38 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -618,7 +618,7 @@
     Preparing to sync <ph name="NUMBER_OF_ITEMS">$1<ex>3</ex></ph> items...
   </message>
   <message name="IDS_FILE_BROWSER_READY_TO_SYNC_MY_DRIVE" desc="Files app message displayed to users after they copy files into My Drive to help them understand that files pasted into My Drive are not immediately synced.">
-    Ready
+    Ready to sync
   </message>
   <message name="IDS_FILE_BROWSER_EXTRACT_FILESYSTEM_ERROR" desc="File Manager error message.">
     Extract operation failed. <ph name="ERROR_MESSAGE">$1<ex>An error occurred (code: ABORT)</ex></ph>
@@ -879,7 +879,7 @@
     This folder
   </message>
   <message name="IDS_FILE_BROWSER_SEARCH_OPTIONS_RECENCY_ALL_TIME" desc="Text shown in search options if search results are not limited by time">
-    All time
+    Any time
   </message>
   <message name="IDS_FILE_BROWSER_SEARCH_OPTIONS_RECENCY_TODAY" desc="Text shown in search options if search results are limited to files modified today">
     Today
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_READY_TO_SYNC_MY_DRIVE.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_READY_TO_SYNC_MY_DRIVE.png.sha1
index 63f7cf6..cd554a1 100644
--- a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_READY_TO_SYNC_MY_DRIVE.png.sha1
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_READY_TO_SYNC_MY_DRIVE.png.sha1
@@ -1 +1 @@
-479d5939780c1dae1e726c8a94ca3b717254d9d4
\ No newline at end of file
+57e613ca440dc058f4fda179ab85574b7881fb9b
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_OPTIONS_RECENCY_ALL_TIME.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_OPTIONS_RECENCY_ALL_TIME.png.sha1
index 5a4cc6a..1513614 100644
--- a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_OPTIONS_RECENCY_ALL_TIME.png.sha1
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_SEARCH_OPTIONS_RECENCY_ALL_TIME.png.sha1
@@ -1 +1 @@
-f6107ac775b834cad19d0151e563aeab589b8975
\ No newline at end of file
+e8aada00f5173dd232a1183c92e5f9ac60a5ffe0
\ No newline at end of file
diff --git a/ui/events/ozone/features.cc b/ui/events/ozone/features.cc
index 5a5273e2..2eeafa3c 100644
--- a/ui/events/ozone/features.cc
+++ b/ui/events/ozone/features.cc
@@ -18,6 +18,10 @@
              "EnableNeuralPalmAdaptiveHold",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kEnableEdgeDetection,
+             "EnableEdgeDetection",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kEnableNeuralStylusReportFilter,
              "EnableNeuralStylusReportFilter",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/ui/events/ozone/features.h b/ui/events/ozone/features.h
index 54249fc..3c65e08 100644
--- a/ui/events/ozone/features.h
+++ b/ui/events/ozone/features.h
@@ -20,6 +20,9 @@
 BASE_DECLARE_FEATURE(kEnableNeuralPalmAdaptiveHold);
 
 COMPONENT_EXPORT(EVENTS_OZONE)
+BASE_DECLARE_FEATURE(kEnableEdgeDetection);
+
+COMPONENT_EXPORT(EVENTS_OZONE)
 BASE_DECLARE_FEATURE(kEnableNeuralStylusReportFilter);
 
 COMPONENT_EXPORT(EVENTS_OZONE) BASE_DECLARE_FEATURE(kEnableOrdinalMotion);
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index a4789c3..8b4d47c9 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -390,6 +390,7 @@
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
     "//ui/file_manager/file_manager/externs:volume_info",
     "//ui/file_manager/file_manager/externs:volume_manager",
+    "//ui/file_manager/file_manager/externs/ts:store",
   ]
 }
 
@@ -421,6 +422,7 @@
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
     "//ui/file_manager/file_manager/externs:volume_info",
+    "//ui/file_manager/file_manager/externs/ts:store",
   ]
 }
 
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
index 7a8bad5..7cee29e 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -11,6 +11,8 @@
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 import {VolumeInfo} from '../../externs/volume_info.js';
 import {VolumeManager} from '../../externs/volume_manager.js';
+import {removeVolume} from '../../state/actions/volumes.js';
+import {getStore} from '../../state/store.js';
 
 import {EntryLocationImpl} from './entry_location_impl.js';
 import {VolumeInfoListImpl} from './volume_info_list_impl.js';
@@ -312,7 +314,9 @@
             } else {
               console.debug(`Unmounted '${volumeId}'`);
             }
-
+            if (util.isFilesAppExperimental()) {
+              getStore().dispatch(removeVolume({volumeId}));
+            }
             this.volumeInfoList.remove(volumeId);
             this.finishRequest_(requestKey, status);
             return;
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_util.js b/ui/file_manager/file_manager/background/js/volume_manager_util.js
index d03b18f..427c2aea 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_util.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_util.js
@@ -5,6 +5,8 @@
 import {str, util} from '../../common/js/util.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 import {VolumeInfo} from '../../externs/volume_info.js';
+import {addVolume} from '../../state/actions/volumes.js';
+import {getStore} from '../../state/store.js';
 
 import {VolumeInfoImpl} from './volume_info_impl.js';
 
@@ -137,6 +139,13 @@
             volumeMetadata.driveLabel, volumeMetadata.remoteMountPath,
             volumeMetadata.vmType);
       })
+      .then(async (volumeInfo) => {
+        if (util.isFilesAppExperimental()) {
+          await volumeInfo.resolveDisplayRoot();
+          getStore().dispatch(addVolume({volumeMetadata, volumeInfo}));
+        }
+        return volumeInfo;
+      })
       .catch(
           /** @param {*} error */
           error => {
diff --git a/ui/file_manager/file_manager/definitions/file_manager.d.ts b/ui/file_manager/file_manager/definitions/file_manager.d.ts
index 1e178827..4bc563a 100644
--- a/ui/file_manager/file_manager/definitions/file_manager.d.ts
+++ b/ui/file_manager/file_manager/definitions/file_manager.d.ts
@@ -27,6 +27,7 @@
   interface Window {
     fileManager: FileManager;
     IN_TEST: boolean;
+    store: Store;
   }
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index c8401f7e..b49fe11 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -326,7 +326,11 @@
 }
 
 js_library("android_app_list_model") {
-  deps = [ "//ash/webui/common/resources:event_target" ]
+  deps = [
+    "//ash/webui/common/resources:event_target",
+    "//ui/file_manager/file_manager/common/js:util",
+    "//ui/file_manager/file_manager/externs/ts:store",
+  ]
 }
 
 js_library("app_state_controller") {
@@ -364,6 +368,7 @@
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
     "//ui/file_manager/file_manager/externs/background:crostini",
+    "//ui/file_manager/file_manager/externs/ts:store",
   ]
   externs_list = [
     "//ui/file_manager/file_manager/externs/polymer_elements/files_toast.js",
@@ -636,6 +641,7 @@
     "//ui/file_manager/file_manager/externs/background:file_manager_base",
     "//ui/file_manager/file_manager/externs/background:file_operation_manager",
     "//ui/file_manager/file_manager/externs/background:progress_center",
+    "//ui/file_manager/file_manager/externs/ts:store",
   ]
 }
 
@@ -818,6 +824,7 @@
     "//ui/file_manager/file_manager/common/js:metrics",
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
+    "//ui/file_manager/file_manager/externs/ts:store",
   ]
 }
 
@@ -846,6 +853,7 @@
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
     "//ui/file_manager/file_manager/externs:volume_manager",
+    "//ui/file_manager/file_manager/externs/ts:store",
   ]
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/android_app_list_model.js b/ui/file_manager/file_manager/foreground/js/android_app_list_model.js
index 297283e8..d711b73 100644
--- a/ui/file_manager/file_manager/foreground/js/android_app_list_model.js
+++ b/ui/file_manager/file_manager/foreground/js/android_app_list_model.js
@@ -4,6 +4,10 @@
 
 import {NativeEventTarget as EventTarget} from 'chrome://resources/ash/common/event_target.js';
 
+import {util} from '../../common/js/util.js';
+import {addAndroidApps} from '../../state/actions/android_apps.js';
+import {getStore} from '../../state/store.js';
+
 /**
  * Model for managing a list of Android apps.
  */
@@ -33,6 +37,9 @@
 
     chrome.fileManagerPrivate.getAndroidPickerApps(extensions, apps => {
       this.apps_ = apps;
+      if (util.isFilesAppExperimental()) {
+        getStore().dispatch(addAndroidApps({apps}));
+      }
       this.dispatchEvent(new Event('permuted'));
     });
   }
diff --git a/ui/file_manager/file_manager/foreground/js/crostini_controller.js b/ui/file_manager/file_manager/foreground/js/crostini_controller.js
index ebaeab3..db1bc259 100644
--- a/ui/file_manager/file_manager/foreground/js/crostini_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/crostini_controller.js
@@ -5,9 +5,12 @@
 import {assert} from 'chrome://resources/ash/common/assert.js';
 
 import {FakeEntryImpl} from '../../common/js/files_app_entry_types.js';
-import {str, strf} from '../../common/js/util.js';
+import {str, strf, util} from '../../common/js/util.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 import {Crostini} from '../../externs/background/crostini.js';
+import {addUiEntry, removeUiEntry} from '../../state/actions/ui_entries.js';
+import {crostiniPlaceHolderKey} from '../../state/reducers/volumes.js';
+import {getStore} from '../../state/store.js';
 
 import {constants} from './constants.js';
 import {DirectoryModel} from './directory_model.js';
@@ -55,14 +58,20 @@
     // Setup Linux files fake root.
     let crostiniNavigationModelItem;
     if (this.crostini_.isEnabled(constants.DEFAULT_CROSTINI_VM)) {
+      const crostiniEntry = new FakeEntryImpl(
+          str('LINUX_FILES_ROOT_LABEL'), VolumeManagerCommon.RootType.CROSTINI);
       crostiniNavigationModelItem = new NavigationModelFakeItem(
           str('LINUX_FILES_ROOT_LABEL'), NavigationModelItemType.CROSTINI,
-          new FakeEntryImpl(
-              str('LINUX_FILES_ROOT_LABEL'),
-              VolumeManagerCommon.RootType.CROSTINI));
+          crostiniEntry);
       crostiniNavigationModelItem.disabled = this.disabled_;
+      if (util.isFilesAppExperimental()) {
+        getStore().dispatch(addUiEntry({entry: crostiniEntry}));
+      }
     } else {
       crostiniNavigationModelItem = null;
+      if (util.isFilesAppExperimental()) {
+        getStore().dispatch(removeUiEntry({key: crostiniPlaceHolderKey}));
+      }
     }
     this.directoryTree_.dataModel.linuxFilesItem = crostiniNavigationModelItem;
     // Redraw the tree to ensure 'Linux files' is added/removed.
diff --git a/ui/file_manager/file_manager/foreground/js/directory_contents.js b/ui/file_manager/file_manager/foreground/js/directory_contents.js
index d546cfb..a18c723 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_contents.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_contents.js
@@ -321,6 +321,7 @@
     const now = new Date();
     if (this.isSearchingLocal_()) {
       searchPromises.push(new Promise((resolve, reject) => {
+        metrics.startInterval('Search.Local.Latency');
         const rootDir = this.isSearchingRoot_() ?
             this.entry_.filesystem.root :
             /** @type {DirectoryEntry} */ (util.unwrapEntry(this.entry_));
@@ -345,6 +346,7 @@
                     util.FileError.NOT_READABLE_ERR,
                     chrome.runtime.lastError.message));
               } else {
+                metrics.recordInterval('Search.Local.Latency');
                 resolve(entries);
               }
             });
@@ -352,6 +354,7 @@
     }
     if (this.isSearchingDrive_()) {
       searchPromises.push(new Promise((resolve, reject) => {
+        metrics.startInterval('Search.Drive.Latency');
         chrome.fileManagerPrivate.searchDrive(
             {
               query: this.query_,
@@ -368,6 +371,7 @@
               } else if (!entries) {
                 reject(createDOMError(util.FileError.INVALID_MODIFICATION_ERR));
               } else {
+                metrics.recordInterval('Search.Drive.Latency');
                 resolve(entries);
               }
             });
@@ -379,14 +383,17 @@
       successCallback();
     }
     Promise.allSettled(searchPromises).then((results) => {
+      let resultCount = 0;
       for (const result of results) {
         if (result.status === 'rejected') {
           errorCallback(/** @type {DOMError} */ (result.reason));
         } else if (result.status === 'fulfilled') {
           entriesCallback(result.value);
+          resultCount += result.value.length;
         }
       }
       successCallback();
+      metrics.recordMediumCount('Search.ResultCount', resultCount);
     });
   }
 }
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index d64812bd..674d713 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -28,7 +28,10 @@
 import {FakeEntry, FilesAppDirEntry} from '../../externs/files_app_entry_interfaces.js';
 import {ForegroundWindow} from '../../externs/foreground_window.js';
 import {PropStatus} from '../../externs/ts/state.js';
+import {Store} from '../../externs/ts/store.js';
 import {updateSearch} from '../../state/actions.js';
+import {addUiEntry, removeUiEntry} from '../../state/actions/ui_entries.js';
+import {trashRootKey} from '../../state/reducers/volumes.js';
 import {getEmptyState, getStore} from '../../state/store.js';
 
 import {ActionsController} from './actions_controller.js';
@@ -403,6 +406,9 @@
      */
     this.guestMode_ = false;
 
+    /** @private {!Store} */
+    this.store_ = getStore();
+
     startColorChangeUpdater();
   }
 
@@ -1105,7 +1111,9 @@
         str('RECENT_ROOT_LABEL'), VolumeManagerCommon.RootType.RECENT,
         this.getSourceRestriction_(),
         chrome.fileManagerPrivate.FileCategory.ALL);
-
+    if (util.isFilesAppExperimental()) {
+      this.store_.dispatch(addUiEntry({entry: this.recentEntry_}));
+    }
     assert(this.launchParams_);
     this.selectionHandler_ = new FileSelectionHandler(
         assert(this.directoryModel_), assert(this.fileOperationManager_),
@@ -1384,7 +1392,7 @@
     const searchQuery = this.launchParams_.searchQuery;
     if (searchQuery) {
       metrics.startInterval('Load.ProcessInitialSearchQuery');
-      getStore().dispatch(updateSearch({
+      this.store_.dispatch(updateSearch({
         query: searchQuery,
         status: PropStatus.STARTED,
         options: undefined,
@@ -1718,10 +1726,16 @@
             str('TRASH_ROOT_LABEL'), NavigationModelItemType.TRASH,
             new TrashRootEntry());
       }
+      if (util.isFilesAppExperimental()) {
+        this.store_.dispatch(addUiEntry({entry: this.fakeTrashItem_.entry}));
+      }
       this.directoryTree.dataModel.fakeTrashItem = this.fakeTrashItem_;
       return;
     }
 
+    if (util.isFilesAppExperimental()) {
+      this.store_.dispatch(removeUiEntry({key: trashRootKey}));
+    }
     this.directoryTree.dataModel.fakeTrashItem = null;
     this.navigateAwayFromDisabledRoot_(this.fakeTrashItem_);
   }
@@ -1733,19 +1747,19 @@
    */
   toggleDriveRootOnPreferencesUpdate_() {
     if (this.driveEnabled_) {
+      const driveFakeRoot = new FakeEntryImpl(
+          str('DRIVE_DIRECTORY_LABEL'),
+          VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT);
       if (!this.fakeDriveItem_) {
         this.fakeDriveItem_ = new NavigationModelFakeItem(
             str('DRIVE_DIRECTORY_LABEL'), NavigationModelItemType.DRIVE,
-            new FakeEntryImpl(
-                str('DRIVE_DIRECTORY_LABEL'),
-                VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT));
+            driveFakeRoot);
         this.fakeDriveItem_.disabled = this.volumeManager_.isDisabled(
             VolumeManagerCommon.VolumeType.DRIVE);
       }
       this.directoryTree.dataModel.fakeDriveItem = this.fakeDriveItem_;
       return;
     }
-
     this.directoryTree.dataModel.fakeDriveItem = null;
     this.navigateAwayFromDisabledRoot_(this.fakeDriveItem_);
   }
diff --git a/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js b/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js
index 7efed3e9..a657d39 100644
--- a/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js
+++ b/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js
@@ -10,6 +10,8 @@
 import {metrics} from '../../common/js/metrics.js';
 import {util} from '../../common/js/util.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
+import {addFolderShortcut, refreshFolderShortcut, removeFolderShortcut} from '../../state/actions/folder_shortcuts.js';
+import {getStore} from '../../state/store.js';
 
 /**
  * The drive mount path used in the persisted storage. It must be '/drive'.
@@ -35,6 +37,7 @@
     this.pendingPaths_ = {};  // Hash map for easier deleting.
     this.unresolvablePaths_ = {};
     this.lastDriveRootURL_ = null;
+    this.store_ = getStore();
 
     // Queue to serialize resolving entries.
     this.queue_ = new AsyncQueue();
@@ -181,6 +184,9 @@
         }
         // If something changed, then save.
         if (changed) {
+          if (util.isFilesAppExperimental()) {
+            this.store_.dispatch(refreshFolderShortcut({entries: this.array_}));
+          }
           this.save_();
         }
         queueCallback();
@@ -310,6 +316,9 @@
    */
   add(value) {
     const result = this.addInternal_(value);
+    if (util.isFilesAppExperimental()) {
+      this.store_.dispatch(addFolderShortcut(value));
+    }
     metrics.recordUserAction('FolderShortcut.Add');
     this.save_();
     return result;
@@ -361,6 +370,9 @@
   remove(value) {
     const result = this.removeInternal_(value);
     if (result !== -1) {
+      if (util.isFilesAppExperimental()) {
+        this.store_.dispatch(removeFolderShortcut(value));
+      }
       this.save_();
       metrics.recordUserAction('FolderShortcut.Remove');
     }
diff --git a/ui/file_manager/file_manager/foreground/js/guest_os_controller.js b/ui/file_manager/file_manager/foreground/js/guest_os_controller.js
index 4e130427..8702920 100644
--- a/ui/file_manager/file_manager/foreground/js/guest_os_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/guest_os_controller.js
@@ -7,6 +7,8 @@
 import {util} from '../../common/js/util.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 import {VolumeManager} from '../../externs/volume_manager.js';
+import {addUiEntry, removeUiEntry} from '../../state/actions/ui_entries.js';
+import {getEntry, getStore} from '../../state/store.js';
 
 import {DirectoryModel} from './directory_model.js';
 import {NavigationModelFakeItem, NavigationModelItemType} from './navigation_list_model.js';
@@ -55,10 +57,24 @@
    * @param {!Array<!chrome.fileManagerPrivate.MountableGuest>} guests
    */
   async onMountableGuestsChanged(guests) {
+    const store = getStore();
+    if (util.isFilesAppExperimental()) {
+      const newGuestIdSet = new Set(guests.map(guest => guest.id));
+      const state = store.getState();
+      // Remove non-existed guest os.
+      for (const uiEntryKey of state.uiEntries) {
+        const uiEntry = getEntry(state, uiEntryKey);
+        if (uiEntry && 'guest_id' in uiEntry &&
+            !newGuestIdSet.has(uiEntry.guest_id)) {
+          store.dispatch(removeUiEntry({key: uiEntryKey}));
+        }
+      }
+    }
     this.directoryTree_.dataModel.guestOsPlaceholders = guests.map(guest => {
+      const guestOsEntry =
+          new GuestOsPlaceholder(guest.displayName, guest.id, guest.vmType);
       const navigationModelItem = new NavigationModelFakeItem(
-          guest.displayName, NavigationModelItemType.GUEST_OS,
-          new GuestOsPlaceholder(guest.displayName, guest.id, guest.vmType));
+          guest.displayName, NavigationModelItemType.GUEST_OS, guestOsEntry);
       if (guest.vmType == chrome.fileManagerPrivate.VmType.ARCVM) {
         navigationModelItem.disabled = this.volumeManager_.isDisabled(
             VolumeManagerCommon.VolumeType.ANDROID_FILES);
@@ -66,6 +82,9 @@
         navigationModelItem.disabled = this.volumeManager_.isDisabled(
             VolumeManagerCommon.VolumeType.GUEST_OS);
       }
+      if (util.isFilesAppExperimental()) {
+        store.dispatch(addUiEntry({entry: guestOsEntry}));
+      }
       return navigationModelItem;
     });
 
diff --git a/ui/file_manager/file_manager/foreground/js/path_component.js b/ui/file_manager/file_manager/foreground/js/path_component.js
index 0e7d266..6a9dc94 100644
--- a/ui/file_manager/file_manager/foreground/js/path_component.js
+++ b/ui/file_manager/file_manager/foreground/js/path_component.js
@@ -83,7 +83,11 @@
     let displayRootFullPath = locationInfo.volumeInfo.displayRoot.fullPath;
 
     const prefixEntry = locationInfo.volumeInfo.prefixEntry;
-    if (prefixEntry) {
+    // Directories under Drive Fake Root can return the fake root entry list as
+    // prefix entry, but we will never show "Google Drive" as the prefix in the
+    // breadcrumb.
+    if (prefixEntry &&
+        prefixEntry.rootType !== VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT) {
       components.push(new PathComponent(
           prefixEntry.name, prefixEntry.toURL(), prefixEntry));
     }
diff --git a/ui/file_manager/file_manager/state/store.ts b/ui/file_manager/file_manager/state/store.ts
index 0ebe1a4..232b0c95c 100644
--- a/ui/file_manager/file_manager/state/store.ts
+++ b/ui/file_manager/file_manager/state/store.ts
@@ -19,8 +19,10 @@
 /**
  * Store singleton instance.
  * It's only exposed via `getStore()` to guarantee it's a single instance.
+ * TODO(b/272120634): Use window.store temporarily, uncomment below code after
+ * the duplicate store issue is resolved.
  */
-let store: null|Store = null;
+// let store: null|Store = null;
 
 /**
  * Returns the singleton instance for the Files app's Store.
@@ -29,12 +31,14 @@
  * at the app's main entry point.
  */
 export function getStore(): Store {
-  if (!store) {
-    store =
+  // TODO(b/272120634): Put the store on window to prevent Store being created
+  // twice.
+  if (!window.store) {
+    window.store =
         new BaseStore<State, Action>({allEntries: {}} as State, rootReducer);
   }
 
-  return store;
+  return window.store;
 }
 
 export function getEmptyState(): State {
diff --git a/ui/file_manager/integration_tests/file_manager/search.js b/ui/file_manager/integration_tests/file_manager/search.js
index 0d25d1e..5a46ddb 100644
--- a/ui/file_manager/integration_tests/file_manager/search.js
+++ b/ui/file_manager/integration_tests/file_manager/search.js
@@ -518,7 +518,7 @@
   chrome.test.assertEq(
       'This folder', await getSelectedOptionText(appId, 'location'));
   chrome.test.assertEq(
-      'All time', await getSelectedOptionText(appId, 'recency'));
+      'Any time', await getSelectedOptionText(appId, 'recency'));
   chrome.test.assertEq('All types', await getSelectedOptionText(appId, 'type'));
 
   // Change options.
@@ -538,7 +538,7 @@
   chrome.test.assertEq(
       'This folder', await getSelectedOptionText(appId, 'location'));
   chrome.test.assertEq(
-      'All time', await getSelectedOptionText(appId, 'recency'));
+      'Any time', await getSelectedOptionText(appId, 'recency'));
   chrome.test.assertEq('All types', await getSelectedOptionText(appId, 'type'));
 };
 
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 3fefa94..5e83234 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -271,6 +271,8 @@
       "gl_surface_egl_x11.h",
       "gl_surface_egl_x11_gles2.cc",
       "gl_surface_egl_x11_gles2.h",
+      "native_pixmap_egl_x11_binding_helper.cc",
+      "native_pixmap_egl_x11_binding_helper.h",
     ]
 
     deps += [
diff --git a/ui/gl/gl_image_egl_pixmap.cc b/ui/gl/gl_image_egl_pixmap.cc
index 38acaa1..0b18b36 100644
--- a/ui/gl/gl_image_egl_pixmap.cc
+++ b/ui/gl/gl_image_egl_pixmap.cc
@@ -4,90 +4,28 @@
 
 #include "ui/gl/gl_image_egl_pixmap.h"
 
-#include <memory>
-
-#include "ui/gfx/x/connection.h"
-#include "ui/gl/buffer_format_utils.h"
-#include "ui/gl/gl_bindings.h"
-
 namespace gl {
 
-inline EGLDisplay FromXDisplay() {
-  auto* x_display = x11::Connection::Get()->GetXlibDisplay().display();
-  return eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(x_display));
-}
-
 GLImageEGLPixmap::GLImageEGLPixmap(const gfx::Size& size,
                                    gfx::BufferFormat format)
-    : surface_(nullptr),
-      size_(size),
-      format_(format),
-      display_(FromXDisplay()) {}
+    : binding_helper_(size, format) {}
 
-GLImageEGLPixmap::~GLImageEGLPixmap() {
-  if (surface_)
-    eglDestroySurface(display_, surface_);
-}
+GLImageEGLPixmap::~GLImageEGLPixmap() = default;
 
 bool GLImageEGLPixmap::Initialize(x11::Pixmap pixmap) {
-  if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE)
-    return false;
-
-  EGLint attribs[] = {EGL_BUFFER_SIZE,
-                      32,
-                      EGL_ALPHA_SIZE,
-                      8,
-                      EGL_BLUE_SIZE,
-                      8,
-                      EGL_GREEN_SIZE,
-                      8,
-                      EGL_RED_SIZE,
-                      8,
-                      EGL_SURFACE_TYPE,
-                      EGL_PIXMAP_BIT,
-                      EGL_BIND_TO_TEXTURE_RGBA,
-                      EGL_TRUE,
-                      EGL_NONE};
-
-  EGLint num_configs;
-  EGLConfig config = nullptr;
-
-  if ((eglChooseConfig(display_, attribs, &config, 1, &num_configs) !=
-       EGL_TRUE) ||
-      !num_configs) {
-    return false;
-  }
-
-  std::vector<EGLint> attrs = {EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
-                               EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE};
-
-  surface_ = eglCreatePixmapSurface(
-      display_, config, static_cast<EGLNativePixmapType>(pixmap), attrs.data());
-  return surface_ != EGL_NO_SURFACE;
+  return binding_helper_.Initialize(pixmap);
 }
 
 gfx::Size GLImageEGLPixmap::GetSize() {
-  return size_;
+  return binding_helper_.GetSize();
 }
 
 bool GLImageEGLPixmap::BindTexImage(unsigned target) {
-  if (!surface_)
-    return false;
-
-  // Requires TEXTURE_2D target.
-  if (target != GL_TEXTURE_2D)
-    return false;
-
-  if (eglBindTexImage(display_, surface_, EGL_BACK_BUFFER) != EGL_TRUE)
-    return false;
-
-  return true;
+  return binding_helper_.BindTexImage(target);
 }
 
 void GLImageEGLPixmap::ReleaseEGLImage() {
-  DCHECK_NE(nullptr, surface_);
-
-  eglReleaseTexImage(display_, surface_, EGL_BACK_BUFFER);
+  return binding_helper_.ReleaseEGLImage();
 }
 
 }  // namespace gl
diff --git a/ui/gl/gl_image_egl_pixmap.h b/ui/gl/gl_image_egl_pixmap.h
index 58fa1fbc..888221b 100644
--- a/ui/gl/gl_image_egl_pixmap.h
+++ b/ui/gl/gl_image_egl_pixmap.h
@@ -5,16 +5,12 @@
 #ifndef UI_GL_GL_IMAGE_EGL_PIXMAP_H_
 #define UI_GL_GL_IMAGE_EGL_PIXMAP_H_
 
-#include <stdint.h>
-
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/x/glx.h"
 #include "ui/gl/gl_export.h"
 #include "ui/gl/gl_image.h"
-
-typedef void* EGLSurface;
-typedef void* EGLDisplay;
+#include "ui/gl/native_pixmap_egl_x11_binding_helper.h"
 
 namespace media {
 class VaapiPictureNativePixmapAngle;
@@ -52,12 +48,7 @@
 
   ~GLImageEGLPixmap() override;
 
-  gfx::BufferFormat format() const { return format_; }
-
-  EGLSurface surface_;
-  const gfx::Size size_;
-  gfx::BufferFormat format_;
-  EGLDisplay display_;
+  NativePixmapEGLX11BindingHelper binding_helper_;
 };
 
 }  // namespace gl
diff --git a/ui/gl/native_pixmap_egl_x11_binding_helper.cc b/ui/gl/native_pixmap_egl_x11_binding_helper.cc
new file mode 100644
index 0000000..e3a02122
--- /dev/null
+++ b/ui/gl/native_pixmap_egl_x11_binding_helper.cc
@@ -0,0 +1,96 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/native_pixmap_egl_x11_binding_helper.h"
+
+#include <memory>
+
+#include "ui/gfx/x/connection.h"
+#include "ui/gl/buffer_format_utils.h"
+#include "ui/gl/gl_bindings.h"
+
+namespace gl {
+
+inline EGLDisplay FromXDisplay() {
+  auto* x_display = x11::Connection::Get()->GetXlibDisplay().display();
+  return eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(x_display));
+}
+
+NativePixmapEGLX11BindingHelper::NativePixmapEGLX11BindingHelper(
+    const gfx::Size& size,
+    gfx::BufferFormat format)
+    : size_(size), format_(format), display_(FromXDisplay()) {}
+
+NativePixmapEGLX11BindingHelper::~NativePixmapEGLX11BindingHelper() {
+  if (surface_) {
+    eglDestroySurface(display_, surface_);
+  }
+}
+
+bool NativePixmapEGLX11BindingHelper::Initialize(x11::Pixmap pixmap) {
+  if (eglInitialize(display_, nullptr, nullptr) != EGL_TRUE) {
+    return false;
+  }
+
+  EGLint attribs[] = {EGL_BUFFER_SIZE,
+                      32,
+                      EGL_ALPHA_SIZE,
+                      8,
+                      EGL_BLUE_SIZE,
+                      8,
+                      EGL_GREEN_SIZE,
+                      8,
+                      EGL_RED_SIZE,
+                      8,
+                      EGL_SURFACE_TYPE,
+                      EGL_PIXMAP_BIT,
+                      EGL_BIND_TO_TEXTURE_RGBA,
+                      EGL_TRUE,
+                      EGL_NONE};
+
+  EGLint num_configs;
+  EGLConfig config = nullptr;
+
+  if ((eglChooseConfig(display_, attribs, &config, 1, &num_configs) !=
+       EGL_TRUE) ||
+      !num_configs) {
+    return false;
+  }
+
+  std::vector<EGLint> attrs = {EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+                               EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE};
+
+  surface_ = eglCreatePixmapSurface(
+      display_, config, static_cast<EGLNativePixmapType>(pixmap), attrs.data());
+  return surface_ != EGL_NO_SURFACE;
+}
+
+gfx::Size NativePixmapEGLX11BindingHelper::GetSize() {
+  return size_;
+}
+
+bool NativePixmapEGLX11BindingHelper::BindTexImage(unsigned target) {
+  if (!surface_) {
+    return false;
+  }
+
+  // Requires TEXTURE_2D target.
+  if (target != GL_TEXTURE_2D) {
+    return false;
+  }
+
+  if (eglBindTexImage(display_, surface_, EGL_BACK_BUFFER) != EGL_TRUE) {
+    return false;
+  }
+
+  return true;
+}
+
+void NativePixmapEGLX11BindingHelper::ReleaseEGLImage() {
+  DCHECK_NE(nullptr, surface_);
+
+  eglReleaseTexImage(display_, surface_, EGL_BACK_BUFFER);
+}
+
+}  // namespace gl
diff --git a/ui/gl/native_pixmap_egl_x11_binding_helper.h b/ui/gl/native_pixmap_egl_x11_binding_helper.h
new file mode 100644
index 0000000..28855c4b
--- /dev/null
+++ b/ui/gl/native_pixmap_egl_x11_binding_helper.h
@@ -0,0 +1,54 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_NATIVE_PIXMAP_EGL_X11_BINDING_HELPER_H_
+#define UI_GL_NATIVE_PIXMAP_EGL_X11_BINDING_HELPER_H_
+
+#include <stdint.h>
+
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/x/glx.h"
+#include "ui/gl/gl_export.h"
+
+typedef void* EGLSurface;
+typedef void* EGLDisplay;
+
+namespace gl {
+
+class GL_EXPORT NativePixmapEGLX11BindingHelper {
+ public:
+  NativePixmapEGLX11BindingHelper(const gfx::Size& size,
+                                  gfx::BufferFormat format);
+
+  NativePixmapEGLX11BindingHelper(const NativePixmapEGLX11BindingHelper&) =
+      delete;
+  NativePixmapEGLX11BindingHelper& operator=(
+      const NativePixmapEGLX11BindingHelper&) = delete;
+
+  bool Initialize(x11::Pixmap pixmap);
+
+  gfx::Size GetSize();
+
+  // Binds image to texture currently bound to |target|. Returns true on
+  // success.
+  bool BindTexImage(unsigned target);
+
+  // Releases the image that was bound via BindTexImage().
+  void ReleaseEGLImage();
+
+  ~NativePixmapEGLX11BindingHelper();
+
+ private:
+  gfx::BufferFormat format() const { return format_; }
+
+  EGLSurface surface_ = nullptr;
+  const gfx::Size size_;
+  gfx::BufferFormat format_;
+  EGLDisplay display_;
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_NATIVE_PIXMAP_EGL_X11_BINDING_HELPER_H_
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 5353bd7..946028f 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -26,6 +26,7 @@
 #include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/models/image_model.h"
 #include "ui/base/models/menu_model.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
@@ -359,10 +360,20 @@
     const ui::ImageModel& minor_icon,
     const ui::ImageModel& icon,
     Type type,
-    ui::MenuSeparatorType separator_style) {
+    ui::MenuSeparatorType separator_style,
+    absl::optional<ui::ColorId> submenu_background_color,
+    absl::optional<ui::ColorId> foreground_color) {
   DCHECK_NE(type, Type::kEmpty);
-  if (!submenu_)
+  if (!submenu_) {
     CreateSubmenu();
+    // Set the submenu border color to be the same as the first submenu
+    // background color;
+    submenu_->SetBorderColor(submenu_background_color);
+    if (submenu_background_color.has_value()) {
+      submenu_->SetBackground(
+          views::CreateThemedSolidBackground(submenu_background_color.value()));
+    }
+  }
   DCHECK_LE(index, submenu_->children().size());
   if (type == Type::kSeparator) {
     submenu_->AddChildViewAt(std::make_unique<MenuSeparator>(separator_style),
@@ -378,6 +389,7 @@
   item->SetMinorText(minor_text);
   item->SetMinorIcon(minor_icon);
   item->SetIcon(icon);
+  item->SetForegroundColorId(foreground_color);
   if (type == Type::kSubMenu || type == Type::kActionableSubMenu)
     item->CreateSubmenu();
   if (type == Type::kHighlighted) {
@@ -1163,6 +1175,12 @@
 }
 
 SkColor MenuItemView::GetTextColor(bool minor, bool paint_as_selected) const {
+  // Use a custom color if provided by the controller. If the item is selected,
+  // use the default color.
+  if (!paint_as_selected && foreground_color_id_.has_value()) {
+    return GetColorProvider()->GetColor(foreground_color_id_.value());
+  }
+
   style::TextContext context =
       GetMenuController() && GetMenuController()->use_ash_system_ui_layout()
           ? style::CONTEXT_TOUCH_MENU
@@ -1518,6 +1536,18 @@
     radio_check_image_view_->SetImage(ui::ImageModel::FromVectorIcon(
         radio_icon, radio_icon_color, kMenuCheckSize));
   }
+
+  // Update any vector icons if a custom color is used.
+  if (foreground_color_id_.has_value() && icon_view_) {
+    ui::ImageModel icon_model = icon_view_->GetImageModel();
+    if (!icon_model.IsEmpty() && icon_model.IsVectorIcon()) {
+      ui::VectorIconModel model = icon_model.GetVectorIcon();
+      const gfx::VectorIcon* icon = model.vector_icon();
+      const ui::ImageModel& image_model = ui::ImageModel::FromVectorIcon(
+          *icon, colors.fg_color, model.icon_size());
+      icon_view_->SetImage(image_model);
+    }
+  }
 }
 
 bool MenuItemView::ShouldPaintAsSelected(PaintMode mode) const {
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index 4c50932..f8f6d75 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -157,15 +157,18 @@
 
   // Add an item to the menu at a specified index.  ChildrenChanged() should
   // called after adding menu items if the menu may be active.
-  MenuItemView* AddMenuItemAt(size_t index,
-                              int item_id,
-                              const std::u16string& label,
-                              const std::u16string& secondary_label,
-                              const std::u16string& minor_text,
-                              const ui::ImageModel& minor_icon,
-                              const ui::ImageModel& icon,
-                              Type type,
-                              ui::MenuSeparatorType separator_style);
+  MenuItemView* AddMenuItemAt(
+      size_t index,
+      int item_id,
+      const std::u16string& label,
+      const std::u16string& secondary_label,
+      const std::u16string& minor_text,
+      const ui::ImageModel& minor_icon,
+      const ui::ImageModel& icon,
+      Type type,
+      ui::MenuSeparatorType separator_style,
+      absl::optional<ui::ColorId> submenu_background_color = absl::nullopt,
+      absl::optional<ui::ColorId> foreground_color = absl::nullopt);
 
   // Remove the specified item from the menu. |item| will be deleted when
   // ChildrenChanged() is invoked.
@@ -551,6 +554,10 @@
   // could interact with model state.
   bool IsScheduledForDeletion() const;
 
+  void SetForegroundColorId(absl::optional<ui::ColorId> foreground_color_id) {
+    foreground_color_id_ = foreground_color_id;
+  }
+
   // The delegate. This is only valid for the root menu item. You shouldn't
   // use this directly, instead use GetDelegate() which walks the tree as
   // as necessary.
@@ -679,6 +686,8 @@
   const std::u16string new_badge_text_ = l10n_util::GetStringUTF16(
       features::IsChromeRefresh2023() ? IDS_NEW_BADGE_UPPERCASE
                                       : IDS_NEW_BADGE);
+
+  absl::optional<ui::ColorId> foreground_color_id_;
 };
 
 }  // namespace views
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index daf9fff..e826d424 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -111,11 +111,12 @@
 
   ui::ImageModel icon = model->GetIconAt(model_index);
   ui::ImageModel minor_icon = model->GetMinorIconAt(model_index);
-  auto* menu_item_view =
-      menu->AddMenuItemAt(menu_index, item_id, model->GetLabelAt(model_index),
-                          model->GetSecondaryLabelAt(model_index),
-                          model->GetMinorTextAt(model_index), minor_icon, icon,
-                          *type, ui::NORMAL_SEPARATOR);
+  auto* menu_item_view = menu->AddMenuItemAt(
+      menu_index, item_id, model->GetLabelAt(model_index),
+      model->GetSecondaryLabelAt(model_index),
+      model->GetMinorTextAt(model_index), minor_icon, icon, *type,
+      ui::NORMAL_SEPARATOR, model->GetSubmenuBackgroundColor(model_index),
+      model->GetForegroundColor(model_index));
 
   if (model->IsAlertedAt(model_index))
     menu_item_view->SetAlerted();
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc
index 46eb2fa..c034e29 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -421,13 +421,22 @@
             content_view_->GetMenuItem()->GetMenuController())) {
       CreateBubbleBorder();
     } else {
+      gfx::Insets insets = gfx::Insets::TLBR(vertical_inset, horizontal_inset,
+                                             bottom_inset, horizontal_inset);
+      // When a custom background color is used, ensure that the border uses
+      // the custom background color for its insets.
+      if (border_color_id_.has_value()) {
+        SetBorder(views::CreateThemedSolidSidedBorder(
+            insets, border_color_id_.value()));
+        return;
+      }
+
       SkColor color = GetWidget()
                           ? GetColorProvider()->GetColor(ui::kColorMenuBorder)
                           : gfx::kPlaceholderColor;
       SetBorder(views::CreateBorderPainter(
           std::make_unique<views::RoundRectPainter>(color, corner_radius_),
-          gfx::Insets::TLBR(vertical_inset, horizontal_inset, bottom_inset,
-                            horizontal_inset)));
+          insets));
     }
   } else {
     SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(
diff --git a/ui/views/controls/menu/menu_scroll_view_container.h b/ui/views/controls/menu/menu_scroll_view_container.h
index 46478504..afb7eed 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.h
+++ b/ui/views/controls/menu/menu_scroll_view_container.h
@@ -44,6 +44,10 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnThemeChanged() override;
 
+  void SetBorderColorId(absl::optional<ui::ColorId> border_color_id) {
+    border_color_id_ = border_color_id;
+  }
+
  protected:
   // View override.
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
@@ -99,6 +103,8 @@
 
   // Whether the menu uses ash system UI layout.
   const bool use_ash_system_ui_layout_;
+
+  absl::optional<ui::ColorId> border_color_id_;
 };
 
 }  // namespace views
diff --git a/ui/views/controls/menu/submenu_view.cc b/ui/views/controls/menu/submenu_view.cc
index 876c0f0..ac3c251 100644
--- a/ui/views/controls/menu/submenu_view.cc
+++ b/ui/views/controls/menu/submenu_view.cc
@@ -516,6 +516,7 @@
     scroll_view_container_ = new MenuScrollViewContainer(this);
     // Otherwise MenuHost would delete us.
     scroll_view_container_->set_owned_by_client();
+    scroll_view_container_->SetBorderColorId(border_color_id_);
   }
   return scroll_view_container_;
 }
@@ -607,6 +608,14 @@
   return false;
 }
 
+void SubmenuView::SetBorderColor(absl::optional<ui::ColorId> color_id) {
+  if (scroll_view_container_) {
+    scroll_view_container_->SetBorderColorId(color_id);
+  }
+
+  border_color_id_ = color_id;
+}
+
 BEGIN_METADATA(SubmenuView, View)
 END_METADATA
 
diff --git a/ui/views/controls/menu/submenu_view.h b/ui/views/controls/menu/submenu_view.h
index 851e46f..b8ccb91 100644
--- a/ui/views/controls/menu/submenu_view.h
+++ b/ui/views/controls/menu/submenu_view.h
@@ -181,6 +181,8 @@
   }
   MenuHost* host() { return host_; }
 
+  void SetBorderColor(absl::optional<ui::ColorId> color_id);
+
  protected:
   // View method. Overridden to schedule a paint. We do this so that when
   // scrolling occurs, everything is repainted correctly.
@@ -237,6 +239,8 @@
   float roundoff_error_ = 0;
 
   PrefixSelector prefix_selector_;
+
+  absl::optional<ui::ColorId> border_color_id_;
 };
 
 }  // namespace views